@openape/apes 0.22.1 → 0.23.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
@@ -379,7 +379,7 @@ async function loginWithPKCE(idp) {
379
379
  consola2.success(`Logged in as ${payload.email || payload.sub}`);
380
380
  }
381
381
  async function loginWithKey(idp, keyPath, agentEmail) {
382
- const { readFileSync: readFileSync8 } = await import("fs");
382
+ const { readFileSync: readFileSync9 } = await import("fs");
383
383
  const { sign: sign3 } = await import("crypto");
384
384
  const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
385
385
  const challengeUrl = await getAgentChallengeEndpoint(idp);
@@ -392,7 +392,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
392
392
  throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
393
393
  }
394
394
  const { challenge } = await challengeResp.json();
395
- const keyContent = readFileSync8(keyPath, "utf-8");
395
+ const keyContent = readFileSync9(keyPath, "utf-8");
396
396
  const privateKey = loadEd25519PrivateKey2(keyContent);
397
397
  const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
398
398
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -1873,7 +1873,7 @@ mkdir -p "$HOME_DIR/.ssh" "$HOME_DIR/.config/apes"
1873
1873
  cat > "$HOME_DIR/.ssh/id_ed25519" ${shHeredoc(privatePemForHeredoc.trimEnd())}
1874
1874
  cat > "$HOME_DIR/.ssh/id_ed25519.pub" ${shHeredoc(`${input.publicKeySshLine}`)}
1875
1875
  cat > "$HOME_DIR/.config/apes/auth.json" ${shHeredoc(input.authJson)}
1876
- ${claudeBlock}${claudeTokenBlock}
1876
+ ${claudeBlock}${claudeTokenBlock}${buildBridgeBlock(input.bridge)}
1877
1877
  chown -R "$NAME:staff" "$HOME_DIR"
1878
1878
  chmod 700 "$HOME_DIR/.ssh"
1879
1879
  chmod 700 "$HOME_DIR/.config"
@@ -1886,6 +1886,28 @@ if [ -f "$HOME_DIR/.config/openape/claude-token.env" ]; then
1886
1886
  fi
1887
1887
 
1888
1888
  echo "OK $NAME uid=$NEXT_UID home=$HOME_DIR"
