@openape/apes 1.23.0 → 1.24.1

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
@@ -1773,13 +1773,15 @@ var adminCommand = defineCommand19({
1773
1773
  import { defineCommand as defineCommand28 } from "citty";
1774
1774
 
1775
1775
  // src/commands/agents/allow.ts
1776
- import { execFileSync as execFileSync3 } from "child_process";
1776
+ import { execFileSync as execFileSync4 } from "child_process";
1777
1777
  import { defineCommand as defineCommand20 } from "citty";
1778
1778
  import consola18 from "consola";
1779
1779
 
1780
1780
  // src/lib/agent-bootstrap.ts
1781
1781
  import { Buffer as Buffer3 } from "buffer";
1782
+ import { execFileSync as execFileSync2 } from "child_process";
1782
1783
  import { createPrivateKey, sign } from "crypto";
1784
+ import { rmSync } from "fs";
1783
1785
 
1784
1786
  // ../../node_modules/.pnpm/ofetch@1.5.1/node_modules/ofetch/dist/node.mjs
1785
1787
  import http from "http";
@@ -2650,9 +2652,30 @@ NAME=${shQuote(name)}
2650
2652
  HOME_DIR=${shQuote(homeDir)}
2651
2653
  SHELL_PATH=${shQuote(shellPath)}
2652
2654
 
2655
+ # Tombstone-reuse: \`apes agents destroy\` leaves the dscl user
2656
+ # record behind (opendirectoryd refuses \`dscl . -delete\` from
2657
+ # escapes' setuid-root context without admin auth, so we accept the
2658
+ # stranded record as a harmless tombstone \u2014 see
2659
+ # runPhaseGTeardownInProcess). When the operator re-spawns with the
2660
+ # same name, we recycle the tombstone instead of refusing: if the
2661
+ # previously-recorded home dir is gone from disk (matching the
2662
+ # Phase-G teardown's \`rm -rf\`), we reuse the existing UID +
2663
+ # overwrite the home. If the home is still on disk it's a real,
2664
+ # live agent \u2014 refuse.
2665
+ TOMBSTONE_REUSE=0
2653
2666
  if dscl . -read "/Users/$NAME" >/dev/null 2>&1; then
2654
- echo "User $NAME already exists; refusing to overwrite." >&2
2655
- exit 1
2667
+ EXISTING_HOME=$(dscl . -read "/Users/$NAME" NFSHomeDirectory 2>/dev/null | awk '/NFSHomeDirectory:/ {print $2}')
2668
+ if [ -n "$EXISTING_HOME" ] && [ -d "$EXISTING_HOME" ]; then
2669
+ echo "User $NAME already exists with a live home at $EXISTING_HOME; refusing to overwrite." >&2
2670
+ exit 1
2671
+ fi
2672
+ NEXT_UID=$(dscl . -read "/Users/$NAME" UniqueID 2>/dev/null | awk '/UniqueID:/ {print $2}')
2673
+ if [ -z "$NEXT_UID" ]; then
2674
+ echo "User $NAME exists but has no UniqueID; record is malformed, refusing to recycle." >&2
2675
+ exit 1
2676
+ fi
2677
+ echo "Recycling tombstone dscl record for $NAME (uid=$NEXT_UID, prior home $EXISTING_HOME \u2014 gone)"
2678
+ TOMBSTONE_REUSE=1
2656
2679
  fi
2657
2680
 
2658
2681
  # Phase G: agent home dirs live under /var/openape/homes/, not
@@ -2661,24 +2684,30 @@ fi
2661
2684
  mkdir -p /var/openape/homes
2662
2685
  chmod 755 /var/openape/homes
2663
2686
 
2664
- # Pick the next free UID in the [200, 500) hidden service-account range.
2665
- # Starts the running max at 199 so an empty range yields 200 after the
2666
- # floor check; otherwise NEXT_UID = max(existing in-range UIDs) + 1.
2667
- NEXT_UID=199
2668
- for uid in $(dscl . -list /Users UniqueID | awk '$2 >= 200 && $2 < 500 {print $2}'); do
2669
- if [ "$uid" -ge "$NEXT_UID" ]; then
2670
- NEXT_UID=$((uid + 1))
2687
+ if [ "$TOMBSTONE_REUSE" = "0" ]; then
2688
+ # Pick the next free UID in the [200, 500) hidden service-account range.
2689
+ # Starts the running max at 199 so an empty range yields 200 after the
2690
+ # floor check; otherwise NEXT_UID = max(existing in-range UIDs) + 1.
2691
+ NEXT_UID=199
2692
+ for uid in $(dscl . -list /Users UniqueID | awk '$2 >= 200 && $2 < 500 {print $2}'); do
2693
+ if [ "$uid" -ge "$NEXT_UID" ]; then
2694
+ NEXT_UID=$((uid + 1))
2695
+ fi
2696
+ done
2697
+ if [ "$NEXT_UID" -lt 200 ]; then
2698
+ NEXT_UID=200
2671
2699
  fi
2672
- done
2673
- if [ "$NEXT_UID" -lt 200 ]; then
2674
- NEXT_UID=200
2675
- fi
2676
- if [ "$NEXT_UID" -ge 500 ]; then
2677
- echo "No free UID in [200, 500) \u2014 refusing to clobber a real user." >&2
2678
- exit 1
2700
+ if [ "$NEXT_UID" -ge 500 ]; then
2701
+ echo "No free UID in [200, 500) \u2014 refusing to clobber a real user." >&2
2702
+ exit 1
2703
+ fi
2704
+
2705
+ dscl . -create "/Users/$NAME"
2679
2706
  fi
2680
2707
 
2681
- dscl . -create "/Users/$NAME"
2708
+ # Idempotent attribute writes \u2014 \`dscl . -create\` on an existing
2709
+ # property overwrites in place, so the tombstone-reuse path lands
2710
+ # the same end-state as a fresh create.
2682
2711
  dscl . -create "/Users/$NAME" UserShell "$SHELL_PATH"
2683
2712
  dscl . -create "/Users/$NAME" RealName "OpenApe Agent $NAME"
2684
2713
  dscl . -create "/Users/$NAME" UniqueID "$NEXT_UID"
@@ -2725,34 +2754,37 @@ mkdir -p "$HOME_DIR/Library/Logs" "$HOME_DIR/.openape/agent/tasks"
2725
2754
  function buildTroopBootstrapBlock(_troop, _name) {
2726
2755
  return "";
2727
2756
  }
2728
- function buildPhaseGTeardownScript(input) {
2757
+ function runPhaseGTeardownInProcess(input) {
2729
2758
  const { name, homeDir } = input;
2730
- return `#!/bin/bash
2731
- set -u
2732
-
2733
- NAME=${shQuote(name)}
2734
- HOME_DIR=${shQuote(homeDir)}
2735
-
2736
- UID_OF=$(dscl . -read "/Users/$NAME" UniqueID 2>/dev/null | awk '/UniqueID:/ {print $2}')
2737
-
2738
- if [ -n "$UID_OF" ]; then
2739
- launchctl bootout "user/$UID_OF" 2>/dev/null || true
2740
- pkill -9 -u "$UID_OF" 2>/dev/null || true
2741
- fi
2742
-
2743
- # Per-agent ecosystem files written by the Nest's pm2-supervisor.
2744
- rm -rf "/var/openape/agents/$NAME"
2745
-
2746
- # Home dir lives under /var/openape/homes/ \u2014 no FDA wall, root can
2747
- # remove directly.
2748
- if [ -d "$HOME_DIR" ] && [ "$HOME_DIR" != "/" ] && [ "$HOME_DIR" != "" ]; then
2749
- rm -rf "$HOME_DIR"
2750
- fi
2751
-
2752
- # dscl record stays as a tombstone. Operators run
2753
- # \`sudo sysadminctl -deleteUser $NAME\` to fully clean up if desired.
2754
- echo "OK Phase-G teardown done for $NAME (dscl record kept as tombstone)"
2755
- `;
2759
+ let uid = null;
2760
+ try {
2761
+ const out = execFileSync2("/usr/bin/dscl", [".", "-read", `/Users/${name}`, "UniqueID"], { encoding: "utf8" });
2762
+ const m = out.match(/UniqueID:\s*(\d+)/);
2763
+ if (m) uid = m[1];
2764
+ } catch {
2765
+ }
2766
+ if (uid) {
2767
+ try {
2768
+ execFileSync2("/bin/launchctl", ["bootout", `user/${uid}`], { stdio: "ignore" });
2769
+ } catch {
2770
+ }
2771
+ try {
2772
+ execFileSync2("/usr/bin/pkill", ["-9", "-u", uid], { stdio: "ignore" });
2773
+ } catch {
2774
+ }
2775
+ }
2776
+ const agentDir = `/var/openape/agents/${name}`;
2777
+ try {
2778
+ rmSync(agentDir, { recursive: true, force: true });
2779
+ } catch {
2780
+ }
2781
+ if (homeDir && homeDir !== "/" && homeDir.startsWith("/var/openape/homes/")) {
2782
+ try {
2783
+ rmSync(homeDir, { recursive: true, force: true });
2784
+ } catch {
2785
+ }
2786
+ }
2787
+ console.log(`OK Phase-G teardown done for ${name} (dscl record kept as tombstone)`);
2756
2788
  }
2757
2789
  function buildDestroyTeardownScript(input) {
2758
2790
  const { name, homeDir, adminUser } = input;
@@ -2876,7 +2908,7 @@ print(json.dumps(out))
2876
2908
  `;
2877
2909
 
2878
2910
  // src/lib/macos-user.ts
2879
- import { execFileSync as execFileSync2 } from "child_process";
2911
+ import { execFileSync as execFileSync3 } from "child_process";
2880
2912
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2881
2913
  function isDarwin() {
2882
2914
  return process.platform === "darwin";
@@ -2884,7 +2916,7 @@ function isDarwin() {
2884
2916
  function readMacOSUser(name) {
2885
2917
  let output;
2886
2918
  try {
2887
- output = execFileSync2("dscl", [".", "-read", `/Users/${name}`], {
2919
+ output = execFileSync3("dscl", [".", "-read", `/Users/${name}`], {
2888
2920
  encoding: "utf-8",
2889
2921
  stdio: ["ignore", "pipe", "pipe"]
2890
2922
  });
@@ -2904,7 +2936,7 @@ function readMacOSUser(name) {
2904
2936
  function listMacOSUserNames() {
2905
2937
  let output;
2906
2938
  try {
2907
- output = execFileSync2("dscl", [".", "-list", "/Users"], {
2939
+ output = execFileSync3("dscl", [".", "-list", "/Users"], {
2908
2940
  encoding: "utf-8",
2909
2941
  stdio: ["ignore", "pipe", "pipe"]
2910
2942
  });
@@ -2917,7 +2949,7 @@ function listMacOSUserNames() {
2917
2949
  }
2918
2950
  function whichBinary(name) {
2919
2951
  try {
2920
- const out = execFileSync2("which", [name], {
2952
+ const out = execFileSync3("which", [name], {
2921
2953
  encoding: "utf-8",
2922
2954
  stdio: ["ignore", "pipe", "ignore"]
2923
2955
  }).trim();
@@ -2994,7 +3026,7 @@ PY
2994
3026
  chmod 600 "$F"
2995
3027
  `;
2996
3028
  consola18.start(`Adding ${email} to ${agent}'s allowlist\u2026`);
2997
- execFileSync3(apes, ["run", "--as", agent, "--wait", "--", "bash", "-c", script], { stdio: "inherit" });
3029
+ execFileSync4(apes, ["run", "--as", agent, "--wait", "--", "bash", "-c", script], { stdio: "inherit" });
2998
3030
  consola18.success(`${agent} will auto-accept future contact requests from ${email} (within ~30s of next bridge connect).`);
2999
3031
  }
3000
3032
  });
@@ -3003,8 +3035,8 @@ function shQuote2(s) {
3003
3035
  }
3004
3036
 
3005
3037
  // src/commands/agents/destroy.ts
3006
- import { execFileSync as execFileSync4 } from "child_process";
3007
- import { mkdtempSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
3038
+ import { execFileSync as execFileSync5 } from "child_process";
3039
+ import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
3008
3040
  import { tmpdir, userInfo } from "os";
3009
3041
  import { join as join3 } from "path";
3010
3042
  import { defineCommand as defineCommand21 } from "citty";
@@ -3136,6 +3168,10 @@ var destroyAgentCommand = defineCommand21({
3136
3168
  "keep-os-user": {
3137
3169
  type: "boolean",
3138
3170
  description: "Skip OS-side teardown. Useful for CI where the agent has no OS user."
3171
+ },
3172
+ "root-stage": {
3173
+ type: "boolean",
3174
+ description: "Internal \u2014 destroy.ts re-invokes itself via `apes run --as root --` with this flag set, then runs only the Phase-G teardown (rm home, launchctl bootout, kill processes). Skips IdP + auth + interactive prompts since those already ran in the outer pass."
3139
3175
  }
3140
3176
  },
3141
3177
  async run({ args }) {
@@ -3145,6 +3181,15 @@ var destroyAgentCommand = defineCommand21({
3145
3181
  `Invalid agent name "${name}". Must match /^[a-z][a-z0-9-]{0,23}$/.`
3146
3182
  );
3147
3183
  }
3184
+ if (args["root-stage"]) {
3185
+ if (process.geteuid?.() !== 0) {
3186
+ throw new CliError("--root-stage was passed but this process is not running as root. Refusing to continue.");
3187
+ }
3188
+ const homeDir = readMacOSUser(name)?.homeDir ?? `/var/openape/homes/${name}`;
3189
+ consola19.start(`Running teardown for ${name} (Phase-G, root-stage)\u2026`);
3190
+ runPhaseGTeardownInProcess({ name, homeDir });
3191
+ return;
3192
+ }
3148
3193
  const auth = loadAuth();
3149
3194
  if (!auth) {
3150
3195
  throw new CliError("Not authenticated. Run `apes login` first.");
@@ -3199,15 +3244,24 @@ ${consequences.join("\n")}`);
3199
3244
  const homeDir = osUser?.homeDir ?? `/Users/${name}`;
3200
3245
  const isPhaseG = homeDir.startsWith("/var/openape/homes/");
3201
3246
  if (isPhaseG) {
3202
- const scratch = mkdtempSync(join3(tmpdir(), `apes-destroy-${name}-`));
3203
- const scriptPath = join3(scratch, "teardown.sh");
3204
- try {
3205
- const script = buildPhaseGTeardownScript({ name, homeDir });
3206
- writeFileSync2(scriptPath, script, { mode: 448 });
3247
+ if (process.geteuid?.() === 0) {
3248
+ consola19.start("Running teardown (Phase G \u2014 already root, no grant needed)\u2026");
3249
+ runPhaseGTeardownInProcess({ name, homeDir });
3250
+ } else {
3207
3251
  consola19.start("Running teardown (Phase G \u2014 no admin password needed)\u2026");
3208
- execFileSync4("apes", ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
3209
- } finally {
3210
- rmSync(scratch, { recursive: true, force: true });
3252
+ execFileSync5("apes", [
3253
+ "run",
3254
+ "--as",
3255
+ "root",
3256
+ "--wait",
3257
+ "--",
3258
+ "apes",
3259
+ "agents",
3260
+ "destroy",
3261
+ name,
3262
+ "--force",
3263
+ "--root-stage"
3264
+ ], { stdio: "inherit" });
3211
3265
  }
3212
3266
  consola19.info(`dscl record /Users/${name} kept as tombstone (hidden, no home). Run \`sudo sysadminctl -deleteUser ${name}\` to fully remove.`);
3213
3267
  } else {
@@ -3235,14 +3289,14 @@ ${consequences.join("\n")}`);
3235
3289
  const script = buildDestroyTeardownScript({ name, homeDir, adminUser });
3236
3290
  writeFileSync2(scriptPath, script, { mode: 448 });
3237
3291
  consola19.start("Running teardown via sudo\u2026");
3238
- execFileSync4(sudo, ["-S", "--prompt=", "--", "bash", scriptPath], {
3292
+ execFileSync5(sudo, ["-S", "--prompt=", "--", "bash", scriptPath], {
3239
3293
  input: `${adminPassword}
3240
3294
  ${adminPassword}
3241
3295
  `,
3242
3296
  stdio: ["pipe", "inherit", "inherit"]
3243
3297
  });
3244
3298
  } finally {
3245
- rmSync(scratch, { recursive: true, force: true });
3299
+ rmSync2(scratch, { recursive: true, force: true });
3246
3300
  }
3247
3301
  }
3248
3302
  }
@@ -3768,8 +3822,8 @@ async function handleInbound(msg, sessions) {
3768
3822
  }
3769
3823
 
3770
3824
  // src/commands/agents/spawn.ts
3771
- import { execFileSync as execFileSync6 } from "child_process";
3772
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
3825
+ import { execFileSync as execFileSync7 } from "child_process";
3826
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
3773
3827
  import { tmpdir as tmpdir2 } from "os";
3774
3828
  import { join as join7 } from "path";
3775
3829
  import { defineCommand as defineCommand26 } from "citty";
@@ -3884,7 +3938,7 @@ function generateKeyPairInMemory() {
3884
3938
  }
3885
3939
 
3886
3940
  // src/lib/llm-bridge.ts
3887
- import { execFileSync as execFileSync5 } from "child_process";
3941
+ import { execFileSync as execFileSync6 } from "child_process";
3888
3942
  import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
3889
3943
  import { homedir as homedir8 } from "os";
3890
3944
  import { dirname as dirname2, join as join6 } from "path";
@@ -3928,7 +3982,7 @@ function captureHostBinDirs() {
3928
3982
  for (const bin of ["node", "ape-agent", "apes"]) {
3929
3983
  let resolved;
3930
3984
  try {
3931
- resolved = execFileSync5("/usr/bin/which", [bin], { encoding: "utf8" }).trim();
3985
+ resolved = execFileSync6("/usr/bin/which", [bin], { encoding: "utf8" }).trim();
3932
3986
  } catch {
3933
3987
  const installCmd = bin === "ape-agent" ? "npm i -g @openape/ape-agent" : bin === "apes" ? "npm i -g @openape/apes" : "install Node.js (e.g. brew install node)";
3934
3988
  throw new Error(`'${bin}' not found on host PATH. ${installCmd} before spawning agents \u2014 the bridge runtime resolves these at spawn time and bakes the dir into the agent's launchd plist.`);
@@ -4192,11 +4246,11 @@ and try again.`
4192
4246
  const alreadyRoot = process.getuid?.() === 0;
4193
4247
  if (alreadyRoot) {
4194
4248
  consola23.start("Running privileged setup directly (already root)\u2026");
4195
- execFileSync6("bash", [scriptPath], { stdio: "inherit" });
4249
+ execFileSync7("bash", [scriptPath], { stdio: "inherit" });
4196
4250
  } else {
4197
4251
  consola23.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
4198
4252
  consola23.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
4199
- execFileSync6(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
4253
+ execFileSync7(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
4200
4254
  }
4201
4255
  try {
4202
4256
  const uid = readMacOSUidOrNull(name);
@@ -4225,7 +4279,7 @@ and try again.`
4225
4279
  console.log("Run as the agent with:");
4226
4280
  console.log(` apes run --as ${name} -- claude --session-name ${name} --dangerously-skip-permissions`);
4227
4281
  } finally {
4228
- rmSync2(scratch, { recursive: true, force: true });
4282
+ rmSync3(scratch, { recursive: true, force: true });
4229
4283
  }
4230
4284
  }
4231
4285
  });
