feat: rename pass_mgr → secret-mgr, add ego-mgr binary and skill
M1: Rename pass_mgr to secret-mgr - Rename directory, binary, and Go module - Update install.mjs to build/install secret-mgr - Update pcexec.ts to support secret-mgr patterns (with legacy pass_mgr compat) - Update plugin config schema (passMgrPath → secretMgrPath) - Create new skills/secret-mgr/SKILL.md - install.mjs now initializes ego.json on install M2: Implement ego-mgr binary (Go) - Agent Scope and Public Scope column management - Commands: add column/public-column, delete, set, get, show, list columns - pcexec environment validation (AGENT_VERIFY, AGENT_ID, AGENT_WORKSPACE) - File locking for concurrent write safety - Proper exit codes per spec (0-6) - Agent auto-registration on read/write - Global column name uniqueness enforcement M3: ego-mgr Skill - Create skills/ego-mgr/SKILL.md with usage guide and examples Ref: REQUIREMENTS_EGO_MGR.md
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean", "default": true },
|
||||
"passMgrPath": { "type": "string", "default": "" },
|
||||
"secretMgrPath": { "type": "string", "default": "" },
|
||||
"openclawProfilePath": { "type": "string", "default": "" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,33 +46,55 @@ export interface PcExecError extends Error {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract pass_mgr invocations from a command string.
|
||||
* Extract secret-mgr (and legacy pass_mgr) invocations from a command string.
|
||||
*
|
||||
* Supports both legacy and new formats:
|
||||
* Legacy: $(pass_mgr get <key>) / `pass_mgr get <key>`
|
||||
* New: $(pass_mgr get-secret --key <key>) / `pass_mgr get-secret --key <key>`
|
||||
* Supports:
|
||||
* Current: $(secret-mgr get-secret --key <key>) / `secret-mgr get-secret --key <key>`
|
||||
* Legacy: $(pass_mgr get-secret --key <key>) / `pass_mgr get-secret --key <key>`
|
||||
* Legacy: $(pass_mgr get <key>) / `pass_mgr get <key>`
|
||||
*
|
||||
* Returns array of { fullMatch, subcommand, key } where subcommand is
|
||||
* Returns array of { fullMatch, subcommand, key, binary } where subcommand is
|
||||
* "get" | "get-secret".
|
||||
*/
|
||||
function extractPassMgrGets(
|
||||
function extractSecretMgrGets(
|
||||
command: string,
|
||||
): Array<{ key: string; fullMatch: string; subcommand: string }> {
|
||||
const results: Array<{ key: string; fullMatch: string; subcommand: string }> = [];
|
||||
): Array<{ key: string; fullMatch: string; subcommand: string; binary: string }> {
|
||||
const results: Array<{ key: string; fullMatch: string; subcommand: string; binary: string }> = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
// New format: pass_mgr get-secret --key <key>
|
||||
// secret-mgr get-secret --key <key>
|
||||
const secretMgrPatterns = [
|
||||
/\$\(\s*secret-mgr\s+get-secret\s+--key\s+(\S+)\s*\)/g,
|
||||
/`\s*secret-mgr\s+get-secret\s+--key\s+(\S+)\s*`/g,
|
||||
];
|
||||
|
||||
// Legacy pass_mgr get-secret --key <key>
|
||||
const newPatterns = [
|
||||
/\$\(\s*pass_mgr\s+get-secret\s+--key\s+(\S+)\s*\)/g,
|
||||
/`\s*pass_mgr\s+get-secret\s+--key\s+(\S+)\s*`/g,
|
||||
];
|
||||
|
||||
// Legacy format: pass_mgr get <key>
|
||||
// Legacy pass_mgr get <key>
|
||||
const legacyPatterns = [
|
||||
/\$\(\s*pass_mgr\s+get\s+(\S+)\s*\)/g,
|
||||
/`\s*pass_mgr\s+get\s+(\S+)\s*`/g,
|
||||
];
|
||||
|
||||
for (const pattern of secretMgrPatterns) {
|
||||
let match;
|
||||
while ((match = pattern.exec(command)) !== null) {
|
||||
if (!seen.has(match[0])) {
|
||||
seen.add(match[0]);
|
||||
results.push({
|
||||
key: match[1],
|
||||
fullMatch: match[0],
|
||||
subcommand: 'get-secret',
|
||||
binary: 'secret-mgr',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const pattern of newPatterns) {
|
||||
let match;
|
||||
while ((match = pattern.exec(command)) !== null) {
|
||||
@@ -82,6 +104,7 @@ function extractPassMgrGets(
|
||||
key: match[1],
|
||||
fullMatch: match[0],
|
||||
subcommand: 'get-secret',
|
||||
binary: 'pass_mgr',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -96,6 +119,7 @@ function extractPassMgrGets(
|
||||
key: match[1],
|
||||
fullMatch: match[0],
|
||||
subcommand: 'get',
|
||||
binary: 'pass_mgr',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -105,22 +129,24 @@ function extractPassMgrGets(
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute pass_mgr to retrieve a secret.
|
||||
* Execute secret-mgr (or legacy pass_mgr) to retrieve a secret.
|
||||
* Uses the same env vars that the caller passes so pcguard checks pass.
|
||||
*/
|
||||
async function fetchPassword(
|
||||
subcommand: string,
|
||||
key: string,
|
||||
env: Record<string, string>,
|
||||
binary: string = 'secret-mgr',
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const passMgrPath = env.PASS_MGR_PATH || process.env.PASS_MGR_PATH || 'pass_mgr';
|
||||
// Prefer SECRET_MGR_PATH, fall back to PASS_MGR_PATH for legacy compat
|
||||
const binaryPath = env.SECRET_MGR_PATH || env.PASS_MGR_PATH || process.env.SECRET_MGR_PATH || process.env.PASS_MGR_PATH || 'secret-mgr';
|
||||
const args =
|
||||
subcommand === 'get-secret'
|
||||
? ['get-secret', '--key', key]
|
||||
: ['get', key];
|
||||
|
||||
const child = spawn(passMgrPath, args, {
|
||||
const child = spawn(binaryPath, args, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
env: { ...process.env, ...env },
|
||||
});
|
||||
@@ -131,7 +157,7 @@ async function fetchPassword(
|
||||
child.stderr.on('data', (d) => (stderr += d.toString()));
|
||||
child.on('close', (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`pass_mgr ${subcommand} failed: ${stderr || stdout}`));
|
||||
reject(new Error(`secret-mgr ${subcommand} failed: ${stderr || stdout}`));
|
||||
} else {
|
||||
resolve(stdout.trim());
|
||||
}
|
||||
@@ -155,18 +181,18 @@ function sanitizeOutput(output: string, passwords: string[]): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-resolve pass_mgr invocations, replace them inline, and collect passwords.
|
||||
* Pre-resolve secret-mgr (and legacy pass_mgr) invocations, replace them inline, and collect passwords.
|
||||
*/
|
||||
async function replacePassMgrGets(
|
||||
async function replaceSecretMgrGets(
|
||||
command: string,
|
||||
env: Record<string, string>,
|
||||
): Promise<{ command: string; passwords: string[] }> {
|
||||
const matches = extractPassMgrGets(command);
|
||||
const matches = extractSecretMgrGets(command);
|
||||
const passwords: string[] = [];
|
||||
let replaced = command;
|
||||
|
||||
for (const { key, fullMatch, subcommand } of matches) {
|
||||
const pw = await fetchPassword(subcommand, key, env);
|
||||
for (const { key, fullMatch, subcommand, binary } of matches) {
|
||||
const pw = await fetchPassword(subcommand, key, env, binary);
|
||||
passwords.push(pw);
|
||||
replaced = replaced.split(fullMatch).join(pw);
|
||||
}
|
||||
@@ -193,7 +219,7 @@ export async function pcexec(
|
||||
let finalCommand = command;
|
||||
let passwords: string[] = [];
|
||||
|
||||
const resolved = await replacePassMgrGets(command, env);
|
||||
const resolved = await replaceSecretMgrGets(command, env);
|
||||
finalCommand = resolved.command;
|
||||
passwords = resolved.passwords;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user