1889
+ ${buildBridgeBootstrapBlock(input.bridge)}`;
1890
+ }
1891
+ function buildBridgeBlock(bridge) {
1892
+ if (!bridge) return "";
1893
+ return `
1894
+ mkdir -p "$HOME_DIR/Library/LaunchAgents" "$HOME_DIR/Library/Application Support/openape/bridge" "$HOME_DIR/Library/Logs" "$HOME_DIR/.pi/agent"
1895
+ cat > "$HOME_DIR/.pi/agent/.env" ${shHeredoc(bridge.envFile)}
1896
+ cat > "$HOME_DIR/Library/Application Support/openape/bridge/start.sh" ${shHeredoc(bridge.startScript)}
1897
+ chmod 755 "$HOME_DIR/Library/Application Support/openape/bridge/start.sh"
1898
+ cat > "$HOME_DIR/Library/LaunchAgents/${bridge.plistLabel}.plist" ${shHeredoc(bridge.plistContent)}
1899
+ chmod 600 "$HOME_DIR/.pi/agent/.env"
1900
+ `;
1901
+ }
1902
+ function buildBridgeBootstrapBlock(bridge) {
1903
+ if (!bridge) return "";
1904
+ return `
1905
+ # Load the bridge launchd job into the agent's gui domain. Runs as root
1906
+ # from the spawn setup script so we target gui/<agent-uid> explicitly.
1907
+ # Failure here is non-fatal \u2014 the plist still lands and launchd will pick
1908
+ # it up next time the agent logs in.
1909
+ launchctl bootstrap "gui/$NEXT_UID" "$HOME_DIR/Library/LaunchAgents/${bridge.plistLabel}.plist" || \\
1910
+ echo "warn: bridge bootstrap failed for gui/$NEXT_UID; loads on next login"
1889
1911
  `;
1890
1912
  }
1891
1913
  function buildDestroyTeardownScript(input) {
@@ -2329,7 +2351,7 @@ var registerAgentCommand = defineCommand22({
2329
2351
  import { execFileSync as execFileSync4 } from "child_process";
2330
2352
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
2331
2353
  import { tmpdir as tmpdir2 } from "os";
2332
- import { join as join3 } from "path";
2354
+ import { join as join4 } from "path";
2333
2355
  import { defineCommand as defineCommand23 } from "citty";
2334
2356
  import consola21 from "consola";
2335
2357
 
@@ -2389,6 +2411,103 @@ function generateKeyPairInMemory() {
2389
2411
  };
2390
2412
  }
2391
2413
 
2414
+ // src/lib/llm-bridge.ts
2415
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
2416
+ import { homedir as homedir5 } from "os";
2417
+ import { join as join3 } from "path";
2418
+ var BRIDGE_PLIST_LABEL = "eco.hofmann.apes.bridge";
2419
+ function readLitellmEnv(envPath = join3(homedir5(), "litellm", ".env")) {
2420
+ if (!existsSync6(envPath)) return null;
2421
+ try {
2422
+ const text = readFileSync5(envPath, "utf8");
2423
+ const out = {};
2424
+ for (const line of text.split("\n")) {
2425
+ const trimmed = line.trim();
2426
+ if (!trimmed || trimmed.startsWith("#")) continue;
2427
+ const eq = trimmed.indexOf("=");
2428
+ if (eq < 0) continue;
2429
+ const key = trimmed.slice(0, eq).trim();
2430
+ const value = trimmed.slice(eq + 1).trim();
2431
+ if (key === "LITELLM_MASTER_KEY" || key === "LITELLM_API_KEY") out.apiKey = value;
2432
+ if (key === "LITELLM_BASE_URL") out.baseUrl = value;
2433
+ }
2434
+ return out;
2435
+ } catch {
2436
+ return null;
2437
+ }
2438
+ }
2439
+ function resolveBridgeConfig(opts) {
2440
+ const env = readLitellmEnv(opts.envPath);
2441
+ const apiKey = opts.cliKey ?? env?.apiKey;
2442
+ const baseUrl = opts.cliBaseUrl ?? env?.baseUrl ?? "http://127.0.0.1:4000/v1";
2443
+ if (!apiKey) {
2444
+ throw new Error(
2445
+ "No LITELLM_API_KEY resolved. Pass --bridge-key sk-\u2026 or write LITELLM_MASTER_KEY into ~/litellm/.env first."
2446
+ );
2447
+ }
2448
+ return { baseUrl, apiKey };
2449
+ }
2450
+ function buildBridgeEnvFile(cfg) {
2451
+ return `# Auto-generated by 'apes agents spawn --bridge'.
2452
+ # Read by the chat-bridge daemon at boot to talk to the local LLM proxy.
2453
+ LITELLM_BASE_URL=${cfg.baseUrl}
2454
+ LITELLM_API_KEY=${cfg.apiKey}
2455
+ `;
2456
+ }
2457
+ function buildBridgeStartScript() {
2458
+ return `#!/usr/bin/env bash
2459
+ # Auto-generated by 'apes agents spawn --bridge'.
2460
+ # Idempotent installer + launcher. First boot installs @openape/chat-bridge
2461
+ # globally via bun (homebrew-provided); subsequent boots skip the install.
2462
+ set -euo pipefail
2463
+ export PATH="/opt/homebrew/bin:$HOME/.bun/bin:$PATH"
2464
+ if ! command -v openape-chat-bridge >/dev/null 2>&1; then
2465
+ bun add -g @openape/chat-bridge
2466
+ fi
2467
+ set -a
2468
+ . "$HOME/.pi/agent/.env"
2469
+ set +a
2470
+ exec openape-chat-bridge
2471
+ `;
2472
+ }
2473
+ function buildBridgePlist(homeDir) {
2474
+ const startScript = `${homeDir}/Library/Application Support/openape/bridge/start.sh`;
2475
+ const stdoutLog = `${homeDir}/Library/Logs/openape-chat-bridge.log`;
2476
+ const stderrLog = `${homeDir}/Library/Logs/openape-chat-bridge.err.log`;
2477
+ return `<?xml version="1.0" encoding="UTF-8"?>
2478
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2479
+ <plist version="1.0">
2480
+ <dict>
2481
+ <key>Label</key>
2482
+ <string>${BRIDGE_PLIST_LABEL}</string>
2483
+ <key>ProgramArguments</key>
2484
+ <array>
2485
+ <string>${startScript}</string>
2486
+ </array>
2487
+ <key>WorkingDirectory</key>
2488
+ <string>${homeDir}</string>
2489
+ <key>RunAtLoad</key>
2490
+ <true/>
2491
+ <key>KeepAlive</key>
2492
+ <true/>
2493
+ <key>ThrottleInterval</key>
2494
+ <integer>10</integer>
2495
+ <key>StandardOutPath</key>
2496
+ <string>${stdoutLog}</string>
2497
+ <key>StandardErrorPath</key>
2498
+ <string>${stderrLog}</string>
2499
+ <key>EnvironmentVariables</key>
2500
+ <dict>
2501
+ <key>HOME</key>
2502
+ <string>${homeDir}</string>
2503
+ <key>PATH</key>
2504
+ <string>/opt/homebrew/bin:/usr/bin:/bin</string>
2505
+ </dict>
2506
+ </dict>
2507
+ </plist>
2508
+ `;
2509
+ }
2510
+
2392
2511
  // src/commands/agents/spawn.ts
2393
2512
  var spawnAgentCommand = defineCommand23({
2394
2513
  meta: {
@@ -2416,6 +2535,18 @@ var spawnAgentCommand = defineCommand23({
2416
2535
  "claude-token-stdin": {
2417
2536
  type: "boolean",
2418
2537
  description: "Read the Claude Code OAuth token from stdin (paranoid form of --claude-token)."
2538
+ },
2539
+ "bridge": {
2540
+ type: "boolean",
2541
+ description: "Install the openape-chat-bridge daemon for this agent: drops a launchd plist that runs `@openape/chat-bridge` so the agent answers chat.openape.ai messages. Reads LITELLM_API_KEY/BASE_URL defaults from ~/litellm/.env; override via --bridge-key / --bridge-base-url."
2542
+ },
2543
+ "bridge-key": {
2544
+ type: "string",
2545
+ description: "Override LITELLM_API_KEY for the bridge (default: read from ~/litellm/.env)."
2546
+ },
2547
+ "bridge-base-url": {
2548
+ type: "string",
2549
+ description: "Override LITELLM_BASE_URL for the bridge (default: read from ~/litellm/.env or http://127.0.0.1:4000/v1)."
2419
2550
  }
2420
2551
  },
2421
2552
  async run({ args }) {
@@ -2461,8 +2592,8 @@ and try again.`
2461
2592
  throw new CliError(`macOS user "${name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
2462
2593
  }
2463
2594
  const homeDir = `/Users/${name}`;
2464
- const scratch = mkdtempSync2(join3(tmpdir2(), `apes-spawn-${name}-`));
2465
- const scriptPath = join3(scratch, "setup.sh");
2595
+ const scratch = mkdtempSync2(join4(tmpdir2(), `apes-spawn-${name}-`));
2596
+ const scriptPath = join4(scratch, "setup.sh");
2466
2597
  try {
2467
2598
  consola21.start(`Generating keypair for ${name}\u2026`);
2468
2599
  const { privatePem, publicSshLine } = generateKeyPairInMemory();
@@ -2486,6 +2617,18 @@ and try again.`
2486
2617
  flag: typeof args["claude-token"] === "string" ? args["claude-token"] : void 0,
2487
2618
  fromStdin: !!args["claude-token-stdin"]
2488
2619
  });