@@ -4253,18 +4307,18 @@ async function resolveClaudeToken(opts) {
4253
4307
  }
4254
4308
 
4255
4309
  // src/commands/agents/sync.ts
4256
- import { chownSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync9, rmSync as rmSync3, statSync, writeFileSync as writeFileSync5 } from "fs";
4310
+ import { chownSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync9, rmSync as rmSync4, statSync, writeFileSync as writeFileSync5 } from "fs";
4257
4311
  import { homedir as homedir9 } from "os";
4258
4312
  import { join as join8 } from "path";
4259
4313
  import { defineCommand as defineCommand27 } from "citty";
4260
4314
  import consola24 from "consola";
4261
4315
 
4262
4316
  // src/lib/macos-host.ts
4263
- import { execFileSync as execFileSync7 } from "child_process";
4317
+ import { execFileSync as execFileSync8 } from "child_process";
4264
4318
  import { hostname as hostname3 } from "os";
4265
4319
  function getHostId() {
4266
4320
  try {
4267
- const output = execFileSync7(
4321
+ const output = execFileSync8(
4268
4322
  "/usr/sbin/ioreg",
4269
4323
  ["-d2", "-c", "IOPlatformExpertDevice"],
4270
4324
  { encoding: "utf8", timeout: 2e3 }
@@ -4397,7 +4451,7 @@ var syncAgentCommand = defineCommand27({
4397
4451
  for (const entry of readdirSync(skillsDir)) {
4398
4452
  if (incomingNames.has(entry)) continue;
4399
4453
  try {
4400
- rmSync3(join8(skillsDir, entry), { recursive: true, force: true });
4454
+ rmSync4(join8(skillsDir, entry), { recursive: true, force: true });
4401
4455
  } catch {
4402
4456
  }
4403
4457
  }
@@ -4438,7 +4492,7 @@ var agentsCommand = defineCommand28({
4438
4492
  import { defineCommand as defineCommand36 } from "citty";
4439
4493
 
4440
4494
  // src/commands/nest/authorize.ts
4441
- import { execFileSync as execFileSync8 } from "child_process";
4495
+ import { execFileSync as execFileSync9 } from "child_process";
4442
4496
  import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
4443
4497
  import { join as join10 } from "path";
4444
4498
  import { defineCommand as defineCommand30 } from "citty";
@@ -4605,7 +4659,7 @@ var authorizeNestCommand = defineCommand30({
4605
4659
  cmdArgs.push("--expires-in", args["expires-in"]);
4606
4660
  }
4607
4661
  try {
4608
- execFileSync8("apes", cmdArgs, { stdio: "inherit" });
4662
+ execFileSync9("apes", cmdArgs, { stdio: "inherit" });
4609
4663
  } catch (err) {
4610
4664
  throw new CliError(err instanceof Error ? err.message : String(err));
4611
4665
  }
@@ -4615,7 +4669,7 @@ var authorizeNestCommand = defineCommand30({
4615
4669
  });
4616
4670
 
4617
4671
  // src/commands/nest/destroy.ts
4618
- import { execFileSync as execFileSync9 } from "child_process";
4672
+ import { execFileSync as execFileSync10 } from "child_process";
4619
4673
  import { defineCommand as defineCommand31 } from "citty";
4620
4674
  import consola27 from "consola";
4621
4675
  var destroyNestCommand = defineCommand31({
@@ -4629,7 +4683,7 @@ var destroyNestCommand = defineCommand31({
4629
4683
  async run({ args }) {
4630
4684
  const name = String(args.name);
4631
4685
  try {
4632
- execFileSync9("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
4686
+ execFileSync10("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
4633
4687
  consola27.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
4634
4688
  } catch (err) {
4635
4689
  const status = err.status ?? 1;
@@ -4639,7 +4693,7 @@ var destroyNestCommand = defineCommand31({
4639
4693
  });
4640
4694
 
4641
4695
  // src/commands/nest/install.ts
4642
- import { execFileSync as execFileSync10 } from "child_process";
4696
+ import { execFileSync as execFileSync11 } from "child_process";
4643
4697
  import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
4644
4698
  import { homedir as homedir11, userInfo as userInfo2 } from "os";
4645
4699
  import { dirname as dirname3, join as join11 } from "path";
@@ -4838,10 +4892,10 @@ var installNestCommand = defineCommand32({
4838
4892
  }
4839
4893
  const uid = userInfo2().uid;
4840
4894
  try {
4841
- execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4895
+ execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4842
4896
  } catch {
4843
4897
  }
4844
- execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4898
+ execFileSync11("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4845
4899
  consola28.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4846
4900
  consola28.info("");
4847
4901
  consola28.info("Next steps for zero-prompt spawn \u2014 both one-time:");
@@ -4883,7 +4937,7 @@ var listNestCommand = defineCommand33({
4883
4937
  });
4884
4938
 
4885
4939
  // src/commands/nest/spawn.ts
4886
- import { execFileSync as execFileSync11 } from "child_process";
4940
+ import { execFileSync as execFileSync12 } from "child_process";
4887
4941
  import { defineCommand as defineCommand34 } from "citty";
4888
4942
  import consola30 from "consola";
4889
4943
  var spawnNestCommand = defineCommand34({
@@ -4916,7 +4970,7 @@ var spawnNestCommand = defineCommand34({
4916
4970
  if (typeof args["bridge-base-url"] === "string") apesArgs.push("--bridge-base-url", args["bridge-base-url"]);
4917
4971
  if (typeof args["bridge-model"] === "string") apesArgs.push("--bridge-model", args["bridge-model"]);
4918
4972
  try {
4919
- execFileSync11("apes", apesArgs, { stdio: "inherit" });
4973
+ execFileSync12("apes", apesArgs, { stdio: "inherit" });
4920
4974
  consola30.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
4921
4975
  } catch (err) {
4922
4976
  const status = err.status ?? 1;
@@ -4926,7 +4980,7 @@ var spawnNestCommand = defineCommand34({
4926
4980
  });
4927
4981
 
4928
4982
  // src/commands/nest/uninstall.ts
4929
- import { execFileSync as execFileSync12 } from "child_process";
4983
+ import { execFileSync as execFileSync13 } from "child_process";
4930
4984
  import { existsSync as existsSync14, unlinkSync } from "fs";
4931
4985
  import { homedir as homedir12, userInfo as userInfo3 } from "os";
4932
4986
  import { join as join12 } from "path";
@@ -4942,7 +4996,7 @@ var uninstallNestCommand = defineCommand35({
4942
4996
  const uid = userInfo3().uid;
4943
4997
  const path2 = join12(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4944
4998
  try {
4945
- execFileSync12("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4999
+ execFileSync13("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4946
5000
  consola31.success("Nest daemon stopped");
4947
5001
  } catch {
4948
5002
  consola31.info("Nest daemon was not loaded");
@@ -5496,7 +5550,7 @@ var adapterCommand = defineCommand41({
5496
5550
  });
5497
5551
 
5498
5552
  // src/commands/run.ts
5499
- import { execFileSync as execFileSync13 } from "child_process";
5553
+ import { execFileSync as execFileSync14 } from "child_process";
5500
5554
  import { hostname as hostname5 } from "os";
5501
5555
  import { basename } from "path";
5502
5556
  import { defineCommand as defineCommand42 } from "citty";
@@ -5774,7 +5828,7 @@ function execShellCommand(command) {
5774
5828
  throw new CliError("No command to execute");
5775
5829
  try {
5776
5830
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5777
- execFileSync13(command[0], command.slice(1), {
5831
+ execFileSync14(command[0], command.slice(1), {
5778
5832
  stdio: "inherit",
5779
5833
  env: inheritedEnv
5780
5834
  });
@@ -5943,7 +5997,7 @@ function executeWithGrantToken(opts) {
5943
5997
  consola36.info(`Executing: ${command.join(" ")}`);
5944
5998
  try {
5945
5999
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5946
- execFileSync13(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
6000
+ execFileSync14(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
5947
6001
  stdio: "inherit",
5948
6002
  env: inheritedEnv
5949
6003
  });
@@ -6014,7 +6068,7 @@ note = "VPC-internal hostname suffix"
6014
6068
 
6015
6069
  // src/proxy/local-proxy.ts
6016
6070
  import { spawn } from "child_process";
6017
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
6071
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
6018
6072
  import { createRequire } from "module";
6019
6073
  import { tmpdir as tmpdir3 } from "os";
6020
6074
  import { dirname as dirname4, join as join13, resolve as resolve3 } from "path";
@@ -6039,7 +6093,7 @@ async function startEphemeralProxy(configToml) {
6039
6093
  });
6040
6094
  const cleanupTmp = () => {
6041
6095
  try {
6042
- rmSync4(tmpDir, { recursive: true, force: true });
6096
+ rmSync5(tmpDir, { recursive: true, force: true });
6043
6097
  } catch {
6044
6098
  }
6045
6099
  };
@@ -6466,7 +6520,7 @@ var mcpCommand = defineCommand48({
6466
6520
  if (transport !== "stdio" && transport !== "sse") {
6467
6521
  throw new Error('Transport must be "stdio" or "sse"');
6468
6522
  }
6469
- const { startMcpServer } = await import("./server-D3WUVVZ2.js");
6523
+ const { startMcpServer } = await import("./server-KR6GVKRI.js");
6470
6524
  await startMcpServer(transport, port);
6471
6525
  }
6472
6526
  });
@@ -6474,7 +6528,7 @@ var mcpCommand = defineCommand48({
6474
6528
  // src/commands/init/index.ts
6475
6529
  import { existsSync as existsSync15, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
6476
6530
  import { randomBytes } from "crypto";
6477
- import { execFileSync as execFileSync14 } from "child_process";
6531
+ import { execFileSync as execFileSync15 } from "child_process";
6478
6532
  import { join as join14 } from "path";
6479
6533
  import { defineCommand as defineCommand49 } from "citty";
6480
6534
  import consola40 from "consola";
@@ -6486,11 +6540,11 @@ async function downloadTemplate(repo, targetDir) {
6486
6540
  function installDeps(dir) {
6487
6541
  const hasLockFile = (name) => existsSync15(join14(dir, name));
6488
6542
  if (hasLockFile("pnpm-lock.yaml")) {
6489
- execFileSync14("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6543
+ execFileSync15("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6490
6544
  } else if (hasLockFile("bun.lockb")) {
6491
- execFileSync14("bun", ["install"], { cwd: dir, stdio: "inherit" });
6545
+ execFileSync15("bun", ["install"], { cwd: dir, stdio: "inherit" });
6492
6546
  } else {
6493
- execFileSync14("npm", ["install"], { cwd: dir, stdio: "inherit" });
6547
+ execFileSync15("npm", ["install"], { cwd: dir, stdio: "inherit" });
6494
6548
  }
6495
6549
  }
6496
6550
  async function promptChoice(message, choices) {
@@ -7104,7 +7158,7 @@ async function bestEffortGrantCount(idp) {
7104
7158
  }
7105
7159
  }
7106
7160
  async function runHealth(args) {
7107
- const version = true ? "1.23.0" : "0.0.0";
7161
+ const version = true ? "1.24.1" : "0.0.0";
7108
7162
  const auth = loadAuth();
7109
7163
  if (!auth) {
7110
7164
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -7377,10 +7431,10 @@ if (shellRewrite) {
7377
7431
  if (shellRewrite.action === "rewrite") {
7378
7432
  process.argv = shellRewrite.argv;
7379
7433
  } else if (shellRewrite.action === "version") {
7380
- console.log(`ape-shell ${"1.23.0"} (OpenApe DDISA shell wrapper)`);
7434
+ console.log(`ape-shell ${"1.24.1"} (OpenApe DDISA shell wrapper)`);
7381
7435
  process.exit(0);
7382
7436
  } else if (shellRewrite.action === "help") {
7383
- console.log(`ape-shell ${"1.23.0"} \u2014 OpenApe DDISA shell wrapper`);
7437
+ console.log(`ape-shell ${"1.24.1"} \u2014 OpenApe DDISA shell wrapper`);
7384
7438
  console.log("");
7385
7439
  console.log("Usage:");
7386
7440
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -7438,7 +7492,7 @@ var configCommand = defineCommand60({
7438
7492
  var main = defineCommand60({
7439
7493
  meta: {
7440
7494
  name: "apes",
7441
- version: "1.23.0",
7495
+ version: "1.24.1",
7442
7496
  description: "Unified CLI for OpenApe"
7443
7497
  },
7444
7498
  subCommands: {
@@ -7495,7 +7549,7 @@ async function maybeRefreshAuth() {
7495
7549
  }
7496
7550
  }
7497
7551
  await maybeRefreshAuth();
7498
- await maybeWarnStaleVersion("1.23.0").catch(() => {
7552
+ await maybeWarnStaleVersion("1.24.1").catch(() => {
7499
7553
  });
7500
7554
  runMain(main).catch((err) => {
7501
7555
  if (err instanceof CliExit) {