@openape/apes 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -2643,6 +2643,12 @@ if dscl . -read "/Users/$NAME" >/dev/null 2>&1; then
|
|
|
2643
2643
|
exit 1
|
|
2644
2644
|
fi
|
|
2645
2645
|
|
|
2646
|
+
# Phase G: agent home dirs live under /var/openape/homes/, not
|
|
2647
|
+
# /Users/. Pre-create the parent and chmod it world-traversable
|
|
2648
|
+
# so per-agent dirs can be reached by their respective uids.
|
|
2649
|
+
mkdir -p /var/openape/homes
|
|
2650
|
+
chmod 755 /var/openape/homes
|
|
2651
|
+
|
|
2646
2652
|
# Pick the next free UID in the [200, 500) hidden service-account range.
|
|
2647
2653
|
# Starts the running max at 199 so an empty range yields 200 after the
|
|
2648
2654
|
# floor check; otherwise NEXT_UID = max(existing in-range UIDs) + 1.
|
|
@@ -2707,6 +2713,35 @@ mkdir -p "$HOME_DIR/Library/Logs" "$HOME_DIR/.openape/agent/tasks"
|
|
|
2707
2713
|
function buildTroopBootstrapBlock(_troop, _name) {
|
|
2708
2714
|
return "";
|
|
2709
2715
|
}
|
|
2716
|
+
function buildPhaseGTeardownScript(input) {
|
|
2717
|
+
const { name, homeDir } = input;
|
|
2718
|
+
return `#!/bin/bash
|
|
2719
|
+
set -u
|
|
2720
|
+
|
|
2721
|
+
NAME=${shQuote(name)}
|
|
2722
|
+
HOME_DIR=${shQuote(homeDir)}
|
|
2723
|
+
|
|
2724
|
+
UID_OF=$(dscl . -read "/Users/$NAME" UniqueID 2>/dev/null | awk '/UniqueID:/ {print $2}')
|
|
2725
|
+
|
|
2726
|
+
if [ -n "$UID_OF" ]; then
|
|
2727
|
+
launchctl bootout "user/$UID_OF" 2>/dev/null || true
|
|
2728
|
+
pkill -9 -u "$UID_OF" 2>/dev/null || true
|
|
2729
|
+
fi
|
|
2730
|
+
|
|
2731
|
+
# Per-agent ecosystem files written by the Nest's pm2-supervisor.
|
|
2732
|
+
rm -rf "/var/openape/agents/$NAME"
|
|
2733
|
+
|
|
2734
|
+
# Home dir lives under /var/openape/homes/ \u2014 no FDA wall, root can
|
|
2735
|
+
# remove directly.
|
|
2736
|
+
if [ -d "$HOME_DIR" ] && [ "$HOME_DIR" != "/" ] && [ "$HOME_DIR" != "" ]; then
|
|
2737
|
+
rm -rf "$HOME_DIR"
|
|
2738
|
+
fi
|
|
2739
|
+
|
|
2740
|
+
# dscl record stays as a tombstone. Operators run
|
|
2741
|
+
# \`sudo sysadminctl -deleteUser $NAME\` to fully clean up if desired.
|
|
2742
|
+
echo "OK Phase-G teardown done for $NAME (dscl record kept as tombstone)"
|
|
2743
|
+
`;
|
|
2744
|
+
}
|
|
2710
2745
|
function buildDestroyTeardownScript(input) {
|
|
2711
2746
|
const { name, homeDir, adminUser } = input;
|
|
2712
2747
|
return `#!/bin/bash
|
|
@@ -2846,10 +2881,12 @@ function readMacOSUser(name) {
|
|
|
2846
2881
|
}
|
|
2847
2882
|
const uidMatch = output.match(/UniqueID:\s*(\d+)/);
|
|
2848
2883
|
const shellMatch = output.match(/UserShell:\s*(\S.*)$/m);
|
|
2884
|
+
const homeMatch = output.match(/NFSHomeDirectory:\s*(\S.*)$/m);
|
|
2849
2885
|
return {
|
|
2850
2886
|
name,
|
|
2851
2887
|
uid: uidMatch ? Number.parseInt(uidMatch[1], 10) : null,
|
|
2852
|
-
shell: shellMatch ? shellMatch[1].trim() : null
|
|
2888
|
+
shell: shellMatch ? shellMatch[1].trim() : null,
|
|
2889
|
+
homeDir: homeMatch ? homeMatch[1].trim() : null
|
|
2853
2890
|
};
|
|
2854
2891
|
}
|
|
2855
2892
|
function listMacOSUserNames() {
|
|
@@ -3107,14 +3144,18 @@ var destroyAgentCommand = defineCommand21({
|
|
|
3107
3144
|
const owned = await apiFetch("/api/my-agents", { idp });
|
|
3108
3145
|
const idpAgent = owned.find((u) => u.name === name);
|
|
3109
3146
|
const idpExists = idpAgent !== void 0;
|
|
3110
|
-
const
|
|
3147
|
+
const osUser = isDarwin() ? readMacOSUser(name) : null;
|
|
3148
|
+
const osUserExists = !args["keep-os-user"] && osUser !== null;
|
|
3111
3149
|
if (!idpExists && !osUserExists) {
|
|
3112
3150
|
consola19.info(`Nothing to destroy: no IdP agent and no OS user named "${name}".`);
|
|
3113
3151
|
return;
|
|
3114
3152
|
}
|
|
3115
3153
|
if (!args.force) {
|
|
3116
3154
|
const consequences = [];
|
|
3117
|
-
if (osUserExists)
|
|
3155
|
+
if (osUserExists) {
|
|
3156
|
+
const home = osUser?.homeDir ?? `/Users/${name}`;
|
|
3157
|
+
consequences.push(`\u2022 Remove macOS user ${name} and rm -rf ${home}`);
|
|
3158
|
+
}
|
|
3118
3159
|
if (idpExists) {
|
|
3119
3160
|
consequences.push(args.soft ? `\u2022 Deactivate IdP agent ${idpAgent.email} (PATCH isActive=false)` : `\u2022 Hard-delete IdP agent ${idpAgent.email} and all its SSH keys`);
|
|
3120
3161
|
}
|
|
@@ -3143,26 +3184,42 @@ ${consequences.join("\n")}`);
|
|
|
3143
3184
|
consola19.info("No IdP agent to remove (skipped).");
|
|
3144
3185
|
}
|
|
3145
3186
|
if (osUserExists) {
|
|
3146
|
-
const
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3187
|
+
const homeDir = osUser?.homeDir ?? `/Users/${name}`;
|
|
3188
|
+
const isPhaseG = homeDir.startsWith("/var/openape/homes/");
|
|
3189
|
+
if (isPhaseG) {
|
|
3190
|
+
const scratch = mkdtempSync(join3(tmpdir(), `apes-destroy-${name}-`));
|
|
3191
|
+
const scriptPath = join3(scratch, "teardown.sh");
|
|
3192
|
+
try {
|
|
3193
|
+
const script = buildPhaseGTeardownScript({ name, homeDir });
|
|
3194
|
+
writeFileSync2(scriptPath, script, { mode: 448 });
|
|
3195
|
+
consola19.start("Running teardown (Phase G \u2014 no admin password needed)\u2026");
|
|
3196
|
+
execFileSync4("apes", ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
|
|
3197
|
+
} finally {
|
|
3198
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
3199
|
+
}
|
|
3200
|
+
consola19.info(`dscl record /Users/${name} kept as tombstone (hidden, no home). Run \`sudo sysadminctl -deleteUser ${name}\` to fully remove.`);
|
|
3201
|
+
} else {
|
|
3202
|
+
const sudo = whichBinary("sudo");
|
|
3203
|
+
if (!sudo) {
|
|
3204
|
+
throw new CliError("`sudo` not found on PATH; required for OS teardown.");
|
|
3205
|
+
}
|
|
3206
|
+
const adminUser = userInfo().username;
|
|
3207
|
+
const adminPassword = await collectAdminPassword({ adminUser });
|
|
3208
|
+
const scratch = mkdtempSync(join3(tmpdir(), `apes-destroy-${name}-`));
|
|
3209
|
+
const scriptPath = join3(scratch, "teardown.sh");
|
|
3210
|
+
try {
|
|
3211
|
+
const script = buildDestroyTeardownScript({ name, homeDir, adminUser });
|
|
3212
|
+
writeFileSync2(scriptPath, script, { mode: 448 });
|
|
3213
|
+
consola19.start("Running teardown via sudo\u2026");
|
|
3214
|
+
execFileSync4(sudo, ["-S", "--prompt=", "--", "bash", scriptPath], {
|
|
3215
|
+
input: `${adminPassword}
|
|
3160
3216
|
${adminPassword}
|
|
3161
3217
|
`,
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3218
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
3219
|
+
});
|
|
3220
|
+
} finally {
|
|
3221
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
3222
|
+
}
|
|
3166
3223
|
}
|
|
3167
3224
|
} else if (!args["keep-os-user"] && isDarwin()) {
|
|
3168
3225
|
consola19.info("No macOS user to remove (skipped).");
|
|
@@ -3215,13 +3272,17 @@ var listAgentsCommand = defineCommand22({
|
|
|
3215
3272
|
const all = await apiFetch("/api/my-agents", { idp });
|
|
3216
3273
|
const filtered = args["include-inactive"] ? all : all.filter((u) => u.isActive !== false);
|
|
3217
3274
|
const osUsers = isDarwin() ? listMacOSUserNames() : /* @__PURE__ */ new Set();
|
|
3218
|
-
const
|
|
3275
|
+
const homeOf = (name) => {
|
|
3276
|
+
if (!osUsers.has(name)) return null;
|
|
3277
|
+
const u = readMacOSUser(name);
|
|
3278
|
+
return u?.homeDir ?? `/Users/${name}`;
|
|
3279
|
+
};
|
|
3219
3280
|
const rows = filtered.map((u) => ({
|
|
3220
3281
|
name: u.name,
|
|
3221
3282
|
email: u.email,
|
|
3222
3283
|
isActive: u.isActive !== false,
|
|
3223
3284
|
osUser: osUsers.has(u.name),
|
|
3224
|
-
home:
|
|
3285
|
+
home: homeOf(u.name)
|
|
3225
3286
|
}));
|
|
3226
3287
|
if (args.json) {
|
|
3227
3288
|
process.stdout.write(`${JSON.stringify(rows, null, 2)}
|
|
@@ -4029,7 +4090,7 @@ and try again.`
|
|
|
4029
4090
|
if (existing) {
|
|
4030
4091
|
throw new CliError(`macOS user "${name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
|
|
4031
4092
|
}
|
|
4032
|
-
const homeDir = `/
|
|
4093
|
+
const homeDir = `/var/openape/homes/${name}`;
|
|
4033
4094
|
const scratch = mkdtempSync2(join7(tmpdir2(), `apes-spawn-${name}-`));
|
|
4034
4095
|
const scriptPath = join7(scratch, "setup.sh");
|
|
4035
4096
|
try {
|
|
@@ -6306,7 +6367,7 @@ var mcpCommand = defineCommand48({
|
|
|
6306
6367
|
if (transport !== "stdio" && transport !== "sse") {
|
|
6307
6368
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
6308
6369
|
}
|
|
6309
|
-
const { startMcpServer } = await import("./server-
|
|
6370
|
+
const { startMcpServer } = await import("./server-FVFFPVVN.js");
|
|
6310
6371
|
await startMcpServer(transport, port);
|
|
6311
6372
|
}
|
|
6312
6373
|
});
|
|
@@ -6944,7 +7005,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6944
7005
|
}
|
|
6945
7006
|
}
|
|
6946
7007
|
async function runHealth(args) {
|
|
6947
|
-
const version = true ? "1.
|
|
7008
|
+
const version = true ? "1.16.0" : "0.0.0";
|
|
6948
7009
|
const auth = loadAuth();
|
|
6949
7010
|
if (!auth) {
|
|
6950
7011
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -7217,10 +7278,10 @@ if (shellRewrite) {
|
|
|
7217
7278
|
if (shellRewrite.action === "rewrite") {
|
|
7218
7279
|
process.argv = shellRewrite.argv;
|
|
7219
7280
|
} else if (shellRewrite.action === "version") {
|
|
7220
|
-
console.log(`ape-shell ${"1.
|
|
7281
|
+
console.log(`ape-shell ${"1.16.0"} (OpenApe DDISA shell wrapper)`);
|
|
7221
7282
|
process.exit(0);
|
|
7222
7283
|
} else if (shellRewrite.action === "help") {
|
|
7223
|
-
console.log(`ape-shell ${"1.
|
|
7284
|
+
console.log(`ape-shell ${"1.16.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
7224
7285
|
console.log("");
|
|
7225
7286
|
console.log("Usage:");
|
|
7226
7287
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -7278,7 +7339,7 @@ var configCommand = defineCommand60({
|
|
|
7278
7339
|
var main = defineCommand60({
|
|
7279
7340
|
meta: {
|
|
7280
7341
|
name: "apes",
|
|
7281
|
-
version: "1.
|
|
7342
|
+
version: "1.16.0",
|
|
7282
7343
|
description: "Unified CLI for OpenApe"
|
|
7283
7344
|
},
|
|
7284
7345
|
subCommands: {
|
|
@@ -7335,7 +7396,7 @@ async function maybeRefreshAuth() {
|
|
|
7335
7396
|
}
|
|
7336
7397
|
}
|
|
7337
7398
|
await maybeRefreshAuth();
|
|
7338
|
-
await maybeWarnStaleVersion("1.
|
|
7399
|
+
await maybeWarnStaleVersion("1.16.0").catch(() => {
|
|
7339
7400
|
});
|
|
7340
7401
|
runMain(main).catch((err) => {
|
|
7341
7402
|
if (err instanceof CliExit) {
|