2620
+ const bridge = args.bridge ? (() => {
2621
+ const cfg = resolveBridgeConfig({
2622
+ cliKey: typeof args["bridge-key"] === "string" ? args["bridge-key"] : void 0,
2623
+ cliBaseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0
2624
+ });
2625
+ return {
2626
+ plistLabel: BRIDGE_PLIST_LABEL,
2627
+ plistContent: buildBridgePlist(homeDir),
2628
+ startScript: buildBridgeStartScript(),
2629
+ envFile: buildBridgeEnvFile(cfg)
2630
+ };
2631
+ })() : null;
2489
2632
  const script = buildSpawnSetupScript({
2490
2633
  name,
2491
2634
  homeDir,
@@ -2495,7 +2638,8 @@ and try again.`
2495
2638
  authJson,
2496
2639
  claudeSettingsJson: includeClaudeHook ? CLAUDE_SETTINGS_JSON : null,
2497
2640
  hookScriptSource: includeClaudeHook ? BASH_VIA_APE_SHELL_HOOK_SOURCE : null,
2498
- claudeOauthToken
2641
+ claudeOauthToken,
2642
+ bridge
2499
2643
  });
2500
2644
  writeFileSync3(scriptPath, script, { mode: 448 });
2501
2645
  consola21.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
@@ -3359,7 +3503,7 @@ import { spawn } from "child_process";
3359
3503
  import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
3360
3504
  import { createRequire } from "module";
3361
3505
  import { tmpdir as tmpdir3 } from "os";
3362
- import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
3506
+ import { dirname as dirname2, join as join5, resolve as resolve3 } from "path";
3363
3507
  var require2 = createRequire(import.meta.url);
3364
3508
  function findProxyBin() {
3365
3509
  const pkgPath = require2.resolve("@openape/proxy/package.json");
@@ -3371,8 +3515,8 @@ function findProxyBin() {
3371
3515
  return resolve3(dirname2(pkgPath), binRel);
3372
3516
  }
3373
3517
  async function startEphemeralProxy(configToml) {
3374
- const tmpDir = mkdtempSync3(join4(tmpdir3(), "openape-proxy-"));
3375
- const configPath = join4(tmpDir, "config.toml");
3518
+ const tmpDir = mkdtempSync3(join5(tmpdir3(), "openape-proxy-"));
3519
+ const configPath = join5(tmpDir, "config.toml");
3376
3520
  writeFileSync4(configPath, configToml, { mode: 384 });
3377
3521
  const binPath = findProxyBin();
3378
3522
  const child = spawn(process.execPath, [binPath, "-c", configPath], {
@@ -3808,16 +3952,16 @@ var mcpCommand = defineCommand32({
3808
3952
  if (transport !== "stdio" && transport !== "sse") {
3809
3953
  throw new Error('Transport must be "stdio" or "sse"');
3810
3954
  }
3811
- const { startMcpServer } = await import("./server-WBR7VEDY.js");
3955
+ const { startMcpServer } = await import("./server-D5EQSCWF.js");
3812
3956
  await startMcpServer(transport, port);
3813
3957
  }
3814
3958
  });
3815
3959
 
3816
3960
  // src/commands/init/index.ts
3817
- import { existsSync as existsSync6, copyFileSync, writeFileSync as writeFileSync5 } from "fs";
3961
+ import { existsSync as existsSync7, copyFileSync, writeFileSync as writeFileSync5 } from "fs";
3818
3962
  import { randomBytes } from "crypto";
3819
3963
  import { execFileSync as execFileSync6 } from "child_process";
3820
- import { join as join5 } from "path";
3964
+ import { join as join6 } from "path";
3821
3965
  import { defineCommand as defineCommand33 } from "citty";
3822
3966
  import consola27 from "consola";
3823
3967
  var DEFAULT_IDP_URL = "https://id.openape.at";
@@ -3826,7 +3970,7 @@ async function downloadTemplate(repo, targetDir) {
3826
3970
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
3827
3971
  }
3828
3972
  function installDeps(dir) {
3829
- const hasLockFile = (name) => existsSync6(join5(dir, name));
3973
+ const hasLockFile = (name) => existsSync7(join6(dir, name));
3830
3974
  if (hasLockFile("pnpm-lock.yaml")) {
3831
3975
  execFileSync6("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
3832
3976
  } else if (hasLockFile("bun.lockb")) {
@@ -3891,7 +4035,7 @@ var initCommand = defineCommand33({
3891
4035
  });
3892
4036
  async function initSP(targetDir) {
3893
4037
  const dir = targetDir || "my-app";
3894
- if (existsSync6(join5(dir, "package.json"))) {
4038
+ if (existsSync7(join6(dir, "package.json"))) {
3895
4039
  throw new CliError(`Directory "${dir}" already contains a project.`);
3896
4040
  }
3897
4041
  consola27.start("Scaffolding SP starter...");
@@ -3900,9 +4044,9 @@ async function initSP(targetDir) {
3900
4044
  consola27.start("Installing dependencies...");
3901
4045
  installDeps(dir);
3902
4046
  consola27.success("Dependencies installed");
3903
- const envExample = join5(dir, ".env.example");
3904
- const envFile = join5(dir, ".env");
3905
- if (existsSync6(envExample) && !existsSync6(envFile)) {
4047
+ const envExample = join6(dir, ".env.example");
4048
+ const envFile = join6(dir, ".env");
4049
+ if (existsSync7(envExample) && !existsSync7(envFile)) {
3906
4050
  copyFileSync(envExample, envFile);
3907
4051
  consola27.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
3908
4052
  }
@@ -3916,7 +4060,7 @@ async function initSP(targetDir) {
3916
4060
  }
3917
4061
  async function initIdP(targetDir) {
3918
4062
  const dir = targetDir || "my-idp";
3919
- if (existsSync6(join5(dir, "package.json"))) {
4063
+ if (existsSync7(join6(dir, "package.json"))) {
3920
4064
  throw new CliError(`Directory "${dir}" already contains a project.`);
3921
4065
  }
3922
4066
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -3948,7 +4092,7 @@ async function initIdP(targetDir) {
3948
4092
  `NUXT_OPENAPE_RP_ID=${domain}`,
3949
4093
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
3950
4094
  ].join("\n");
3951
- writeFileSync5(join5(dir, ".env"), `${envContent}
4095
+ writeFileSync5(join6(dir, ".env"), `${envContent}
3952
4096
  `, { mode: 384 });
3953
4097
  consola27.success(".env created");
3954
4098
  console.log("");
@@ -3969,7 +4113,7 @@ async function initIdP(targetDir) {
3969
4113
 
3970
4114
  // src/commands/enroll.ts
3971
4115
  import { Buffer as Buffer5 } from "buffer";
3972
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
4116
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
3973
4117
  import { execFile as execFile2 } from "child_process";
3974
4118
  import { sign as sign2 } from "crypto";
3975
4119
  import { defineCommand as defineCommand34 } from "citty";
@@ -3985,7 +4129,7 @@ function openBrowser2(url) {
3985
4129
  }
3986
4130
  async function pollForEnrollment(idp, agentEmail, keyPath) {
3987
4131
  const resolvedKey = resolveKeyPath(keyPath);
3988
- const keyContent = readFileSync5(resolvedKey, "utf-8");
4132
+ const keyContent = readFileSync6(resolvedKey, "utf-8");
3989
4133
  const privateKey = loadEd25519PrivateKey(keyContent);
3990
4134
  const challengeUrl = await getAgentChallengeEndpoint(idp);
3991
4135
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -4053,7 +4197,7 @@ var enrollCommand = defineCommand34({
4053
4197
  }) || DEFAULT_KEY_PATH;
4054
4198
  const resolvedKey = resolveKeyPath(keyPath);
4055
4199
  let publicKey;
4056
- if (existsSync7(resolvedKey)) {
4200
+ if (existsSync8(resolvedKey)) {
4057
4201
  publicKey = readPublicKey(resolvedKey);
4058
4202
  consola28.success(`Using existing key ${keyPath}`);
4059
4203
  } else {
@@ -4097,7 +4241,7 @@ var enrollCommand = defineCommand34({
4097
4241
  });
4098
4242
 
4099
4243
  // src/commands/register-user.ts
4100
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
4244
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
4101
4245
  import { defineCommand as defineCommand35 } from "citty";
4102
4246
  import consola29 from "consola";
4103
4247
  var registerUserCommand = defineCommand35({
@@ -4136,8 +4280,8 @@ var registerUserCommand = defineCommand35({
4136
4280
  throw new CliError("No IdP URL configured. Run `apes login` first.");
4137
4281
  }
4138
4282
  let publicKey = args.key;
4139
- if (existsSync8(args.key)) {
4140
- publicKey = readFileSync6(args.key, "utf-8").trim();
4283
+ if (existsSync9(args.key)) {
4284
+ publicKey = readFileSync7(args.key, "utf-8").trim();
4141
4285
  }
4142
4286
  if (!publicKey.startsWith("ssh-ed25519 ")) {
4143
4287
  throw new CliError("Public key must be in ssh-ed25519 format.");
@@ -4446,7 +4590,7 @@ async function bestEffortGrantCount(idp) {
4446
4590
  }
4447
4591
  }
4448
4592
  async function runHealth(args) {
4449
- const version = true ? "0.22.1" : "0.0.0";
4593
+ const version = true ? "0.23.0" : "0.0.0";
4450
4594
  const auth = loadAuth();
4451
4595
  if (!auth) {
4452
4596
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -4639,25 +4783,25 @@ var workflowsCommand = defineCommand43({
4639
4783
  });
4640
4784
 
4641
4785
  // src/version-check.ts
4642
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
4643
- import { homedir as homedir5 } from "os";
4644
- import { join as join6 } from "path";
4786
+ import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
4787
+ import { homedir as homedir6 } from "os";
4788
+ import { join as join7 } from "path";
4645
4789
  import consola35 from "consola";
4646
4790
  var PACKAGE_NAME = "@openape/apes";
4647
4791
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
4648
- var CACHE_FILE = join6(homedir5(), ".config", "apes", ".version-check.json");
4792
+ var CACHE_FILE = join7(homedir6(), ".config", "apes", ".version-check.json");
4649
4793
  function readCache() {
4650
- if (!existsSync9(CACHE_FILE)) return null;
4794
+ if (!existsSync10(CACHE_FILE)) return null;
4651
4795
  try {
4652
- return JSON.parse(readFileSync7(CACHE_FILE, "utf-8"));
4796
+ return JSON.parse(readFileSync8(CACHE_FILE, "utf-8"));
4653
4797
  } catch {
4654
4798
  return null;
4655
4799
  }
4656
4800
  }
4657
4801
  function writeCache(entry) {
4658
4802
  try {
4659
- const dir = join6(homedir5(), ".config", "apes");
4660
- if (!existsSync9(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
4803
+ const dir = join7(homedir6(), ".config", "apes");
4804
+ if (!existsSync10(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
4661
4805
  writeFileSync6(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
4662
4806
  } catch {
4663
4807
  }
@@ -4719,10 +4863,10 @@ if (shellRewrite) {
4719
4863
  if (shellRewrite.action === "rewrite") {
4720
4864
  process.argv = shellRewrite.argv;
4721
4865
  } else if (shellRewrite.action === "version") {
4722
- console.log(`ape-shell ${"0.22.1"} (OpenApe DDISA shell wrapper)`);
4866
+ console.log(`ape-shell ${"0.23.0"} (OpenApe DDISA shell wrapper)`);
4723
4867
  process.exit(0);
4724
4868
  } else if (shellRewrite.action === "help") {
4725
- console.log(`ape-shell ${"0.22.1"} \u2014 OpenApe DDISA shell wrapper`);
4869
+ console.log(`ape-shell ${"0.23.0"} \u2014 OpenApe DDISA shell wrapper`);
4726
4870
  console.log("");
4727
4871
  console.log("Usage:");
4728
4872
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -4780,7 +4924,7 @@ var configCommand = defineCommand44({
4780
4924
  var main = defineCommand44({
4781
4925
  meta: {
4782
4926
  name: "apes",
4783
- version: "0.22.1",
4927
+ version: "0.23.0",
4784
4928
  description: "Unified CLI for OpenApe"
4785
4929
  },
4786
4930
  subCommands: {
@@ -4835,7 +4979,7 @@ async function maybeRefreshAuth() {
4835
4979
  }
4836
4980
  }
4837
4981
  await maybeRefreshAuth();
4838
- await maybeWarnStaleVersion("0.22.1").catch(() => {
4982
+ await maybeWarnStaleVersion("0.23.0").catch(() => {
4839
4983
  });
4840
4984
  runMain(main).catch((err) => {
4841
4985
  if (err instanceof CliExit) {