@openape/apes 1.13.1 → 1.15.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.
@@ -2846,10 +2852,12 @@ function readMacOSUser(name) {
2846
2852
  }
2847
2853
  const uidMatch = output.match(/UniqueID:\s*(\d+)/);
2848
2854
  const shellMatch = output.match(/UserShell:\s*(\S.*)$/m);
2855
+ const homeMatch = output.match(/NFSHomeDirectory:\s*(\S.*)$/m);
2849
2856
  return {
2850
2857
  name,
2851
2858
  uid: uidMatch ? Number.parseInt(uidMatch[1], 10) : null,
2852
- shell: shellMatch ? shellMatch[1].trim() : null
2859
+ shell: shellMatch ? shellMatch[1].trim() : null,
2860
+ homeDir: homeMatch ? homeMatch[1].trim() : null
2853
2861
  };
2854
2862
  }
2855
2863
  function listMacOSUserNames() {
@@ -2955,12 +2963,61 @@ function shQuote2(s) {
2955
2963
 
2956
2964
  // src/commands/agents/destroy.ts
2957
2965
  import { execFileSync as execFileSync4 } from "child_process";
2958
- import { mkdtempSync, rmSync, writeFileSync } from "fs";
2966
+ import { mkdtempSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
2959
2967
  import { tmpdir, userInfo } from "os";
2960
- import { join as join2 } from "path";
2968
+ import { join as join3 } from "path";
2961
2969
  import { defineCommand as defineCommand21 } from "citty";
2962
2970
  import consola19 from "consola";
2963
2971
 
2972
+ // src/lib/nest-registry.ts
2973
+ import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
2974
+ import { homedir as homedir4 } from "os";
2975
+ import { join as join2 } from "path";
2976
+ function resolveRegistryPath() {
2977
+ if (existsSync4("/var/openape/nest/agents.json")) return "/var/openape/nest/agents.json";
2978
+ if (existsSync4("/var/openape/nest")) return "/var/openape/nest/agents.json";
2979
+ return join2(homedir4(), ".openape", "nest", "agents.json");
2980
+ }
2981
+ function emptyRegistry() {
2982
+ return { version: 1, agents: [] };
2983
+ }
2984
+ function readNestRegistry() {
2985
+ const path2 = resolveRegistryPath();
2986
+ if (!existsSync4(path2)) return emptyRegistry();
2987
+ try {
2988
+ const parsed = JSON.parse(readFileSync3(path2, "utf8"));
2989
+ if (parsed?.version !== 1 || !Array.isArray(parsed.agents)) return emptyRegistry();
2990
+ return parsed;
2991
+ } catch {
2992
+ return emptyRegistry();
2993
+ }
2994
+ }
2995
+ function writeNestRegistry(reg) {
2996
+ const path2 = resolveRegistryPath();
2997
+ const dir = path2.replace(/\/agents\.json$/, "");
2998
+ try {
2999
+ mkdirSync(dir, { recursive: true });
3000
+ } catch {
3001
+ }
3002
+ writeFileSync(path2, `${JSON.stringify(reg, null, 2)}
3003
+ `, { mode: 432 });
3004
+ }
3005
+ function upsertNestAgent(entry) {
3006
+ const reg = readNestRegistry();
3007
+ const existing = reg.agents.findIndex((a) => a.name === entry.name);
3008
+ if (existing >= 0) reg.agents[existing] = entry;
3009
+ else reg.agents.push(entry);
3010
+ writeNestRegistry(reg);
3011
+ }
3012
+ function removeNestAgent(name) {
3013
+ const reg = readNestRegistry();
3014
+ const before = reg.agents.length;
3015
+ reg.agents = reg.agents.filter((a) => a.name !== name);
3016
+ if (reg.agents.length === before) return false;
3017
+ writeNestRegistry(reg);
3018
+ return true;
3019
+ }
3020
+
2964
3021
  // src/lib/silent-password.ts
2965
3022
  function readPasswordSilent(prompt) {
2966
3023
  if (!process.stdin.isTTY) {
@@ -3058,14 +3115,18 @@ var destroyAgentCommand = defineCommand21({
3058
3115
  const owned = await apiFetch("/api/my-agents", { idp });
3059
3116
  const idpAgent = owned.find((u) => u.name === name);
3060
3117
  const idpExists = idpAgent !== void 0;
3061
- const osUserExists = !args["keep-os-user"] && isDarwin() && readMacOSUser(name) !== null;
3118
+ const osUser = isDarwin() ? readMacOSUser(name) : null;
3119
+ const osUserExists = !args["keep-os-user"] && osUser !== null;
3062
3120
  if (!idpExists && !osUserExists) {
3063
3121
  consola19.info(`Nothing to destroy: no IdP agent and no OS user named "${name}".`);
3064
3122
  return;
3065
3123
  }
3066
3124
  if (!args.force) {
3067
3125
  const consequences = [];
3068
- if (osUserExists) consequences.push(`\u2022 Remove macOS user ${name} and rm -rf /Users/${name}`);
3126
+ if (osUserExists) {
3127
+ const home = osUser?.homeDir ?? `/Users/${name}`;
3128
+ consequences.push(`\u2022 Remove macOS user ${name} and rm -rf ${home}`);
3129
+ }
3069
3130
  if (idpExists) {
3070
3131
  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`);
3071
3132
  }
@@ -3100,11 +3161,12 @@ ${consequences.join("\n")}`);
3100
3161
  }
3101
3162
  const adminUser = userInfo().username;
3102
3163
  const adminPassword = await collectAdminPassword({ adminUser });
3103
- const scratch = mkdtempSync(join2(tmpdir(), `apes-destroy-${name}-`));
3104
- const scriptPath = join2(scratch, "teardown.sh");
3164
+ const scratch = mkdtempSync(join3(tmpdir(), `apes-destroy-${name}-`));
3165
+ const scriptPath = join3(scratch, "teardown.sh");
3105
3166
  try {
3106
- const script = buildDestroyTeardownScript({ name, homeDir: `/Users/${name}`, adminUser });
3107
- writeFileSync(scriptPath, script, { mode: 448 });
3167
+ const homeDir = osUser?.homeDir ?? `/Users/${name}`;
3168
+ const script = buildDestroyTeardownScript({ name, homeDir, adminUser });
3169
+ writeFileSync2(scriptPath, script, { mode: 448 });
3108
3170
  consola19.start("Running teardown via sudo\u2026");
3109
3171
  execFileSync4(sudo, ["-S", "--prompt=", "--", "bash", scriptPath], {
3110
3172
  input: `${adminPassword}
@@ -3118,6 +3180,11 @@ ${adminPassword}
3118
3180
  } else if (!args["keep-os-user"] && isDarwin()) {
3119
3181
  consola19.info("No macOS user to remove (skipped).");
3120
3182
  }
3183
+ try {
3184
+ removeNestAgent(name);
3185
+ } catch (err) {
3186
+ consola19.warn(`Could not update nest registry: ${err instanceof Error ? err.message : String(err)}`);
3187
+ }
3121
3188
  consola19.success(`Destroyed ${name}.`);
3122
3189
  }
3123
3190
  });
@@ -3161,13 +3228,17 @@ var listAgentsCommand = defineCommand22({
3161
3228
  const all = await apiFetch("/api/my-agents", { idp });
3162
3229
  const filtered = args["include-inactive"] ? all : all.filter((u) => u.isActive !== false);
3163
3230
  const osUsers = isDarwin() ? listMacOSUserNames() : /* @__PURE__ */ new Set();
3164
- const home = (name) => `/Users/${name}`;
3231
+ const homeOf = (name) => {
3232
+ if (!osUsers.has(name)) return null;
3233
+ const u = readMacOSUser(name);
3234
+ return u?.homeDir ?? `/Users/${name}`;
3235
+ };
3165
3236
  const rows = filtered.map((u) => ({
3166
3237
  name: u.name,
3167
3238
  email: u.email,
3168
3239
  isActive: u.isActive !== false,
3169
3240
  osUser: osUsers.has(u.name),
3170
- home: osUsers.has(u.name) ? home(u.name) : null
3241
+ home: homeOf(u.name)
3171
3242
  }));
3172
3243
  if (args.json) {
3173
3244
  process.stdout.write(`${JSON.stringify(rows, null, 2)}
@@ -3193,7 +3264,7 @@ var listAgentsCommand = defineCommand22({
3193
3264
  });
3194
3265
 
3195
3266
  // src/commands/agents/register.ts
3196
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
3267
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
3197
3268
  import { defineCommand as defineCommand23 } from "citty";
3198
3269
  import consola21 from "consola";
3199
3270
  var registerAgentCommand = defineCommand23({
@@ -3241,10 +3312,10 @@ var registerAgentCommand = defineCommand23({
3241
3312
  throw new CliError("Pass either --public-key or --public-key-file, not both.");
3242
3313
  }
3243
3314
  if (!publicKey && keyFile) {
3244
- if (!existsSync4(keyFile)) {
3315
+ if (!existsSync5(keyFile)) {
3245
3316
  throw new CliError(`Public-key file not found: ${keyFile}`);
3246
3317
  }
3247
- publicKey = readFileSync3(keyFile, "utf-8").trim();
3318
+ publicKey = readFileSync4(keyFile, "utf-8").trim();
3248
3319
  }
3249
3320
  if (!publicKey) {
3250
3321
  throw new CliError('Provide --public-key "<ssh-ed25519 line>" or --public-key-file <path>.');
@@ -3279,9 +3350,9 @@ var registerAgentCommand = defineCommand23({
3279
3350
  });
3280
3351
 
3281
3352
  // src/commands/agents/run.ts
3282
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
3283
- import { homedir as homedir4 } from "os";
3284
- import { join as join3 } from "path";
3353
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
3354
+ import { homedir as homedir5 } from "os";
3355
+ import { join as join4 } from "path";
3285
3356
  import { defineCommand as defineCommand24 } from "citty";
3286
3357
  import consola22 from "consola";
3287
3358
 
@@ -3343,13 +3414,13 @@ function resolveTroopUrl(override) {
3343
3414
  }
3344
3415
 
3345
3416
  // src/commands/agents/run.ts
3346
- var AUTH_PATH = join3(homedir4(), ".config", "apes", "auth.json");
3347
- var TASK_CACHE_DIR = join3(homedir4(), ".openape", "agent", "tasks");
3417
+ var AUTH_PATH = join4(homedir5(), ".config", "apes", "auth.json");
3418
+ var TASK_CACHE_DIR = join4(homedir5(), ".openape", "agent", "tasks");
3348
3419
  function readAuth() {
3349
- if (!existsSync5(AUTH_PATH)) {
3420
+ if (!existsSync6(AUTH_PATH)) {
3350
3421
  throw new CliError(`No agent auth found at ${AUTH_PATH}. Run \`apes agents spawn <name>\` first.`);
3351
3422
  }
3352
- const parsed = JSON.parse(readFileSync4(AUTH_PATH, "utf8"));
3423
+ const parsed = JSON.parse(readFileSync5(AUTH_PATH, "utf8"));
3353
3424
  if (!parsed.access_token) throw new CliError("auth.json missing access_token");
3354
3425
  return parsed;
3355
3426
  }
@@ -3385,26 +3456,26 @@ ${msg}`.slice(0, 9e3);
3385
3456
  }
3386
3457
  }
3387
3458
  function readTaskSpec(taskId) {
3388
- const path2 = join3(TASK_CACHE_DIR, `${taskId}.json`);
3389
- if (!existsSync5(path2)) {
3459
+ const path2 = join4(TASK_CACHE_DIR, `${taskId}.json`);
3460
+ if (!existsSync6(path2)) {
3390
3461
  throw new CliError(`No cached task spec at ${path2}. Run \`apes agents sync\` first to pull the task list from troop.`);
3391
3462
  }
3392
- return JSON.parse(readFileSync4(path2, "utf8"));
3463
+ return JSON.parse(readFileSync5(path2, "utf8"));
3393
3464
  }
3394
- var AGENT_CONFIG_PATH = join3(homedir4(), ".openape", "agent", "agent.json");
3465
+ var AGENT_CONFIG_PATH = join4(homedir5(), ".openape", "agent", "agent.json");
3395
3466
  function readAgentConfig() {
3396
- if (!existsSync5(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
3467
+ if (!existsSync6(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
3397
3468
  try {
3398
- return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf8"));
3469
+ return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf8"));
3399
3470
  } catch {
3400
3471
  return { systemPrompt: "" };
3401
3472
  }
3402
3473
  }
3403
3474
  function readLitellmConfig(model) {
3404
- const envPath = join3(homedir4(), "litellm", ".env");
3475
+ const envPath = join4(homedir5(), "litellm", ".env");
3405
3476
  const env = {};
3406
- if (existsSync5(envPath)) {
3407
- for (const line of readFileSync4(envPath, "utf8").split(/\r?\n/)) {
3477
+ if (existsSync6(envPath)) {
3478
+ for (const line of readFileSync5(envPath, "utf8").split(/\r?\n/)) {
3408
3479
  const m = line.match(/^([A-Z_]+)=(.*)$/);
3409
3480
  if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
3410
3481
  }
@@ -3509,17 +3580,17 @@ var runAgentCommand = defineCommand24({
3509
3580
  });
3510
3581
 
3511
3582
  // src/commands/agents/serve.ts
3512
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
3513
- import { homedir as homedir5 } from "os";
3514
- import { join as join4 } from "path";
3583
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
3584
+ import { homedir as homedir6 } from "os";
3585
+ import { join as join5 } from "path";
3515
3586
  import { createInterface } from "readline";
3516
3587
  import { defineCommand as defineCommand25 } from "citty";
3517
- var AUTH_PATH2 = join4(homedir5(), ".config", "apes", "auth.json");
3588
+ var AUTH_PATH2 = join5(homedir6(), ".config", "apes", "auth.json");
3518
3589
  function readLitellmConfig2(model) {
3519
- const envPath = join4(homedir5(), "litellm", ".env");
3590
+ const envPath = join5(homedir6(), "litellm", ".env");
3520
3591
  const env = {};
3521
- if (existsSync6(envPath)) {
3522
- for (const line of readFileSync5(envPath, "utf8").split(/\r?\n/)) {
3592
+ if (existsSync7(envPath)) {
3593
+ for (const line of readFileSync6(envPath, "utf8").split(/\r?\n/)) {
3523
3594
  const m = line.match(/^([A-Z_]+)=(.*)$/);
3524
3595
  if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
3525
3596
  }
@@ -3551,9 +3622,9 @@ var serveAgentCommand = defineCommand25({
3551
3622
  if (!args.rpc) {
3552
3623
  throw new CliError("apes agents serve currently only supports --rpc mode");
3553
3624
  }
3554
- if (existsSync6(AUTH_PATH2)) {
3625
+ if (existsSync7(AUTH_PATH2)) {
3555
3626
  try {
3556
- JSON.parse(readFileSync5(AUTH_PATH2, "utf8"));
3627
+ JSON.parse(readFileSync6(AUTH_PATH2, "utf8"));
3557
3628
  } catch {
3558
3629
  }
3559
3630
  }
@@ -3629,9 +3700,9 @@ async function handleInbound(msg, sessions) {
3629
3700
 
3630
3701
  // src/commands/agents/spawn.ts
3631
3702
  import { execFileSync as execFileSync6 } from "child_process";
3632
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
3703
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
3633
3704
  import { tmpdir as tmpdir2 } from "os";
3634
- import { join as join6 } from "path";
3705
+ import { join as join7 } from "path";
3635
3706
  import { defineCommand as defineCommand26 } from "citty";
3636
3707
  import consola23 from "consola";
3637
3708
 
@@ -3689,12 +3760,12 @@ ${envBlock} <key>StartInterval</key>
3689
3760
 
3690
3761
  // src/lib/keygen.ts
3691
3762
  import { Buffer as Buffer4 } from "buffer";
3692
- import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
3763
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
3693
3764
  import { generateKeyPairSync } from "crypto";
3694
- import { homedir as homedir6 } from "os";
3765
+ import { homedir as homedir7 } from "os";
3695
3766
  import { dirname, resolve as resolve2 } from "path";
3696
3767
  function resolveKeyPath(p2) {
3697
- return resolve2(p2.replace(/^~/, homedir6()));
3768
+ return resolve2(p2.replace(/^~/, homedir7()));
3698
3769
  }
3699
3770
  function buildSshEd25519Line(rawPub) {
3700
3771
  const keyTypeStr = "ssh-ed25519";
@@ -3707,10 +3778,10 @@ function buildSshEd25519Line(rawPub) {
3707
3778
  }
3708
3779
  function readPublicKey(keyPath) {
3709
3780
  const pubPath = `${keyPath}.pub`;
3710
- if (existsSync7(pubPath)) {
3711
- return readFileSync6(pubPath, "utf-8").trim();
3781
+ if (existsSync8(pubPath)) {
3782
+ return readFileSync7(pubPath, "utf-8").trim();
3712
3783
  }
3713
- const keyContent = readFileSync6(keyPath, "utf-8");
3784
+ const keyContent = readFileSync7(keyPath, "utf-8");
3714
3785
  const privateKey = loadEd25519PrivateKey(keyContent);
3715
3786
  const jwk = privateKey.export({ format: "jwk" });
3716
3787
  const pubBytes = Buffer4.from(jwk.x, "base64url");
@@ -3719,16 +3790,16 @@ function readPublicKey(keyPath) {
3719
3790
  function generateAndSaveKey(keyPath) {
3720
3791
  const resolved = resolveKeyPath(keyPath);
3721
3792
  const dir = dirname(resolved);
3722
- if (!existsSync7(dir)) {
3723
- mkdirSync(dir, { recursive: true });
3793
+ if (!existsSync8(dir)) {
3794
+ mkdirSync2(dir, { recursive: true });
3724
3795
  }
3725
3796
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
3726
3797
  const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
3727
- writeFileSync2(resolved, privatePem, { mode: 384 });
3798
+ writeFileSync3(resolved, privatePem, { mode: 384 });
3728
3799
  const jwk = publicKey.export({ format: "jwk" });
3729
3800
  const pubBytes = Buffer4.from(jwk.x, "base64url");
3730
3801
  const pubKeyStr = buildSshEd25519Line(pubBytes);
3731
- writeFileSync2(`${resolved}.pub`, `${pubKeyStr}
3802
+ writeFileSync3(`${resolved}.pub`, `${pubKeyStr}
3732
3803
  `, { mode: 420 });
3733
3804
  return pubKeyStr;
3734
3805
  }
@@ -3745,14 +3816,14 @@ function generateKeyPairInMemory() {
3745
3816
 
3746
3817
  // src/lib/llm-bridge.ts
3747
3818
  import { execFileSync as execFileSync5 } from "child_process";
3748
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
3749
- import { homedir as homedir7 } from "os";
3750
- import { dirname as dirname2, join as join5 } from "path";
3819
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
3820
+ import { homedir as homedir8 } from "os";
3821
+ import { dirname as dirname2, join as join6 } from "path";
3751
3822
  var PLIST_LABEL_PREFIX = "eco.hofmann.apes.bridge";
3752
- function readLitellmEnv(envPath = join5(homedir7(), "litellm", ".env")) {
3753
- if (!existsSync8(envPath)) return null;
3823
+ function readLitellmEnv(envPath = join6(homedir8(), "litellm", ".env")) {
3824
+ if (!existsSync9(envPath)) return null;
3754
3825
  try {
3755
- const text = readFileSync7(envPath, "utf8");
3826
+ const text = readFileSync8(envPath, "utf8");
3756
3827
  const out = {};
3757
3828
  for (const line of text.split("\n")) {
3758
3829
  const trimmed = line.trim();
@@ -3881,6 +3952,14 @@ function buildBridgePlist(agentName, homeDir, ownerEmail, hostBinDirs) {
3881
3952
  }
3882
3953
 
3883
3954
  // src/commands/agents/spawn.ts
3955
+ function readMacOSUidOrNull(name) {
3956
+ try {
3957
+ const u = readMacOSUser(name);
3958
+ return u?.uid ?? null;
3959
+ } catch {
3960
+ return null;
3961
+ }
3962
+ }
3884
3963
  var spawnAgentCommand = defineCommand26({
3885
3964
  meta: {
3886
3965
  name: "spawn",
@@ -3967,9 +4046,9 @@ and try again.`
3967
4046
  if (existing) {
3968
4047
  throw new CliError(`macOS user "${name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
3969
4048
  }
3970
- const homeDir = `/Users/${name}`;
3971
- const scratch = mkdtempSync2(join6(tmpdir2(), `apes-spawn-${name}-`));
3972
- const scriptPath = join6(scratch, "setup.sh");
4049
+ const homeDir = `/var/openape/homes/${name}`;
4050
+ const scratch = mkdtempSync2(join7(tmpdir2(), `apes-spawn-${name}-`));
4051
+ const scriptPath = join7(scratch, "setup.sh");
3973
4052
  try {
3974
4053
  consola23.start(`Generating keypair for ${name}\u2026`);
3975
4054
  const { privatePem, publicSshLine } = generateKeyPairInMemory();
@@ -4039,10 +4118,27 @@ and try again.`
4039
4118
  bridge,
4040
4119
  troop
4041
4120
  });
4042
- writeFileSync3(scriptPath, script, { mode: 448 });
4121
+ writeFileSync4(scriptPath, script, { mode: 448 });
4043
4122
  consola23.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
4044
4123
  consola23.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
4045
4124
  execFileSync6(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
4125
+ try {
4126
+ const uid = readMacOSUidOrNull(name);
4127
+ upsertNestAgent({
4128
+ name,
4129
+ uid: uid ?? -1,
4130
+ home: homeDir,
4131
+ email: registration.email,
4132
+ registeredAt: Math.floor(Date.now() / 1e3),
4133
+ bridge: args.bridge ? {
4134
+ baseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0,
4135
+ apiKey: typeof args["bridge-key"] === "string" ? args["bridge-key"] : void 0,
4136
+ model: typeof args["bridge-model"] === "string" ? args["bridge-model"] : void 0
4137
+ } : void 0
4138
+ });
4139
+ } catch (err) {
4140
+ consola23.warn(`Could not write to nest registry: ${err instanceof Error ? err.message : String(err)}`);
4141
+ }
4046
4142
  consola23.success(`Agent ${name} spawned.`);
4047
4143
  consola23.info(`\u{1F517} Troop: https://troop.openape.ai/agents/${name}`);
4048
4144
  if (args.bridge) {
@@ -4081,9 +4177,9 @@ async function resolveClaudeToken(opts) {
4081
4177
  }
4082
4178
 
4083
4179
  // src/commands/agents/sync.ts
4084
- import { chownSync, existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, statSync, writeFileSync as writeFileSync4 } from "fs";
4085
- import { homedir as homedir8 } from "os";
4086
- import { join as join7 } from "path";
4180
+ import { chownSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync5 } from "fs";
4181
+ import { homedir as homedir9 } from "os";
4182
+ import { join as join8 } from "path";
4087
4183
  import { defineCommand as defineCommand27 } from "citty";
4088
4184
  import consola24 from "consola";
4089
4185
 
@@ -4112,15 +4208,15 @@ function getHostname() {
4112
4208
  }
4113
4209
 
4114
4210
  // src/commands/agents/sync.ts
4115
- var AUTH_PATH3 = join7(homedir8(), ".config", "apes", "auth.json");
4116
- var TASK_CACHE_DIR2 = join7(homedir8(), ".openape", "agent", "tasks");
4211
+ var AUTH_PATH3 = join8(homedir9(), ".config", "apes", "auth.json");
4212
+ var TASK_CACHE_DIR2 = join8(homedir9(), ".openape", "agent", "tasks");
4117
4213
  function readAuthJson() {
4118
- if (!existsSync9(AUTH_PATH3)) {
4214
+ if (!existsSync10(AUTH_PATH3)) {
4119
4215
  throw new CliError(
4120
4216
  `No agent auth found at ${AUTH_PATH3}. Run \`apes agents spawn <name>\` to provision an agent first.`
4121
4217
  );
4122
4218
  }
4123
- const raw = readFileSync8(AUTH_PATH3, "utf8");
4219
+ const raw = readFileSync9(AUTH_PATH3, "utf8");
4124
4220
  let parsed;
4125
4221
  try {
4126
4222
  parsed = JSON.parse(raw);
@@ -4181,7 +4277,7 @@ var syncAgentCommand = defineCommand27({
4181
4277
  let agentGid = null;
4182
4278
  if (process.geteuid?.() === 0) {
4183
4279
  try {
4184
- const homeStat = statSync(homedir8());
4280
+ const homeStat = statSync(homedir9());
4185
4281
  agentUid = homeStat.uid;
4186
4282
  agentGid = homeStat.gid;
4187
4283
  } catch {
@@ -4195,23 +4291,23 @@ var syncAgentCommand = defineCommand27({
4195
4291
  }
4196
4292
  }
4197
4293
  }
4198
- const agentDir = join7(homedir8(), ".openape", "agent");
4199
- mkdirSync2(agentDir, { recursive: true });
4200
- chownToAgent(join7(homedir8(), ".openape"));
4294
+ const agentDir = join8(homedir9(), ".openape", "agent");
4295
+ mkdirSync3(agentDir, { recursive: true });
4296
+ chownToAgent(join8(homedir9(), ".openape"));
4201
4297
  chownToAgent(agentDir);
4202
- const agentJsonPath = join7(agentDir, "agent.json");
4203
- writeFileSync4(
4298
+ const agentJsonPath = join8(agentDir, "agent.json");
4299
+ writeFileSync5(
4204
4300
  agentJsonPath,
4205
4301
  `${JSON.stringify({ systemPrompt }, null, 2)}
4206
4302
  `,
4207
4303
  { mode: 384 }
4208
4304
  );
4209
4305
  chownToAgent(agentJsonPath);
4210
- mkdirSync2(TASK_CACHE_DIR2, { recursive: true });
4306
+ mkdirSync3(TASK_CACHE_DIR2, { recursive: true });
4211
4307
  chownToAgent(TASK_CACHE_DIR2);
4212
4308
  for (const task of tasks) {
4213
- const path2 = join7(TASK_CACHE_DIR2, `${task.taskId}.json`);
4214
- writeFileSync4(path2, `${JSON.stringify(task, null, 2)}
4309
+ const path2 = join8(TASK_CACHE_DIR2, `${task.taskId}.json`);
4310
+ writeFileSync5(path2, `${JSON.stringify(task, null, 2)}
4215
4311
  `, { mode: 384 });
4216
4312
  chownToAgent(path2);
4217
4313
  }
@@ -4242,18 +4338,18 @@ import { defineCommand as defineCommand36 } from "citty";
4242
4338
 
4243
4339
  // src/commands/nest/authorize.ts
4244
4340
  import { execFileSync as execFileSync8 } from "child_process";
4245
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
4246
- import { join as join9 } from "path";
4341
+ import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
4342
+ import { join as join10 } from "path";
4247
4343
  import { defineCommand as defineCommand30 } from "citty";
4248
4344
  import consola26 from "consola";
4249
4345
 
4250
4346
  // src/commands/nest/enroll.ts
4251
- import { hostname as hostname4, homedir as homedir9 } from "os";
4252
- import { existsSync as existsSync10, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5, chmodSync } from "fs";
4253
- import { join as join8 } from "path";
4347
+ import { hostname as hostname4, homedir as homedir10 } from "os";
4348
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, chmodSync } from "fs";
4349
+ import { join as join9 } from "path";
4254
4350
  import { defineCommand as defineCommand29 } from "citty";
4255
4351
  import consola25 from "consola";
4256
- var NEST_DATA_DIR = join8(homedir9(), ".openape", "nest");
4352
+ var NEST_DATA_DIR = join9(homedir10(), ".openape", "nest");
4257
4353
  function nestAgentName() {
4258
4354
  const raw = hostname4().toLowerCase();
4259
4355
  const head = raw.split(".")[0] ?? raw;
@@ -4286,19 +4382,19 @@ var enrollNestCommand = defineCommand29({
4286
4382
  throw new CliError("Run `apes login <email>` first \u2014 nest enroll attaches the new identity to your owner account.");
4287
4383
  }
4288
4384
  const name = args.name || nestAgentName();
4289
- const authPath = join8(NEST_DATA_DIR, ".config", "apes", "auth.json");
4290
- if (existsSync10(authPath) && !args.force) {
4385
+ const authPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
4386
+ if (existsSync11(authPath) && !args.force) {
4291
4387
  throw new CliError(`Nest already enrolled at ${authPath}. Pass --force to re-enroll.`);
4292
4388
  }
4293
- const sshDir = join8(NEST_DATA_DIR, ".ssh");
4294
- const configDir = join8(NEST_DATA_DIR, ".config", "apes");
4295
- mkdirSync3(sshDir, { recursive: true });
4296
- mkdirSync3(configDir, { recursive: true });
4389
+ const sshDir = join9(NEST_DATA_DIR, ".ssh");
4390
+ const configDir = join9(NEST_DATA_DIR, ".config", "apes");
4391
+ mkdirSync4(sshDir, { recursive: true });
4392
+ mkdirSync4(configDir, { recursive: true });
4297
4393
  consola25.start(`Generating keypair for ${name}\u2026`);
4298
4394
  const { privatePem, publicSshLine } = generateKeyPairInMemory();
4299
- writeFileSync5(join8(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
4395
+ writeFileSync6(join9(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
4300
4396
  `, { mode: 384 });
4301
- writeFileSync5(join8(sshDir, "id_ed25519.pub"), `${publicSshLine}
4397
+ writeFileSync6(join9(sshDir, "id_ed25519.pub"), `${publicSshLine}
4302
4398
  `, { mode: 420 });
4303
4399
  chmodSync(sshDir, 448);
4304
4400
  consola25.start(`Registering nest at ${idp}\u2026`);
@@ -4315,10 +4411,10 @@ var enrollNestCommand = defineCommand29({
4315
4411
  accessToken: token,
4316
4412
  email: registration.email,
4317
4413
  expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
4318
- keyPath: join8(sshDir, "id_ed25519"),
4414
+ keyPath: join9(sshDir, "id_ed25519"),
4319
4415
  ownerEmail: ownerAuth.email
4320
4416
  });
4321
- writeFileSync5(authPath, authJson, { mode: 384 });
4417
+ writeFileSync6(authPath, authJson, { mode: 384 });
4322
4418
  chmodSync(configDir, 448);
4323
4419
  consola25.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
4324
4420
  consola25.info("");
@@ -4384,11 +4480,11 @@ var authorizeNestCommand = defineCommand30({
4384
4480
  }
4385
4481
  },
4386
4482
  async run({ args }) {
4387
- const nestAuthPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
4388
- if (!existsSync11(nestAuthPath)) {
4483
+ const nestAuthPath = join10(NEST_DATA_DIR, ".config", "apes", "auth.json");
4484
+ if (!existsSync12(nestAuthPath)) {
4389
4485
  throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
4390
4486
  }
4391
- const nestAuth = JSON.parse(readFileSync9(nestAuthPath, "utf8"));
4487
+ const nestAuth = JSON.parse(readFileSync10(nestAuthPath, "utf8"));
4392
4488
  if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
4393
4489
  const allow = args.allow ?? DEFAULT_ALLOW_PATTERNS.join(",");
4394
4490
  consola26.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
@@ -4415,102 +4511,31 @@ var authorizeNestCommand = defineCommand30({
4415
4511
  });
4416
4512
 
4417
4513
  // src/commands/nest/destroy.ts
4514
+ import { execFileSync as execFileSync9 } from "child_process";
4418
4515
  import { defineCommand as defineCommand31 } from "citty";
4419
4516
  import consola27 from "consola";
4420
-
4421
- // src/lib/nest-intent.ts
4422
- import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync10, statSync as statSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
4423
- import { homedir as homedir10 } from "os";
4424
- import { join as join10 } from "path";
4425
- import { randomUUID } from "crypto";
4426
- var POLL_INTERVAL_MS = 200;
4427
- var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
4428
- function resolveIntentDir() {
4429
- if (process.env.OPENAPE_NEST_INTENT_DIR) return process.env.OPENAPE_NEST_INTENT_DIR;
4430
- if (existsSync12("/var/openape/nest/intents")) return "/var/openape/nest/intents";
4431
- return join10(homedir10(), ".openape", "nest", "intents");
4432
- }
4433
- async function dispatchIntent(intent, opts = {}) {
4434
- const id = randomUUID();
4435
- const dir = resolveIntentDir();
4436
- if (!existsSync12(dir)) {
4437
- throw new CliError(`Nest intent dir does not exist: ${dir}
4438
- Is the nest daemon running? Try \`ps aux | grep openape-nest\`.`);
4439
- }
4440
- const intentPath = join10(dir, `${id}.json`);
4441
- const responsePath = join10(dir, `${id}.response`);
4442
- const tmpPath = `${intentPath}.tmp`;
4443
- writeFileSync6(tmpPath, `${JSON.stringify({ id, ...intent })}
4444
- `, { mode: 432 });
4445
- try {
4446
- const fs = await import("fs");
4447
- fs.renameSync(tmpPath, intentPath);
4448
- } catch (err) {
4449
- try {
4450
- unlinkSync(tmpPath);
4451
- } catch {
4452
- }
4453
- throw err;
4454
- }
4455
- const deadline = Date.now() + (opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
4456
- while (Date.now() < deadline) {
4457
- if (existsSync12(responsePath)) {
4458
- let raw;
4459
- try {
4460
- const st = statSync2(responsePath);
4461
- if (Date.now() - st.mtimeMs < 50) {
4462
- await sleep(50);
4463
- }
4464
- raw = readFileSync10(responsePath, "utf8");
4465
- } catch {
4466
- await sleep(POLL_INTERVAL_MS);
4467
- continue;
4468
- }
4469
- try {
4470
- unlinkSync(responsePath);
4471
- } catch {
4472
- }
4473
- let parsed;
4474
- try {
4475
- parsed = JSON.parse(raw);
4476
- } catch (err) {
4477
- throw new CliError(`malformed nest response: ${err instanceof Error ? err.message : String(err)}`);
4478
- }
4479
- if (!parsed.ok) {
4480
- throw new CliError(`nest: ${parsed.error}`);
4481
- }
4482
- return parsed.result;
4483
- }
4484
- await sleep(POLL_INTERVAL_MS);
4485
- }
4486
- try {
4487
- unlinkSync(intentPath);
4488
- } catch {
4489
- }
4490
- throw new CliError(`nest intent timeout (${(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS) / 1e3}s) \u2014 Nest daemon may not be running or is stuck.`);
4491
- }
4492
- function sleep(ms) {
4493
- return new Promise((resolve4) => setTimeout(resolve4, ms));
4494
- }
4495
-
4496
- // src/commands/nest/destroy.ts
4497
4517
  var destroyNestCommand = defineCommand31({
4498
4518
  meta: {
4499
4519
  name: "destroy",
4500
- description: "Tear down an agent on the local nest. Drops an intent file the nest daemon picks up."
4520
+ description: "Destroy a local agent. Wraps `apes run --as root -- apes agents destroy <name>`; the Nest watches its registry and pm2-deletes the bridge automatically."
4501
4521
  },
4502
4522
  args: {
4503
4523
  name: { type: "positional", required: true, description: "Agent name to destroy" }
4504
4524
  },
4505
4525
  async run({ args }) {
4506
4526
  const name = String(args.name);
4507
- await dispatchIntent({ action: "destroy", name });
4508
- consola27.success(`Destroyed ${name}`);
4527
+ try {
4528
+ execFileSync9("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
4529
+ consola27.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
4530
+ } catch (err) {
4531
+ const status = err.status ?? 1;
4532
+ throw new CliExit(status);
4533
+ }
4509
4534
  }
4510
4535
  });
4511
4536
 
4512
4537
  // src/commands/nest/install.ts
4513
- import { execFileSync as execFileSync9 } from "child_process";
4538
+ import { execFileSync as execFileSync10 } from "child_process";
4514
4539
  import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
4515
4540
  import { homedir as homedir11, userInfo as userInfo2 } from "os";
4516
4541
  import { dirname as dirname3, join as join11 } from "path";
@@ -4708,10 +4733,10 @@ var installNestCommand = defineCommand32({
4708
4733
  }
4709
4734
  const uid = userInfo2().uid;
4710
4735
  try {
4711
- execFileSync9("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4736
+ execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4712
4737
  } catch {
4713
4738
  }
4714
- execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4739
+ execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4715
4740
  consola28.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4716
4741
  consola28.info("");
4717
4742
  consola28.info("Next steps for zero-prompt spawn \u2014 both one-time:");
@@ -4729,23 +4754,23 @@ import consola29 from "consola";
4729
4754
  var listNestCommand = defineCommand33({
4730
4755
  meta: {
4731
4756
  name: "list",
4732
- description: "List agents registered with the local nest. File-based intent."
4757
+ description: "List agents registered with the local nest. Reads /var/openape/nest/agents.json directly."
4733
4758
  },
4734
4759
  args: {
4735
4760
  json: { type: "boolean", description: "JSON output for scripts" }
4736
4761
  },
4737
4762
  async run({ args }) {
4738
- const result = await dispatchIntent({ action: "list" });
4763
+ const reg = readNestRegistry();
4739
4764
  if (args.json) {
4740
- console.log(JSON.stringify(result, null, 2));
4765
+ console.log(JSON.stringify(reg, null, 2));
4741
4766
  return;
4742
4767
  }
4743
- if (result.agents.length === 0) {
4768
+ if (reg.agents.length === 0) {
4744
4769
  consola29.info("(no agents registered with this nest)");
4745
4770
  return;
4746
4771
  }
4747
- consola29.info(`${result.agents.length} agent(s) registered with this nest:`);
4748
- for (const a of result.agents) {
4772
+ consola29.info(`${reg.agents.length} agent(s) registered with this nest:`);
4773
+ for (const a of reg.agents) {
4749
4774
  const bridge = a.bridge ? " bridge=on" : "";
4750
4775
  consola29.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
4751
4776
  }
@@ -4753,38 +4778,51 @@ var listNestCommand = defineCommand33({
4753
4778
  });
4754
4779
 
4755
4780
  // src/commands/nest/spawn.ts
4781
+ import { execFileSync as execFileSync11 } from "child_process";
4756
4782
  import { defineCommand as defineCommand34 } from "citty";
4757
4783
  import consola30 from "consola";
4758
4784
  var spawnNestCommand = defineCommand34({
4759
4785
  meta: {
4760
4786
  name: "spawn",
4761
- description: "Spawn a new agent on the local nest. Drops an intent file the nest daemon picks up; UNIX permissions on the intents dir gate access."
4787
+ description: "Spawn a new agent locally. Wraps `apes run --as root -- apes agents spawn <name>`; the Nest watches its registry and starts the bridge in pm2 automatically."
4762
4788
  },
4763
4789
  args: {
4764
4790
  name: { type: "positional", required: true, description: "Agent name (lowercase, [a-z0-9-], max 24 chars)" },
4765
4791
  "no-bridge": { type: "boolean", description: "Skip installing the chat-bridge daemon (default: install it)" },
4766
4792
  "bridge-key": { type: "string", description: "Override LITELLM_API_KEY (default: read from ~/litellm/.env)" },
4767
- "bridge-base-url": { type: "string", description: "Override LITELLM_BASE_URL (default: read from ~/litellm/.env)" },
4793
+ "bridge-base-url": { type: "string", description: "Override LITELLM_BASE_URL" },
4768
4794
  "bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" }
4769
4795
  },
4770
4796
  async run({ args }) {
4771
4797
  const name = String(args.name);
4772
- const intent = {
4773
- action: "spawn",
4774
- name,
4775
- bridge: !args["no-bridge"]
4776
- };
4777
- if (typeof args["bridge-key"] === "string") intent.bridgeKey = args["bridge-key"];
4778
- if (typeof args["bridge-base-url"] === "string") intent.bridgeBaseUrl = args["bridge-base-url"];
4779
- if (typeof args["bridge-model"] === "string") intent.bridgeModel = args["bridge-model"];
4780
- const result = await dispatchIntent(intent);
4781
- consola30.success(`Spawned ${result.name} (uid=${result.uid}, home=${result.home})`);
4798
+ const apesArgs = [
4799
+ "run",
4800
+ "--as",
4801
+ "root",
4802
+ "--wait",
4803
+ "--",
4804
+ "apes",
4805
+ "agents",
4806
+ "spawn",
4807
+ name
4808
+ ];
4809
+ if (!args["no-bridge"]) apesArgs.push("--bridge");
4810
+ if (typeof args["bridge-key"] === "string") apesArgs.push("--bridge-key", args["bridge-key"]);
4811
+ if (typeof args["bridge-base-url"] === "string") apesArgs.push("--bridge-base-url", args["bridge-base-url"]);
4812
+ if (typeof args["bridge-model"] === "string") apesArgs.push("--bridge-model", args["bridge-model"]);
4813
+ try {
4814
+ execFileSync11("apes", apesArgs, { stdio: "inherit" });
4815
+ consola30.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
4816
+ } catch (err) {
4817
+ const status = err.status ?? 1;
4818
+ throw new CliExit(status);
4819
+ }
4782
4820
  }
4783
4821
  });
4784
4822
 
4785
4823
  // src/commands/nest/uninstall.ts
4786
- import { execFileSync as execFileSync10 } from "child_process";
4787
- import { existsSync as existsSync14, unlinkSync as unlinkSync2 } from "fs";
4824
+ import { execFileSync as execFileSync12 } from "child_process";
4825
+ import { existsSync as existsSync14, unlinkSync } from "fs";
4788
4826
  import { homedir as homedir12, userInfo as userInfo3 } from "os";
4789
4827
  import { join as join12 } from "path";
4790
4828
  import { defineCommand as defineCommand35 } from "citty";
@@ -4799,13 +4837,13 @@ var uninstallNestCommand = defineCommand35({
4799
4837
  const uid = userInfo3().uid;
4800
4838
  const path2 = join12(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4801
4839
  try {
4802
- execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4840
+ execFileSync12("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4803
4841
  consola31.success("Nest daemon stopped");
4804
4842
  } catch {
4805
4843
  consola31.info("Nest daemon was not loaded");
4806
4844
  }
4807
4845
  if (existsSync14(path2)) {
4808
- unlinkSync2(path2);
4846
+ unlinkSync(path2);
4809
4847
  consola31.success(`Removed ${path2}`);
4810
4848
  }
4811
4849
  consola31.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
@@ -5353,7 +5391,7 @@ var adapterCommand = defineCommand41({
5353
5391
  });
5354
5392
 
5355
5393
  // src/commands/run.ts
5356
- import { execFileSync as execFileSync11 } from "child_process";
5394
+ import { execFileSync as execFileSync13 } from "child_process";
5357
5395
  import { hostname as hostname5 } from "os";
5358
5396
  import { basename } from "path";
5359
5397
  import { defineCommand as defineCommand42 } from "citty";
@@ -5631,7 +5669,7 @@ function execShellCommand(command) {
5631
5669
  throw new CliError("No command to execute");
5632
5670
  try {
5633
5671
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5634
- execFileSync11(command[0], command.slice(1), {
5672
+ execFileSync13(command[0], command.slice(1), {
5635
5673
  stdio: "inherit",
5636
5674
  env: inheritedEnv
5637
5675
  });
@@ -5783,7 +5821,7 @@ async function runAudienceMode(audience, action, args) {
5783
5821
  consola36.info(`Executing: ${command.join(" ")}`);
5784
5822
  try {
5785
5823
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5786
- execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
5824
+ execFileSync13(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
5787
5825
  stdio: "inherit",
5788
5826
  env: inheritedEnv
5789
5827
  });
@@ -6285,7 +6323,7 @@ var mcpCommand = defineCommand48({
6285
6323
  if (transport !== "stdio" && transport !== "sse") {
6286
6324
  throw new Error('Transport must be "stdio" or "sse"');
6287
6325
  }
6288
- const { startMcpServer } = await import("./server-5LHDHGDG.js");
6326
+ const { startMcpServer } = await import("./server-A24A4ILW.js");
6289
6327
  await startMcpServer(transport, port);
6290
6328
  }
6291
6329
  });
@@ -6293,7 +6331,7 @@ var mcpCommand = defineCommand48({
6293
6331
  // src/commands/init/index.ts
6294
6332
  import { existsSync as existsSync15, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
6295
6333
  import { randomBytes } from "crypto";
6296
- import { execFileSync as execFileSync12 } from "child_process";
6334
+ import { execFileSync as execFileSync14 } from "child_process";
6297
6335
  import { join as join14 } from "path";
6298
6336
  import { defineCommand as defineCommand49 } from "citty";
6299
6337
  import consola40 from "consola";
@@ -6305,11 +6343,11 @@ async function downloadTemplate(repo, targetDir) {
6305
6343
  function installDeps(dir) {
6306
6344
  const hasLockFile = (name) => existsSync15(join14(dir, name));
6307
6345
  if (hasLockFile("pnpm-lock.yaml")) {
6308
- execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6346
+ execFileSync14("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6309
6347
  } else if (hasLockFile("bun.lockb")) {
6310
- execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
6348
+ execFileSync14("bun", ["install"], { cwd: dir, stdio: "inherit" });
6311
6349
  } else {
6312
- execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
6350
+ execFileSync14("npm", ["install"], { cwd: dir, stdio: "inherit" });
6313
6351
  }
6314
6352
  }
6315
6353
  async function promptChoice(message, choices) {
@@ -6923,7 +6961,7 @@ async function bestEffortGrantCount(idp) {
6923
6961
  }
6924
6962
  }
6925
6963
  async function runHealth(args) {
6926
- const version = true ? "1.13.1" : "0.0.0";
6964
+ const version = true ? "1.15.0" : "0.0.0";
6927
6965
  const auth = loadAuth();
6928
6966
  if (!auth) {
6929
6967
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -7196,10 +7234,10 @@ if (shellRewrite) {
7196
7234
  if (shellRewrite.action === "rewrite") {
7197
7235
  process.argv = shellRewrite.argv;
7198
7236
  } else if (shellRewrite.action === "version") {
7199
- console.log(`ape-shell ${"1.13.1"} (OpenApe DDISA shell wrapper)`);
7237
+ console.log(`ape-shell ${"1.15.0"} (OpenApe DDISA shell wrapper)`);
7200
7238
  process.exit(0);
7201
7239
  } else if (shellRewrite.action === "help") {
7202
- console.log(`ape-shell ${"1.13.1"} \u2014 OpenApe DDISA shell wrapper`);
7240
+ console.log(`ape-shell ${"1.15.0"} \u2014 OpenApe DDISA shell wrapper`);
7203
7241
  console.log("");
7204
7242
  console.log("Usage:");
7205
7243
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -7257,7 +7295,7 @@ var configCommand = defineCommand60({
7257
7295
  var main = defineCommand60({
7258
7296
  meta: {
7259
7297
  name: "apes",
7260
- version: "1.13.1",
7298
+ version: "1.15.0",
7261
7299
  description: "Unified CLI for OpenApe"
7262
7300
  },
7263
7301
  subCommands: {
@@ -7314,7 +7352,7 @@ async function maybeRefreshAuth() {
7314
7352
  }
7315
7353
  }
7316
7354
  await maybeRefreshAuth();
7317
- await maybeWarnStaleVersion("1.13.1").catch(() => {
7355
+ await maybeWarnStaleVersion("1.15.0").catch(() => {
7318
7356
  });
7319
7357
  runMain(main).catch((err) => {
7320
7358
  if (err instanceof CliExit) {