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:
zhi
2026-03-24 09:36:03 +00:00
parent be0f194f47
commit 98fc3da39c
13 changed files with 821 additions and 132 deletions

View File

@@ -131,13 +131,20 @@ async function build() {
rmSync(SRC_DIST_DIR, { recursive: true, force: true });
log(' Building pass_mgr...', 'blue');
const pmDir = join(__dirname, 'pass_mgr');
log(' Building secret-mgr...', 'blue');
const pmDir = join(__dirname, 'secret-mgr');
exec('go mod tidy', { cwd: pmDir, silent: !options.verbose });
const ldflags = `-X main.buildSecret=${buildSecret}`;
exec(`go build -ldflags "${ldflags}" -o dist/pass_mgr src/main.go`, { cwd: pmDir, silent: !options.verbose });
chmodSync(join(pmDir, 'dist', 'pass_mgr'), 0o755);
logOk('pass_mgr');
exec(`go build -ldflags "${ldflags}" -o dist/secret-mgr src/main.go`, { cwd: pmDir, silent: !options.verbose });
chmodSync(join(pmDir, 'dist', 'secret-mgr'), 0o755);
logOk('secret-mgr');
log(' Building ego-mgr...', 'blue');
const emDir = join(__dirname, 'ego-mgr');
exec('go mod tidy', { cwd: emDir, silent: !options.verbose });
exec('go build -o dist/ego-mgr src/main.go', { cwd: emDir, silent: !options.verbose });
chmodSync(join(emDir, 'dist', 'ego-mgr'), 0o755);
logOk('ego-mgr');
log(' Building pcguard...', 'blue');
const pgDir = join(__dirname, 'pcguard');
@@ -161,7 +168,11 @@ async function build() {
}
function handoffSecretIfPossible(openclawPath) {
const passMgrPath = join(openclawPath, 'bin', 'pass_mgr');
// Check both old (pass_mgr) and new (secret-mgr) binary names
let passMgrPath = join(openclawPath, 'bin', 'secret-mgr');
if (!existsSync(passMgrPath)) {
passMgrPath = join(openclawPath, 'bin', 'pass_mgr');
}
if (!existsSync(passMgrPath)) return null;
const storeA = join(openclawPath, 'pc-pass-store');
@@ -181,7 +192,7 @@ function handoffSecretIfPossible(openclawPath) {
function clearInstallTargets(openclawPath) {
const binDir = join(openclawPath, 'bin');
for (const name of ['pass_mgr', 'pcguard']) {
for (const name of ['pass_mgr', 'secret-mgr', 'ego-mgr', 'pcguard']) {
const p = join(binDir, name);
if (existsSync(p)) { rmSync(p, { force: true }); logOk(`Removed ${p}`); }
}
@@ -213,7 +224,7 @@ function cleanupConfig(openclawPath) {
logOk('Removed from load paths');
}
const skillEntries = ['pcexec', 'safe-restart', 'safe_restart', 'pass-mgr'];
const skillEntries = ['pcexec', 'safe-restart', 'safe_restart', 'pass-mgr', 'secret-mgr', 'ego-mgr'];
for (const sk of skillEntries) {
const p = join(skillsDir, sk);
if (existsSync(p)) {
@@ -240,7 +251,7 @@ async function install() {
log(` OpenClaw path: ${openclawPath}`, 'blue');
// update/reinstall path: remove old install first
if (existsSync(destDir) || existsSync(join(binDir, 'pass_mgr')) || existsSync(join(binDir, 'pcguard'))) {
if (existsSync(destDir) || existsSync(join(binDir, 'pass_mgr')) || existsSync(join(binDir, 'secret-mgr')) || existsSync(join(binDir, 'pcguard'))) {
logWarn('Existing install detected, uninstalling before install...');
handoffSecretIfPossible(openclawPath);
clearInstallTargets(openclawPath);
@@ -259,7 +270,8 @@ async function install() {
mkdirSync(binDir, { recursive: true });
const bins = [
{ name: 'pass_mgr', src: join(__dirname, 'pass_mgr', 'dist', 'pass_mgr') },
{ name: 'secret-mgr', src: join(__dirname, 'secret-mgr', 'dist', 'secret-mgr') },
{ name: 'ego-mgr', src: join(__dirname, 'ego-mgr', 'dist', 'ego-mgr') },
{ name: 'pcguard', src: join(__dirname, 'pcguard', 'dist', 'pcguard') },
];
for (const b of bins) {
@@ -281,11 +293,26 @@ async function install() {
}
}
// Initialize ego.json if it doesn't exist
const egoJsonPath = join(openclawPath, 'ego.json');
if (!existsSync(egoJsonPath)) {
const emptyEgo = {
columns: [],
'public-columns': [],
'public-scope': {},
'agent-scope': {},
};
writeFileSync(egoJsonPath, JSON.stringify(emptyEgo, null, 2) + '\n', { mode: 0o644 });
logOk('Created ego.json');
} else {
logOk('ego.json already exists');
}
// if prior encrypted store exists, run init-from once new binary is installed
const hasStore = existsSync(join(openclawPath, 'pc-pass-store')) || existsSync(join(openclawPath, 'pc-secret-store'));
const secretFile = join(openclawPath, 'pc-pass-store.secret');
if (hasStore && existsSync(secretFile)) {
const passMgrPath = join(binDir, 'pass_mgr');
const passMgrPath = join(binDir, 'secret-mgr');
try {
exec(`${passMgrPath} admin init-from ${secretFile}`, { silent: !options.verbose });
logOk('init-from completed from handoff secret');
@@ -303,7 +330,7 @@ async function configure() {
const openclawPath = resolveOpenclawPath();
const destDir = join(openclawPath, 'plugins', PLUGIN_NAME);
const passMgrPath = join(openclawPath, 'bin', 'pass_mgr');
const secretMgrPath = join(openclawPath, 'bin', 'secret-mgr');
try {
const paths = getOpenclawConfig('plugins.load.paths', []);
@@ -318,7 +345,7 @@ async function configure() {
plugins.entries = plugins.entries || {};
plugins.entries[PLUGIN_NAME] = {
enabled: true,
config: { enabled: true, passMgrPath, openclawProfilePath: openclawPath },
config: { enabled: true, secretMgrPath, openclawProfilePath: openclawPath },
};
setOpenclawConfig('plugins', plugins);
logOk('Plugin entry configured');