@openape/apes 1.15.0 → 1.17.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
|
@@ -2713,6 +2713,35 @@ mkdir -p "$HOME_DIR/Library/Logs" "$HOME_DIR/.openape/agent/tasks"
|
|
|
2713
2713
|
function buildTroopBootstrapBlock(_troop, _name) {
|
|
2714
2714
|
return "";
|
|
2715
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
|
+
}
|
|
2716
2745
|
function buildDestroyTeardownScript(input) {
|
|
2717
2746
|
const { name, homeDir, adminUser } = input;
|
|
2718
2747
|
return `#!/bin/bash
|
|
@@ -3155,27 +3184,42 @@ ${consequences.join("\n")}`);
|
|
|
3155
3184
|
consola19.info("No IdP agent to remove (skipped).");
|
|
3156
3185
|
}
|
|
3157
3186
|
if (osUserExists) {
|
|
3158
|
-
const
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
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}
|
|
3173
3216
|
${adminPassword}
|
|
3174
3217
|
`,
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3218
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
3219
|
+
});
|
|
3220
|
+
} finally {
|
|
3221
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
3222
|
+
}
|
|
3179
3223
|
}
|
|
3180
3224
|
} else if (!args["keep-os-user"] && isDarwin()) {
|
|
3181
3225
|
consola19.info("No macOS user to remove (skipped).");
|
|
@@ -4119,9 +4163,15 @@ and try again.`
|
|
|
4119
4163
|
troop
|
|
4120
4164
|
});
|
|
4121
4165
|
writeFileSync4(scriptPath, script, { mode: 448 });
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4166
|
+
const alreadyRoot = process.getuid?.() === 0;
|
|
4167
|
+
if (alreadyRoot) {
|
|
4168
|
+
consola23.start("Running privileged setup directly (already root)\u2026");
|
|
4169
|
+
execFileSync6("bash", [scriptPath], { stdio: "inherit" });
|
|
4170
|
+
} else {
|
|
4171
|
+
consola23.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
|
|
4172
|
+
consola23.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
|
|
4173
|
+
execFileSync6(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
|
|
4174
|
+
}
|
|
4125
4175
|
try {
|
|
4126
4176
|
const uid = readMacOSUidOrNull(name);
|
|
4127
4177
|
upsertNestAgent({
|
|
@@ -5771,6 +5821,19 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5771
5821
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5772
5822
|
const command = action.split(" ");
|
|
5773
5823
|
const targetHost = args.host || hostname5();
|
|
5824
|
+
const runAs = args.as ?? void 0;
|
|
5825
|
+
const reusableId = await findReusableAudienceGrant({
|
|
5826
|
+
grantsUrl,
|
|
5827
|
+
requester: auth.email,
|
|
5828
|
+
audience,
|
|
5829
|
+
command,
|
|
5830
|
+
targetHost,
|
|
5831
|
+
runAs
|
|
5832
|
+
});
|
|
5833
|
+
if (reusableId) {
|
|
5834
|
+
const { authz_jwt: authz_jwt2 } = await apiFetch(`${grantsUrl}/${reusableId}/token`, { method: "POST" });
|
|
5835
|
+
return executeWithGrantToken({ audience, command, args, token: authz_jwt2 });
|
|
5836
|
+
}
|
|
5774
5837
|
consola36.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5775
5838
|
const grant = await apiFetch(grantsUrl, {
|
|
5776
5839
|
method: "POST",
|
|
@@ -5781,7 +5844,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5781
5844
|
grant_type: args.approval,
|
|
5782
5845
|
command,
|
|
5783
5846
|
reason: args.reason || command.join(" "),
|
|
5784
|
-
...
|
|
5847
|
+
...runAs ? { run_as: runAs } : {}
|
|
5785
5848
|
}
|
|
5786
5849
|
});
|
|
5787
5850
|
if (!shouldWaitForGrant(args)) {
|
|
@@ -5817,11 +5880,15 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5817
5880
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5818
5881
|
method: "POST"
|
|
5819
5882
|
});
|
|
5883
|
+
return executeWithGrantToken({ audience, command, args, token: authz_jwt });
|
|
5884
|
+
}
|
|
5885
|
+
function executeWithGrantToken(opts) {
|
|
5886
|
+
const { audience, command, args, token } = opts;
|
|
5820
5887
|
if (audience === "escapes") {
|
|
5821
5888
|
consola36.info(`Executing: ${command.join(" ")}`);
|
|
5822
5889
|
try {
|
|
5823
5890
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5824
|
-
execFileSync13(args["escapes-path"] || "escapes", ["--grant",
|
|
5891
|
+
execFileSync13(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
|
|
5825
5892
|
stdio: "inherit",
|
|
5826
5893
|
env: inheritedEnv
|
|
5827
5894
|
});
|
|
@@ -5830,7 +5897,28 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5830
5897
|
throw new CliExit(exitCode);
|
|
5831
5898
|
}
|
|
5832
5899
|
} else {
|
|
5833
|
-
process.stdout.write(
|
|
5900
|
+
process.stdout.write(token);
|
|
5901
|
+
}
|
|
5902
|
+
}
|
|
5903
|
+
async function findReusableAudienceGrant(opts) {
|
|
5904
|
+
try {
|
|
5905
|
+
const grants = await apiFetch(`${opts.grantsUrl}?requester=${encodeURIComponent(opts.requester)}&status=approved&limit=50`);
|
|
5906
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
5907
|
+
const match = grants.data.find((g) => {
|
|
5908
|
+
const r3 = g.request;
|
|
5909
|
+
if (r3.audience !== opts.audience) return false;
|
|
5910
|
+
if (r3.target_host !== opts.targetHost) return false;
|
|
5911
|
+
if (r3.grant_type === "once") return false;
|
|
5912
|
+
if (r3.grant_type === "timed" && g.expires_at && g.expires_at <= now) return false;
|
|
5913
|
+
const cmd = r3.command ?? [];
|
|
5914
|
+
if (cmd.length !== opts.command.length) return false;
|
|
5915
|
+
if (!cmd.every((c2, i) => c2 === opts.command[i])) return false;
|
|
5916
|
+
if ((r3.run_as ?? void 0) !== opts.runAs) return false;
|
|
5917
|
+
return true;
|
|
5918
|
+
});
|
|
5919
|
+
return match?.id ?? null;
|
|
5920
|
+
} catch {
|
|
5921
|
+
return null;
|
|
5834
5922
|
}
|
|
5835
5923
|
}
|
|
5836
5924
|
|
|
@@ -6323,7 +6411,7 @@ var mcpCommand = defineCommand48({
|
|
|
6323
6411
|
if (transport !== "stdio" && transport !== "sse") {
|
|
6324
6412
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
6325
6413
|
}
|
|
6326
|
-
const { startMcpServer } = await import("./server-
|
|
6414
|
+
const { startMcpServer } = await import("./server-VPKUJDKY.js");
|
|
6327
6415
|
await startMcpServer(transport, port);
|
|
6328
6416
|
}
|
|
6329
6417
|
});
|
|
@@ -6961,7 +7049,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6961
7049
|
}
|
|
6962
7050
|
}
|
|
6963
7051
|
async function runHealth(args) {
|
|
6964
|
-
const version = true ? "1.
|
|
7052
|
+
const version = true ? "1.17.0" : "0.0.0";
|
|
6965
7053
|
const auth = loadAuth();
|
|
6966
7054
|
if (!auth) {
|
|
6967
7055
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -7234,10 +7322,10 @@ if (shellRewrite) {
|
|
|
7234
7322
|
if (shellRewrite.action === "rewrite") {
|
|
7235
7323
|
process.argv = shellRewrite.argv;
|
|
7236
7324
|
} else if (shellRewrite.action === "version") {
|
|
7237
|
-
console.log(`ape-shell ${"1.
|
|
7325
|
+
console.log(`ape-shell ${"1.17.0"} (OpenApe DDISA shell wrapper)`);
|
|
7238
7326
|
process.exit(0);
|
|
7239
7327
|
} else if (shellRewrite.action === "help") {
|
|
7240
|
-
console.log(`ape-shell ${"1.
|
|
7328
|
+
console.log(`ape-shell ${"1.17.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
7241
7329
|
console.log("");
|
|
7242
7330
|
console.log("Usage:");
|
|
7243
7331
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -7295,7 +7383,7 @@ var configCommand = defineCommand60({
|
|
|
7295
7383
|
var main = defineCommand60({
|
|
7296
7384
|
meta: {
|
|
7297
7385
|
name: "apes",
|
|
7298
|
-
version: "1.
|
|
7386
|
+
version: "1.17.0",
|
|
7299
7387
|
description: "Unified CLI for OpenApe"
|
|
7300
7388
|
},
|
|
7301
7389
|
subCommands: {
|
|
@@ -7352,7 +7440,7 @@ async function maybeRefreshAuth() {
|
|
|
7352
7440
|
}
|
|
7353
7441
|
}
|
|
7354
7442
|
await maybeRefreshAuth();
|
|
7355
|
-
await maybeWarnStaleVersion("1.
|
|
7443
|
+
await maybeWarnStaleVersion("1.17.0").catch(() => {
|
|
7356
7444
|
});
|
|
7357
7445
|
runMain(main).catch((err) => {
|
|
7358
7446
|
if (err instanceof CliExit) {
|