@openape/apes 1.28.11 → 1.28.13

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
@@ -13,7 +13,7 @@ import {
13
13
  runLoop,
14
14
  taskTools,
15
15
  worktreePathFor
16
- } from "./chunk-OOKB2IL2.js";
16
+ } from "./chunk-ZEUSCNCH.js";
17
17
  import {
18
18
  loadEd25519PrivateKey,
19
19
  readPublicKeyComment
@@ -50,7 +50,7 @@ import {
50
50
  searchAdapters,
51
51
  verifyAndExecute,
52
52
  waitForGrantStatus
53
- } from "./chunk-DYSFQ26B.js";
53
+ } from "./chunk-PEA2RDWK.js";
54
54
  import {
55
55
  ApiError,
56
56
  apiFetch,
@@ -58,7 +58,7 @@ import {
58
58
  getAgentChallengeEndpoint,
59
59
  getDelegationsEndpoint,
60
60
  getGrantsEndpoint
61
- } from "./chunk-4KPKANZT.js";
61
+ } from "./chunk-NYJSBFLG.js";
62
62
  import {
63
63
  AUTH_FILE,
64
64
  CONFIG_DIR,
@@ -389,28 +389,30 @@ async function loginWithPKCE(idp) {
389
389
  consola2.success(`Logged in as ${payload.email || payload.sub}`);
390
390
  }
391
391
  async function loginWithKey(idp, keyPath, agentEmail) {
392
- const { readFileSync: readFileSync18 } = await import("fs");
392
+ const { readFileSync: readFileSync21 } = await import("fs");
393
393
  const { sign: sign3 } = await import("crypto");
394
394
  const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
395
395
  const challengeUrl = await getAgentChallengeEndpoint(idp);
396
396
  const challengeResp = await fetch(challengeUrl, {
397
397
  method: "POST",
398
398
  headers: { "Content-Type": "application/json" },
399
- body: JSON.stringify({ agent_id: agentEmail })
399
+ // Use canonical `id` field (the server's /api/auth/challenge handler expects `id`).
400
+ body: JSON.stringify({ id: agentEmail })
400
401
  });
401
402
  if (!challengeResp.ok) {
402
403
  throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
403
404
  }
404
405
  const { challenge } = await challengeResp.json();
405
- const keyContent = readFileSync18(keyPath, "utf-8");
406
+ const keyContent = readFileSync21(keyPath, "utf-8");
406
407
  const privateKey = loadEd25519PrivateKey2(keyContent);
407
408
  const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
408
409
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
409
410
  const authResp = await fetch(authenticateUrl, {
410
411
  method: "POST",
411
412
  headers: { "Content-Type": "application/json" },
413
+ // Use canonical `id` field (the server's /api/auth/authenticate handler expects `id`).
412
414
  body: JSON.stringify({
413
- agent_id: agentEmail,
415
+ id: agentEmail,
414
416
  challenge,
415
417
  signature
416
418
  })
@@ -1982,7 +1984,7 @@ var agentCommand = defineCommand21({
1982
1984
  import { defineCommand as defineCommand32 } from "citty";
1983
1985
 
1984
1986
  // src/commands/agents/allow.ts
1985
- import { execFileSync as execFileSync4 } from "child_process";
1987
+ import { execFileSync as execFileSync10 } from "child_process";
1986
1988
  import { defineCommand as defineCommand22 } from "citty";
1987
1989
  import consola19 from "consola";
1988
1990
 
@@ -2496,6 +2498,348 @@ function isShellRegistered(shellPath) {
2496
2498
  return content.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).includes(shellPath);
2497
2499
  }
2498
2500
 
2501
+ // src/lib/host-platform/index.ts
2502
+ import process2 from "process";
2503
+
2504
+ // src/lib/macos-host.ts
2505
+ import { execFileSync as execFileSync4 } from "child_process";
2506
+ import { hostname as hostname3 } from "os";
2507
+ function getHostId() {
2508
+ try {
2509
+ const output = execFileSync4(
2510
+ "/usr/sbin/ioreg",
2511
+ ["-d2", "-c", "IOPlatformExpertDevice"],
2512
+ { encoding: "utf8", timeout: 2e3 }
2513
+ );
2514
+ const match = output.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
2515
+ return match ? match[1].trim().toLowerCase() : "";
2516
+ } catch {
2517
+ return "";
2518
+ }
2519
+ }
2520
+ function getHostname() {
2521
+ try {
2522
+ return hostname3();
2523
+ } catch {
2524
+ return "";
2525
+ }
2526
+ }
2527
+
2528
+ // src/lib/host-platform/darwin-nest.ts
2529
+ import { execFileSync as execFileSync5 } from "child_process";
2530
+ import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, unlinkSync, writeFileSync } from "fs";
2531
+ import { userInfo } from "os";
2532
+ import { join as join2 } from "path";
2533
+ var PLIST_LABEL = "ai.openape.nest";
2534
+ function plistPath(userHome) {
2535
+ return join2(userHome, "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
2536
+ }
2537
+ function xmlEscape(s) {
2538
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2539
+ }
2540
+ function buildNestPlist(spec) {
2541
+ const logsDir = join2(spec.userHome, "Library", "Logs");
2542
+ return `<?xml version="1.0" encoding="UTF-8"?>
2543
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2544
+ <plist version="1.0">
2545
+ <dict>
2546
+ <key>Label</key>
2547
+ <string>${xmlEscape(PLIST_LABEL)}</string>
2548
+ <key>ProgramArguments</key>
2549
+ <array>
2550
+ <string>${xmlEscape(spec.nestBin)}</string>
2551
+ </array>
2552
+ <key>WorkingDirectory</key>
2553
+ <string>${xmlEscape(spec.nestHome)}</string>
2554
+ <key>RunAtLoad</key>
2555
+ <true/>
2556
+ <key>KeepAlive</key>
2557
+ <true/>
2558
+ <key>ThrottleInterval</key>
2559
+ <integer>10</integer>
2560
+ <key>EnvironmentVariables</key>
2561
+ <dict>
2562
+ <key>HOME</key><string>${xmlEscape(spec.nestHome)}</string>
2563
+ <key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
2564
+ <key>OPENAPE_NEST_PORT</key><string>${spec.port}</string>
2565
+ <key>OPENAPE_APES_BIN</key><string>${xmlEscape(spec.apesBin)}</string>
2566
+ </dict>
2567
+ <key>StandardOutPath</key>
2568
+ <string>${xmlEscape(logsDir)}/openape-nest.log</string>
2569
+ <key>StandardErrorPath</key>
2570
+ <string>${xmlEscape(logsDir)}/openape-nest.log</string>
2571
+ </dict>
2572
+ </plist>
2573
+ `;
2574
+ }
2575
+ async function installNestSupervisorOnDarwin(spec) {
2576
+ const path2 = plistPath(spec.userHome);
2577
+ mkdirSync(join2(spec.userHome, "Library", "LaunchAgents"), { recursive: true });
2578
+ const desired = buildNestPlist(spec);
2579
+ let existing = "";
2580
+ try {
2581
+ existing = readFileSync3(path2, "utf8");
2582
+ } catch {
2583
+ }
2584
+ if (existing !== desired) {
2585
+ writeFileSync(path2, desired, { mode: 420 });
2586
+ }
2587
+ const uid = userInfo().uid;
2588
+ try {
2589
+ execFileSync5("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
2590
+ } catch {
2591
+ }
2592
+ execFileSync5("/bin/launchctl", ["bootstrap", `gui/${uid}`, path2], { stdio: "inherit" });
2593
+ }
2594
+ async function uninstallNestSupervisorOnDarwin() {
2595
+ const home = userInfo().homedir;
2596
+ const uid = userInfo().uid;
2597
+ const path2 = plistPath(home);
2598
+ try {
2599
+ execFileSync5("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
2600
+ } catch {
2601
+ }
2602
+ if (existsSync4(path2)) unlinkSync(path2);
2603
+ }
2604
+
2605
+ // src/lib/host-platform/darwin-exec.ts
2606
+ import { execFileSync as execFileSync6, spawnSync } from "child_process";
2607
+ import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
2608
+ import { tmpdir } from "os";
2609
+ import { join as join3 } from "path";
2610
+ function resolveApesBinary() {
2611
+ return process.env.OPENAPE_APES_BIN || "apes";
2612
+ }
2613
+ async function runPrivilegedBashOnDarwin(script) {
2614
+ const dir = mkdtempSync(join3(tmpdir(), "apes-privileged-"));
2615
+ const scriptPath = join3(dir, "run.sh");
2616
+ writeFileSync2(scriptPath, script, { mode: 448 });
2617
+ try {
2618
+ if (process.getuid?.() === 0) {
2619
+ execFileSync6("bash", [scriptPath], { stdio: "inherit" });
2620
+ } else {
2621
+ execFileSync6(resolveApesBinary(), ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
2622
+ }
2623
+ } finally {
2624
+ try {
2625
+ rmSync2(dir, { recursive: true, force: true });
2626
+ } catch {
2627
+ }
2628
+ }
2629
+ }
2630
+ async function runAsAgentUserOnDarwin(agentName, argv) {
2631
+ const r = spawnSync(
2632
+ resolveApesBinary(),
2633
+ ["run", "--as", agentName, "--wait", "--", ...argv],
2634
+ { encoding: "utf8" }
2635
+ );
2636
+ return {
2637
+ stdout: r.stdout ?? "",
2638
+ stderr: r.stderr ?? "",
2639
+ exitCode: r.status ?? 1
2640
+ };
2641
+ }
2642
+
2643
+ // src/lib/host-platform/darwin.ts
2644
+ var darwinHostPlatform = {
2645
+ getHostId,
2646
+ getHostname,
2647
+ agentUsername: macOSUsernameForAgent,
2648
+ lookupAgentUser: (agentName) => lookupMacOSUserForAgent(agentName),
2649
+ readAgentUser: (osName) => readMacOSUser(osName),
2650
+ listAgentUserNames: listMacOSUserNames,
2651
+ listOrphanAgentUsers: () => listOrphanedAgentRecords().map((r) => ({ name: r.name, uid: r.uid, homeDir: r.homeDir })),
2652
+ installNestSupervisor: installNestSupervisorOnDarwin,
2653
+ uninstallNestSupervisor: uninstallNestSupervisorOnDarwin,
2654
+ runPrivilegedBash: runPrivilegedBashOnDarwin,
2655
+ runAsAgentUser: runAsAgentUserOnDarwin
2656
+ };
2657
+
2658
+ // src/lib/host-platform/linux-host.ts
2659
+ import { hostname as hostname4 } from "os";
2660
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
2661
+ var FALLBACK_PATHS = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
2662
+ function getLinuxHostId() {
2663
+ for (const path2 of FALLBACK_PATHS) {
2664
+ if (!existsSync5(path2)) continue;
2665
+ try {
2666
+ const v = readFileSync4(path2, "utf-8").trim();
2667
+ if (v) return v;
2668
+ } catch {
2669
+ }
2670
+ }
2671
+ return hostname4();
2672
+ }
2673
+ function getLinuxHostname() {
2674
+ return hostname4();
2675
+ }
2676
+
2677
+ // src/lib/host-platform/linux-user.ts
2678
+ import { execFileSync as execFileSync7 } from "child_process";
2679
+ function getentPasswd(name) {
2680
+ try {
2681
+ return execFileSync7("getent", ["passwd", name], {
2682
+ encoding: "utf-8",
2683
+ stdio: ["ignore", "pipe", "ignore"]
2684
+ }).trim() || null;
2685
+ } catch {
2686
+ return null;
2687
+ }
2688
+ }
2689
+ function parsePasswdLine(line) {
2690
+ const parts = line.split(":");
2691
+ if (parts.length < 7) return null;
2692
+ const [name, , uidStr, , , homeDir, shell] = parts;
2693
+ if (!name || !uidStr) return null;
2694
+ const uid = Number.parseInt(uidStr, 10);
2695
+ return {
2696
+ name,
2697
+ uid: Number.isFinite(uid) ? uid : null,
2698
+ shell: shell?.trim() || null,
2699
+ homeDir: homeDir?.trim() || null
2700
+ };
2701
+ }
2702
+ function readLinuxUser(name) {
2703
+ const line = getentPasswd(name);
2704
+ if (!line) return null;
2705
+ return parsePasswdLine(line);
2706
+ }
2707
+ function listLinuxUserNames() {
2708
+ try {
2709
+ const out = execFileSync7("getent", ["passwd"], {
2710
+ encoding: "utf-8",
2711
+ stdio: ["ignore", "pipe", "ignore"]
2712
+ });
2713
+ const names = /* @__PURE__ */ new Set();
2714
+ for (const line of out.split("\n")) {
2715
+ const name = line.split(":", 1)[0]?.trim();
2716
+ if (name) names.add(name);
2717
+ }
2718
+ return names;
2719
+ } catch {
2720
+ return /* @__PURE__ */ new Set();
2721
+ }
2722
+ }
2723
+
2724
+ // src/lib/host-platform/linux-exec.ts
2725
+ import { execFileSync as execFileSync8, spawnSync as spawnSync2 } from "child_process";
2726
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
2727
+ import { tmpdir as tmpdir2 } from "os";
2728
+ import { join as join4 } from "path";
2729
+ async function runPrivilegedBashOnLinux(script) {
2730
+ const dir = mkdtempSync2(join4(tmpdir2(), "apes-privileged-"));
2731
+ const scriptPath = join4(dir, "run.sh");
2732
+ writeFileSync3(scriptPath, script, { mode: 448 });
2733
+ try {
2734
+ if (process.getuid?.() === 0) {
2735
+ execFileSync8("bash", [scriptPath], { stdio: "inherit" });
2736
+ } else {
2737
+ execFileSync8("sudo", ["-n", "--", "bash", scriptPath], { stdio: "inherit" });
2738
+ }
2739
+ } finally {
2740
+ try {
2741
+ rmSync3(dir, { recursive: true, force: true });
2742
+ } catch {
2743
+ }
2744
+ }
2745
+ }
2746
+ async function runAsAgentUserOnLinux(agentName, argv) {
2747
+ const r = spawnSync2("sudo", ["-n", "-H", "-u", agentName, "--", ...argv], { encoding: "utf8" });
2748
+ return {
2749
+ stdout: r.stdout ?? "",
2750
+ stderr: r.stderr ?? "",
2751
+ exitCode: r.status ?? 1
2752
+ };
2753
+ }
2754
+
2755
+ // src/lib/host-platform/linux-nest.ts
2756
+ import { execFileSync as execFileSync9 } from "child_process";
2757
+ import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2758
+ var UNIT_NAME = "openape-nest.service";
2759
+ var UNIT_PATH = `/etc/systemd/system/${UNIT_NAME}`;
2760
+ function buildNestUnit(spec) {
2761
+ return `[Unit]
2762
+ Description=OpenApe Nest supervisor
2763
+ After=network-online.target
2764
+ Wants=network-online.target
2765
+
2766
+ [Service]
2767
+ Type=simple
2768
+ ExecStart=${spec.nestBin}
2769
+ WorkingDirectory=${spec.nestHome}
2770
+ Restart=always
2771
+ RestartSec=10
2772
+ Environment=HOME=${spec.nestHome}
2773
+ Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
2774
+ Environment=OPENAPE_NEST_PORT=${spec.port}
2775
+ Environment=OPENAPE_APES_BIN=${spec.apesBin}
2776
+ StandardOutput=journal
2777
+ StandardError=journal
2778
+
2779
+ [Install]
2780
+ WantedBy=multi-user.target
2781
+ `;
2782
+ }
2783
+ async function installNestSupervisorOnLinux(spec) {
2784
+ const desired = buildNestUnit(spec);
2785
+ let existing = "";
2786
+ try {
2787
+ existing = readFileSync5(UNIT_PATH, "utf8");
2788
+ } catch {
2789
+ }
2790
+ if (existing !== desired) {
2791
+ writeFileSync4(UNIT_PATH, desired, { mode: 420 });
2792
+ }
2793
+ execFileSync9("systemctl", ["daemon-reload"], { stdio: "inherit" });
2794
+ execFileSync9("systemctl", ["enable", "--now", UNIT_NAME], { stdio: "inherit" });
2795
+ }
2796
+ async function uninstallNestSupervisorOnLinux() {
2797
+ try {
2798
+ execFileSync9("systemctl", ["disable", "--now", UNIT_NAME], { stdio: "inherit" });
2799
+ } catch {
2800
+ }
2801
+ if (existsSync6(UNIT_PATH)) unlinkSync2(UNIT_PATH);
2802
+ try {
2803
+ execFileSync9("systemctl", ["daemon-reload"], { stdio: "inherit" });
2804
+ } catch {
2805
+ }
2806
+ }
2807
+
2808
+ // src/lib/host-platform/linux.ts
2809
+ var linuxHostPlatform = {
2810
+ getHostId: getLinuxHostId,
2811
+ getHostname: getLinuxHostname,
2812
+ // No prefix on Linux — the agent name IS the OS username. In the
2813
+ // container, the OpenApe namespace IS the namespace; no need to
2814
+ // disambiguate with `openape-agent-`. (Linux limits usernames to
2815
+ // 32 chars, so a prefix would also reject longer agent names.)
2816
+ agentUsername: (agentName) => agentName,
2817
+ lookupAgentUser: (agentName) => readLinuxUser(agentName),
2818
+ readAgentUser: (osName) => readLinuxUser(osName),
2819
+ listAgentUserNames: listLinuxUserNames,
2820
+ // No tombstone concept on Linux — userdel + groupdel are clean.
2821
+ listOrphanAgentUsers: () => [],
2822
+ installNestSupervisor: installNestSupervisorOnLinux,
2823
+ uninstallNestSupervisor: uninstallNestSupervisorOnLinux,
2824
+ runPrivilegedBash: runPrivilegedBashOnLinux,
2825
+ runAsAgentUser: runAsAgentUserOnLinux
2826
+ };
2827
+
2828
+ // src/lib/host-platform/index.ts
2829
+ function isDarwin2() {
2830
+ return process2.platform === "darwin";
2831
+ }
2832
+ function isLinux() {
2833
+ return process2.platform === "linux";
2834
+ }
2835
+ var testOverride = null;
2836
+ function getHostPlatform() {
2837
+ if (testOverride) return testOverride;
2838
+ if (isDarwin2()) return darwinHostPlatform;
2839
+ if (isLinux()) return linuxHostPlatform;
2840
+ throw new Error(`unsupported host platform: ${process2.platform}`);
2841
+ }
2842
+
2499
2843
  // src/commands/agents/allow.ts
2500
2844
  var allowAgentCommand = defineCommand22({
2501
2845
  meta: {
@@ -2523,10 +2867,10 @@ var allowAgentCommand = defineCommand22({
2523
2867
  if (!email.includes("@")) {
2524
2868
  throw new CliError(`Invalid email "${email}".`);
2525
2869
  }
2526
- if (!isDarwin()) {
2870
+ if (!isDarwin2()) {
2527
2871
  throw new CliError("`apes agents allow` is currently macOS-only.");
2528
2872
  }
2529
- if (!lookupMacOSUserForAgent(agent)) {
2873
+ if (!getHostPlatform().lookupAgentUser(agent)) {
2530
2874
  throw new CliError(`No macOS user for agent "${agent}" \u2014 has it been spawned?`);
2531
2875
  }
2532
2876
  const apes = whichBinary("apes");
@@ -2558,7 +2902,7 @@ PY
2558
2902
  chmod 600 "$F"
2559
2903
  `;
2560
2904
  consola19.start(`Adding ${email} to ${agent}'s allowlist\u2026`);
2561
- execFileSync4(apes, ["run", "--as", agent, "--wait", "--", "bash", "-c", script], { stdio: "inherit" });
2905
+ execFileSync10(apes, ["run", "--as", agent, "--wait", "--", "bash", "-c", script], { stdio: "inherit" });
2562
2906
  consola19.success(`${agent} will auto-accept future contact requests from ${email} (within ~30s of next bridge connect).`);
2563
2907
  }
2564
2908
  });
@@ -2567,7 +2911,7 @@ function shQuote2(s) {
2567
2911
  }
2568
2912
 
2569
2913
  // src/commands/agents/cleanup-orphans.ts
2570
- import { execFileSync as execFileSync5 } from "child_process";
2914
+ import { execFileSync as execFileSync11 } from "child_process";
2571
2915
  import { defineCommand as defineCommand23 } from "citty";
2572
2916
  import consola20 from "consola";
2573
2917
  var cleanupOrphansCommand = defineCommand23({
@@ -2586,10 +2930,10 @@ var cleanupOrphansCommand = defineCommand23({
2586
2930
  }
2587
2931
  },
2588
2932
  async run({ args }) {
2589
- if (!isDarwin()) {
2933
+ if (!isDarwin2()) {
2590
2934
  throw new CliError(`\`apes agents cleanup-orphans\` is macOS-only. Detected platform: ${process.platform}.`);
2591
2935
  }
2592
- const orphans = listOrphanedAgentRecords();
2936
+ const orphans = getHostPlatform().listOrphanAgentUsers();
2593
2937
  if (orphans.length === 0) {
2594
2938
  consola20.success("No agent tombstones found \u2014 dscl is clean.");
2595
2939
  return;
@@ -2626,7 +2970,7 @@ var cleanupOrphansCommand = defineCommand23({
2626
2970
  let failed = 0;
2627
2971
  for (const o of orphans) {
2628
2972
  try {
2629
- execFileSync5("/usr/sbin/sysadminctl", ["-deleteUser", o.name], {
2973
+ execFileSync11("/usr/sbin/sysadminctl", ["-deleteUser", o.name], {
2630
2974
  stdio: ["ignore", "inherit", "inherit"]
2631
2975
  });
2632
2976
  consola20.success(`Deleted ${o.name}`);
@@ -2644,21 +2988,21 @@ var cleanupOrphansCommand = defineCommand23({
2644
2988
  });
2645
2989
 
2646
2990
  // src/commands/agents/code.ts
2647
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
2991
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
2648
2992
  import { homedir as homedir5 } from "os";
2649
- import { join as join3 } from "path";
2650
- import process2 from "process";
2993
+ import { join as join6 } from "path";
2994
+ import process3 from "process";
2651
2995
  import { defineCommand as defineCommand24 } from "citty";
2652
2996
  import { consola as consola21 } from "consola";
2653
2997
 
2654
2998
  // src/lib/agent-secrets-runtime.ts
2655
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync3, watch } from "fs";
2999
+ import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync6, watch } from "fs";
2656
3000
  import { homedir as homedir4 } from "os";
2657
- import { join as join2 } from "path";
3001
+ import { join as join5 } from "path";
2658
3002
  import { openString } from "@openape/core";
2659
- var CONFIG_DIR2 = join2(homedir4(), ".config", "openape");
2660
- var SECRETS_DIR = join2(CONFIG_DIR2, "secrets.d");
2661
- var X25519_KEY_PATH = join2(CONFIG_DIR2, "agent-x25519.key");
3003
+ var CONFIG_DIR2 = join5(homedir4(), ".config", "openape");
3004
+ var SECRETS_DIR = join5(CONFIG_DIR2, "secrets.d");
3005
+ var X25519_KEY_PATH = join5(CONFIG_DIR2, "agent-x25519.key");
2662
3006
  var X25519_PUBKEY_PATH = `${X25519_KEY_PATH}.pub`;
2663
3007
  function envNameFromFile(file) {
2664
3008
  if (!file.endsWith(".blob")) return null;
@@ -2666,13 +3010,13 @@ function envNameFromFile(file) {
2666
3010
  return /^[A-Z][A-Z0-9_]*$/.test(env) ? env : null;
2667
3011
  }
2668
3012
  function readAgentEncryptionKey(keyPath = X25519_KEY_PATH) {
2669
- if (!existsSync4(keyPath)) return null;
2670
- const k = readFileSync3(keyPath, "utf8").trim();
3013
+ if (!existsSync7(keyPath)) return null;
3014
+ const k = readFileSync6(keyPath, "utf8").trim();
2671
3015
  return k.length > 0 ? k : null;
2672
3016
  }
2673
3017
  function readAgentEncryptionPublicKey(pubPath = X25519_PUBKEY_PATH) {
2674
- if (!existsSync4(pubPath)) return null;
2675
- const k = readFileSync3(pubPath, "utf8").trim();
3018
+ if (!existsSync7(pubPath)) return null;
3019
+ const k = readFileSync6(pubPath, "utf8").trim();
2676
3020
  return k.length > 0 ? k : null;
2677
3021
  }
2678
3022
  function materializeSecrets(opts = {}) {
@@ -2683,12 +3027,12 @@ function materializeSecrets(opts = {}) {
2683
3027
  const applied = [];
2684
3028
  const failed = [];
2685
3029
  const key = readAgentEncryptionKey(opts.keyPath);
2686
- const files = key && existsSync4(dir) ? readdirSync(dir) : [];
3030
+ const files = key && existsSync7(dir) ? readdirSync(dir) : [];
2687
3031
  for (const file of files) {
2688
3032
  const name = envNameFromFile(file);
2689
3033
  if (!name) continue;
2690
3034
  try {
2691
- const box = JSON.parse(readFileSync3(join2(dir, file), "utf8"));
3035
+ const box = JSON.parse(readFileSync6(join5(dir, file), "utf8"));
2692
3036
  env[name] = openString(box, key);
2693
3037
  applied.push(name);
2694
3038
  } catch (e) {
@@ -2715,7 +3059,7 @@ function startSecretsWatcher(opts = {}) {
2715
3059
  appliedNames = new Set(r.applied);
2716
3060
  };
2717
3061
  run();
2718
- if (!existsSync4(dir)) return () => {
3062
+ if (!existsSync7(dir)) return () => {
2719
3063
  };
2720
3064
  let timer = null;
2721
3065
  const watcher = watch(dir, () => {
@@ -2815,9 +3159,9 @@ function decideMerge(paths, policy = SECURE_DEFAULT_POLICY, agentRisk) {
2815
3159
  }
2816
3160
  async function loadMergePolicy(worktreeDir) {
2817
3161
  const { readFile } = await import("fs/promises");
2818
- const { join: join20 } = await import("path");
3162
+ const { join: join21 } = await import("path");
2819
3163
  try {
2820
- const raw = await readFile(join20(worktreeDir, ".openape", "coding.json"), "utf8");
3164
+ const raw = await readFile(join21(worktreeDir, ".openape", "coding.json"), "utf8");
2821
3165
  const parsed = JSON.parse(raw);
2822
3166
  const mp = parsed.mergePolicy;
2823
3167
  if (!mp) return SECURE_DEFAULT_POLICY;
@@ -2877,8 +3221,14 @@ async function runCodingTask(input, deps) {
2877
3221
 
2878
3222
  Worktree: ${worktree}`,
2879
3223
  tools: deps.tools,
2880
- maxSteps: deps.maxSteps
3224
+ maxSteps: deps.maxSteps,
3225
+ streamAggregate: deps.streamAggregate
2881
3226
  });
3227
+ if (process.env.OPENAPE_VERBOSE_TRACE === "1") {
3228
+ for (const t of run.trace) {
3229
+ log(`[trace] step=${t.step} type=${t.type}${t.tool ? ` tool=${t.tool}` : ""} ${t.preview}`);
3230
+ }
3231
+ }
2882
3232
  if (run.status !== "ok") {
2883
3233
  return { branch, worktree, runStatus: "error", changedFiles: [], outcome: "run-failed", reason: `coding loop errored after ${run.stepCount} steps` };
2884
3234
  }
@@ -3054,15 +3404,15 @@ function parseCodeowners(text) {
3054
3404
  }
3055
3405
  async function deriveRiskGlobs(worktreeDir) {
3056
3406
  const { readFile, readdir } = await import("fs/promises");
3057
- const { join: join20 } = await import("path");
3407
+ const { join: join21 } = await import("path");
3058
3408
  const globs = /* @__PURE__ */ new Set();
3059
3409
  try {
3060
- const wfDir = join20(worktreeDir, ".github", "workflows");
3410
+ const wfDir = join21(worktreeDir, ".github", "workflows");
3061
3411
  const files = await readdir(wfDir);
3062
3412
  for (const f of files) {
3063
3413
  if (!/deploy.*\.ya?ml$/i.test(f)) continue;
3064
3414
  try {
3065
- const text = await readFile(join20(wfDir, f), "utf8");
3415
+ const text = await readFile(join21(wfDir, f), "utf8");
3066
3416
  for (const g of extractWorkflowPaths(text)) globs.add(g);
3067
3417
  } catch {
3068
3418
  }
@@ -3071,7 +3421,7 @@ async function deriveRiskGlobs(worktreeDir) {
3071
3421
  }
3072
3422
  for (const loc of [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"]) {
3073
3423
  try {
3074
- const text = await readFile(join20(worktreeDir, loc), "utf8");
3424
+ const text = await readFile(join21(worktreeDir, loc), "utf8");
3075
3425
  for (const g of parseCodeowners(text)) globs.add(g);
3076
3426
  break;
3077
3427
  } catch {
@@ -3102,29 +3452,29 @@ var DEFAULT_PERSONA = [
3102
3452
  ].join(" ");
3103
3453
  function readLitellmConfig(model) {
3104
3454
  const env = {};
3105
- const envPath = join3(homedir5(), "litellm", ".env");
3106
- if (existsSync5(envPath)) {
3107
- for (const raw of readFileSync4(envPath, "utf8").split("\n")) {
3455
+ const envPath = join6(homedir5(), "litellm", ".env");
3456
+ if (existsSync8(envPath)) {
3457
+ for (const raw of readFileSync7(envPath, "utf8").split("\n")) {
3108
3458
  const line = raw.trim();
3109
3459
  const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);
3110
3460
  if (m) env[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
3111
3461
  }
3112
3462
  }
3113
3463
  for (const k of ["LITELLM_API_KEY", "LITELLM_MASTER_KEY", "LITELLM_BASE_URL"]) {
3114
- if (process2.env[k]) env[k] = process2.env[k];
3464
+ if (process3.env[k]) env[k] = process3.env[k];
3115
3465
  }
3116
3466
  const apiBase = (env.LITELLM_BASE_URL || "http://127.0.0.1:4000/v1").replace(/\/$/, "");
3117
3467
  const isLoopback = /^https?:\/\/(?:127\.0\.0\.1|localhost|\[::1\])(?::\d+)?(?:\/|$)/.test(apiBase);
3118
3468
  const apiKey = env.LITELLM_API_KEY || env.LITELLM_MASTER_KEY || (isLoopback ? "sk-loopback-noauth" : "");
3119
3469
  if (!apiKey) throw new CliError2("No LITELLM_API_KEY / LITELLM_MASTER_KEY for non-loopback LITELLM_BASE_URL.");
3120
- return { apiBase, apiKey, model: model || process2.env.APE_CHAT_BRIDGE_MODEL || "claude-haiku-4-5" };
3470
+ return { apiBase, apiKey, model: model || process3.env.APE_CHAT_BRIDGE_MODEL || "claude-haiku-4-5" };
3121
3471
  }
3122
3472
  function readPersona(file) {
3123
- if (file && existsSync5(file)) return readFileSync4(file, "utf8");
3124
- const agentJson = join3(homedir5(), ".openape", "agent", "agent.json");
3125
- if (existsSync5(agentJson)) {
3473
+ if (file && existsSync8(file)) return readFileSync7(file, "utf8");
3474
+ const agentJson = join6(homedir5(), ".openape", "agent", "agent.json");
3475
+ if (existsSync8(agentJson)) {
3126
3476
  try {
3127
- const p = JSON.parse(readFileSync4(agentJson, "utf8"));
3477
+ const p = JSON.parse(readFileSync7(agentJson, "utf8"));
3128
3478
  if (p.systemPrompt?.trim()) return p.systemPrompt;
3129
3479
  } catch {
3130
3480
  }
@@ -3165,6 +3515,7 @@ var codeAgentCommand = defineCommand24({
3165
3515
  const persona = readPersona(args["persona-file"]);
3166
3516
  const maxSteps = Number(args["max-steps"]) > 0 ? Number(args["max-steps"]) : 40;
3167
3517
  const tools = taskTools(CODING_TOOLS);
3518
+ const streamAggregate = process3.env.OPENAPE_STREAM_AGGREGATE === "1";
3168
3519
  const deps = {
3169
3520
  runtimeConfig: config,
3170
3521
  tools,
@@ -3173,7 +3524,8 @@ var codeAgentCommand = defineCommand24({
3173
3524
  resolvePolicy: (worktree) => resolveMergePolicy(worktree),
3174
3525
  reviewer: createLlmReviewer(config),
3175
3526
  riskAssessor: createLlmRiskAssessor(config),
3176
- log: (l) => consola21.info(l)
3527
+ log: (l) => consola21.info(l),
3528
+ streamAggregate
3177
3529
  };
3178
3530
  const refs = [];
3179
3531
  if (args["poll-label"]) {
@@ -3207,30 +3559,30 @@ ${result.reason}`);
3207
3559
  });
3208
3560
 
3209
3561
  // src/commands/agents/destroy.ts
3210
- import { execFileSync as execFileSync6 } from "child_process";
3211
- import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
3212
- import { tmpdir, userInfo } from "os";
3213
- import { join as join5 } from "path";
3562
+ import { execFileSync as execFileSync12 } from "child_process";
3563
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
3564
+ import { tmpdir as tmpdir3, userInfo as userInfo2 } from "os";
3565
+ import { join as join8 } from "path";
3214
3566
  import { defineCommand as defineCommand25 } from "citty";
3215
3567
  import consola22 from "consola";
3216
3568
 
3217
3569
  // src/lib/nest-registry.ts
3218
- import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "fs";
3570
+ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
3219
3571
  import { homedir as homedir6 } from "os";
3220
- import { join as join4 } from "path";
3572
+ import { join as join7 } from "path";
3221
3573
  function resolveRegistryPath() {
3222
- if (existsSync6("/var/openape/nest/agents.json")) return "/var/openape/nest/agents.json";
3223
- if (existsSync6("/var/openape/nest")) return "/var/openape/nest/agents.json";
3224
- return join4(homedir6(), ".openape", "nest", "agents.json");
3574
+ if (existsSync9("/var/openape/nest/agents.json")) return "/var/openape/nest/agents.json";
3575
+ if (existsSync9("/var/openape/nest")) return "/var/openape/nest/agents.json";
3576
+ return join7(homedir6(), ".openape", "nest", "agents.json");
3225
3577
  }
3226
3578
  function emptyRegistry() {
3227
3579
  return { version: 1, agents: [] };
3228
3580
  }
3229
3581
  function readNestRegistry() {
3230
3582
  const path2 = resolveRegistryPath();
3231
- if (!existsSync6(path2)) return emptyRegistry();
3583
+ if (!existsSync9(path2)) return emptyRegistry();
3232
3584
  try {
3233
- const parsed = JSON.parse(readFileSync5(path2, "utf8"));
3585
+ const parsed = JSON.parse(readFileSync8(path2, "utf8"));
3234
3586
  if (parsed?.version !== 1 || !Array.isArray(parsed.agents)) return emptyRegistry();
3235
3587
  return parsed;
3236
3588
  } catch {
@@ -3241,10 +3593,10 @@ function writeNestRegistry(reg) {
3241
3593
  const path2 = resolveRegistryPath();
3242
3594
  const dir = path2.replace(/\/agents\.json$/, "");
3243
3595
  try {
3244
- mkdirSync(dir, { recursive: true });
3596
+ mkdirSync2(dir, { recursive: true });
3245
3597
  } catch {
3246
3598
  }
3247
- writeFileSync(path2, `${JSON.stringify(reg, null, 2)}
3599
+ writeFileSync5(path2, `${JSON.stringify(reg, null, 2)}
3248
3600
  `, { mode: 432 });
3249
3601
  }
3250
3602
  function upsertNestAgent(entry) {
@@ -3357,8 +3709,9 @@ var destroyAgentCommand = defineCommand25({
3357
3709
  if (process.geteuid?.() !== 0) {
3358
3710
  throw new CliError("--root-stage was passed but this process is not running as root. Refusing to continue.");
3359
3711
  }
3360
- const resolved = lookupMacOSUserForAgent(name);
3361
- const macOSUsername = resolved?.name ?? macOSUsernameForAgent(name);
3712
+ const platform = getHostPlatform();
3713
+ const resolved = platform.lookupAgentUser(name);
3714
+ const macOSUsername = resolved?.name ?? platform.agentUsername(name);
3362
3715
  const homeDir = resolved?.homeDir ?? `/var/openape/homes/${macOSUsername}`;
3363
3716
  consola22.start(`Running teardown for ${name} (Phase-G, root-stage)\u2026`);
3364
3717
  runPhaseGTeardownInProcess({ name, homeDir, macOSUsername });
@@ -3375,7 +3728,7 @@ var destroyAgentCommand = defineCommand25({
3375
3728
  const owned = await apiFetch("/api/my-agents", { idp });
3376
3729
  const idpAgent = owned.find((u) => u.name === name);
3377
3730
  const idpExists = idpAgent !== void 0;
3378
- const osUser = isDarwin() ? lookupMacOSUserForAgent(name) : null;
3731
+ const osUser = isDarwin2() ? getHostPlatform().lookupAgentUser(name) : null;
3379
3732
  const osUserExists = !args["keep-os-user"] && osUser !== null;
3380
3733
  if (!idpExists && !osUserExists) {
3381
3734
  consola22.info(`Nothing to destroy: no IdP agent and no OS user for "${name}".`);
@@ -3415,7 +3768,7 @@ ${consequences.join("\n")}`);
3415
3768
  consola22.info("No IdP agent to remove (skipped).");
3416
3769
  }
3417
3770
  if (osUserExists) {
3418
- const macOSUsername = osUser?.name ?? macOSUsernameForAgent(name);
3771
+ const macOSUsername = osUser?.name ?? getHostPlatform().agentUsername(name);
3419
3772
  const fallbackHome = macOSUsername.startsWith("openape-agent-") ? `/var/openape/homes/${macOSUsername}` : `/Users/${macOSUsername}`;
3420
3773
  const homeDir = osUser?.homeDir ?? fallbackHome;
3421
3774
  const isPhaseG = homeDir.startsWith("/var/openape/homes/");
@@ -3425,7 +3778,7 @@ ${consequences.join("\n")}`);
3425
3778
  runPhaseGTeardownInProcess({ name, homeDir, macOSUsername });
3426
3779
  } else {
3427
3780
  consola22.start("Running teardown (Phase G \u2014 no admin password needed)\u2026");
3428
- execFileSync6("apes", [
3781
+ execFileSync12("apes", [
3429
3782
  "run",
3430
3783
  "--as",
3431
3784
  "root",
@@ -3445,7 +3798,7 @@ ${consequences.join("\n")}`);
3445
3798
  if (!sudo) {
3446
3799
  throw new CliError("`sudo` not found on PATH; required for OS teardown.");
3447
3800
  }
3448
- const adminUser = userInfo().username;
3801
+ const adminUser = userInfo2().username;
3449
3802
  let adminPassword;
3450
3803
  try {
3451
3804
  adminPassword = await collectAdminPassword({ adminUser });
@@ -3459,24 +3812,24 @@ ${consequences.join("\n")}`);
3459
3812
  }
3460
3813
  }
3461
3814
  if (adminPassword) {
3462
- const scratch = mkdtempSync(join5(tmpdir(), `apes-destroy-${name}-`));
3463
- const scriptPath = join5(scratch, "teardown.sh");
3815
+ const scratch = mkdtempSync3(join8(tmpdir3(), `apes-destroy-${name}-`));
3816
+ const scriptPath = join8(scratch, "teardown.sh");
3464
3817
  try {
3465
3818
  const script = buildDestroyTeardownScript({ name, homeDir, adminUser });
3466
- writeFileSync2(scriptPath, script, { mode: 448 });
3819
+ writeFileSync6(scriptPath, script, { mode: 448 });
3467
3820
  consola22.start("Running teardown via sudo\u2026");
3468
- execFileSync6(sudo, ["-S", "--prompt=", "--", "bash", scriptPath], {
3821
+ execFileSync12(sudo, ["-S", "--prompt=", "--", "bash", scriptPath], {
3469
3822
  input: `${adminPassword}
3470
3823
  ${adminPassword}
3471
3824
  `,
3472
3825
  stdio: ["pipe", "inherit", "inherit"]
3473
3826
  });
3474
3827
  } finally {
3475
- rmSync2(scratch, { recursive: true, force: true });
3828
+ rmSync4(scratch, { recursive: true, force: true });
3476
3829
  }
3477
3830
  }
3478
3831
  }
3479
- } else if (!args["keep-os-user"] && isDarwin()) {
3832
+ } else if (!args["keep-os-user"] && isDarwin2()) {
3480
3833
  consola22.info("No macOS user to remove (skipped).");
3481
3834
  }
3482
3835
  try {
@@ -3526,11 +3879,12 @@ var listAgentsCommand = defineCommand26({
3526
3879
  }
3527
3880
  const all = await apiFetch("/api/my-agents", { idp });
3528
3881
  const filtered = args["include-inactive"] ? all : all.filter((u) => u.isActive !== false);
3529
- const osUsers = isDarwin() ? listMacOSUserNames() : /* @__PURE__ */ new Set();
3882
+ const platform = getHostPlatform();
3883
+ const osUsers = isDarwin2() ? platform.listAgentUserNames() : /* @__PURE__ */ new Set();
3530
3884
  const osStateOf = (agentName) => {
3531
- const u = lookupMacOSUserForAgent(agentName);
3885
+ const u = platform.lookupAgentUser(agentName);
3532
3886
  if (u) return { osUser: true, home: u.homeDir };
3533
- if (osUsers.has(macOSUsernameForAgent(agentName)) || osUsers.has(agentName)) {
3887
+ if (osUsers.has(platform.agentUsername(agentName)) || osUsers.has(agentName)) {
3534
3888
  return { osUser: true, home: null };
3535
3889
  }
3536
3890
  return { osUser: false, home: null };
@@ -3562,14 +3916,14 @@ var listAgentsCommand = defineCommand26({
3562
3916
  for (const r of rows) {
3563
3917
  const active = r.isActive ? "\u2713" : "\u2717";
3564
3918
  const os = r.osUser ? "\u2713" : "\u2717";
3565
- const homeCol = r.home ?? (isDarwin() ? "(missing)" : "(non-darwin)");
3919
+ const homeCol = r.home ?? (isDarwin2() ? "(missing)" : "(non-darwin)");
3566
3920
  console.log(`${r.name.padEnd(nameW)} ${r.email.padEnd(emailW)} ${active.padEnd(6)} ${os.padEnd(7)} ${homeCol}`);
3567
3921
  }
3568
3922
  }
3569
3923
  });
3570
3924
 
3571
3925
  // src/commands/agents/register.ts
3572
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
3926
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
3573
3927
  import { defineCommand as defineCommand27 } from "citty";
3574
3928
  import consola24 from "consola";
3575
3929
  var registerAgentCommand = defineCommand27({
@@ -3617,10 +3971,10 @@ var registerAgentCommand = defineCommand27({
3617
3971
  throw new CliError("Pass either --public-key or --public-key-file, not both.");
3618
3972
  }
3619
3973
  if (!publicKey && keyFile) {
3620
- if (!existsSync7(keyFile)) {
3974
+ if (!existsSync10(keyFile)) {
3621
3975
  throw new CliError(`Public-key file not found: ${keyFile}`);
3622
3976
  }
3623
- publicKey = readFileSync6(keyFile, "utf-8").trim();
3977
+ publicKey = readFileSync9(keyFile, "utf-8").trim();
3624
3978
  }
3625
3979
  if (!publicKey) {
3626
3980
  throw new CliError('Provide --public-key "<ssh-ed25519 line>" or --public-key-file <path>.');
@@ -3655,18 +4009,18 @@ var registerAgentCommand = defineCommand27({
3655
4009
  });
3656
4010
 
3657
4011
  // src/commands/agents/run.ts
3658
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
4012
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
3659
4013
  import { homedir as homedir7 } from "os";
3660
- import { join as join6 } from "path";
4014
+ import { join as join9 } from "path";
3661
4015
  import { defineCommand as defineCommand28 } from "citty";
3662
4016
  import consola25 from "consola";
3663
- var AUTH_PATH = join6(homedir7(), ".config", "apes", "auth.json");
3664
- var TASK_CACHE_DIR = join6(homedir7(), ".openape", "agent", "tasks");
4017
+ var AUTH_PATH = join9(homedir7(), ".config", "apes", "auth.json");
4018
+ var TASK_CACHE_DIR = join9(homedir7(), ".openape", "agent", "tasks");
3665
4019
  function readAuth() {
3666
- if (!existsSync8(AUTH_PATH)) {
4020
+ if (!existsSync11(AUTH_PATH)) {
3667
4021
  throw new CliError(`No agent auth found at ${AUTH_PATH}. Run \`apes agents spawn <name>\` first.`);
3668
4022
  }
3669
- const parsed = JSON.parse(readFileSync7(AUTH_PATH, "utf8"));
4023
+ const parsed = JSON.parse(readFileSync10(AUTH_PATH, "utf8"));
3670
4024
  if (!parsed.access_token) throw new CliError("auth.json missing access_token");
3671
4025
  return parsed;
3672
4026
  }
@@ -3702,26 +4056,26 @@ ${msg}`.slice(0, 9e3);
3702
4056
  }
3703
4057
  }
3704
4058
  function readTaskSpec(taskId) {
3705
- const path2 = join6(TASK_CACHE_DIR, `${taskId}.json`);
3706
- if (!existsSync8(path2)) {
4059
+ const path2 = join9(TASK_CACHE_DIR, `${taskId}.json`);
4060
+ if (!existsSync11(path2)) {
3707
4061
  throw new CliError(`No cached task spec at ${path2}. Run \`apes agents sync\` first to pull the task list from troop.`);
3708
4062
  }
3709
- return JSON.parse(readFileSync7(path2, "utf8"));
4063
+ return JSON.parse(readFileSync10(path2, "utf8"));
3710
4064
  }
3711
- var AGENT_CONFIG_PATH = join6(homedir7(), ".openape", "agent", "agent.json");
4065
+ var AGENT_CONFIG_PATH = join9(homedir7(), ".openape", "agent", "agent.json");
3712
4066
  function readAgentConfig() {
3713
- if (!existsSync8(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
4067
+ if (!existsSync11(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
3714
4068
  try {
3715
- return JSON.parse(readFileSync7(AGENT_CONFIG_PATH, "utf8"));
4069
+ return JSON.parse(readFileSync10(AGENT_CONFIG_PATH, "utf8"));
3716
4070
  } catch {
3717
4071
  return { systemPrompt: "" };
3718
4072
  }
3719
4073
  }
3720
4074
  function readLitellmConfig2(model) {
3721
- const envPath = join6(homedir7(), "litellm", ".env");
4075
+ const envPath = join9(homedir7(), "litellm", ".env");
3722
4076
  const env = {};
3723
- if (existsSync8(envPath)) {
3724
- for (const line of readFileSync7(envPath, "utf8").split(/\r?\n/)) {
4077
+ if (existsSync11(envPath)) {
4078
+ for (const line of readFileSync10(envPath, "utf8").split(/\r?\n/)) {
3725
4079
  const m = line.match(/^([A-Z_]+)=(.*)$/);
3726
4080
  if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
3727
4081
  }
@@ -3827,17 +4181,17 @@ var runAgentCommand = defineCommand28({
3827
4181
  });
3828
4182
 
3829
4183
  // src/commands/agents/serve.ts
3830
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
4184
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
3831
4185
  import { homedir as homedir8 } from "os";
3832
- import { join as join7 } from "path";
4186
+ import { join as join10 } from "path";
3833
4187
  import { createInterface } from "readline";
3834
4188
  import { defineCommand as defineCommand29 } from "citty";
3835
- var AUTH_PATH2 = join7(homedir8(), ".config", "apes", "auth.json");
4189
+ var AUTH_PATH2 = join10(homedir8(), ".config", "apes", "auth.json");
3836
4190
  function readLitellmConfig3(model) {
3837
- const envPath = join7(homedir8(), "litellm", ".env");
4191
+ const envPath = join10(homedir8(), "litellm", ".env");
3838
4192
  const env = {};
3839
- if (existsSync9(envPath)) {
3840
- for (const line of readFileSync8(envPath, "utf8").split(/\r?\n/)) {
4193
+ if (existsSync12(envPath)) {
4194
+ for (const line of readFileSync11(envPath, "utf8").split(/\r?\n/)) {
3841
4195
  const m = line.match(/^([A-Z_]+)=(.*)$/);
3842
4196
  if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
3843
4197
  }
@@ -3869,9 +4223,9 @@ var serveAgentCommand = defineCommand29({
3869
4223
  if (!args.rpc) {
3870
4224
  throw new CliError("apes agents serve currently only supports --rpc mode");
3871
4225
  }
3872
- if (existsSync9(AUTH_PATH2)) {
4226
+ if (existsSync12(AUTH_PATH2)) {
3873
4227
  try {
3874
- JSON.parse(readFileSync8(AUTH_PATH2, "utf8"));
4228
+ JSON.parse(readFileSync11(AUTH_PATH2, "utf8"));
3875
4229
  } catch {
3876
4230
  }
3877
4231
  }
@@ -3951,10 +4305,6 @@ async function handleInbound(msg, sessions) {
3951
4305
  }
3952
4306
 
3953
4307
  // src/commands/agents/spawn.ts
3954
- import { execFileSync as execFileSync8 } from "child_process";
3955
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
3956
- import { tmpdir as tmpdir2 } from "os";
3957
- import { join as join9 } from "path";
3958
4308
  import { defineCommand as defineCommand30 } from "citty";
3959
4309
  import consola26 from "consola";
3960
4310
 
@@ -4012,7 +4362,7 @@ ${envBlock} <key>StartInterval</key>
4012
4362
 
4013
4363
  // src/lib/keygen.ts
4014
4364
  import { Buffer as Buffer4 } from "buffer";
4015
- import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
4365
+ import { existsSync as existsSync13, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
4016
4366
  import { generateKeyPairSync } from "crypto";
4017
4367
  import { homedir as homedir9 } from "os";
4018
4368
  import { dirname, resolve as resolve2 } from "path";
@@ -4031,10 +4381,10 @@ function buildSshEd25519Line(rawPub) {
4031
4381
  }
4032
4382
  function readPublicKey(keyPath) {
4033
4383
  const pubPath = `${keyPath}.pub`;
4034
- if (existsSync10(pubPath)) {
4035
- return readFileSync9(pubPath, "utf-8").trim();
4384
+ if (existsSync13(pubPath)) {
4385
+ return readFileSync12(pubPath, "utf-8").trim();
4036
4386
  }
4037
- const keyContent = readFileSync9(keyPath, "utf-8");
4387
+ const keyContent = readFileSync12(keyPath, "utf-8");
4038
4388
  const privateKey = loadEd25519PrivateKey(keyContent);
4039
4389
  const jwk = privateKey.export({ format: "jwk" });
4040
4390
  const pubBytes = Buffer4.from(jwk.x, "base64url");
@@ -4043,16 +4393,16 @@ function readPublicKey(keyPath) {
4043
4393
  function generateAndSaveKey(keyPath) {
4044
4394
  const resolved = resolveKeyPath(keyPath);
4045
4395
  const dir = dirname(resolved);
4046
- if (!existsSync10(dir)) {
4047
- mkdirSync2(dir, { recursive: true });
4396
+ if (!existsSync13(dir)) {
4397
+ mkdirSync3(dir, { recursive: true });
4048
4398
  }
4049
4399
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
4050
4400
  const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
4051
- writeFileSync3(resolved, privatePem, { mode: 384 });
4401
+ writeFileSync7(resolved, privatePem, { mode: 384 });
4052
4402
  const jwk = publicKey.export({ format: "jwk" });
4053
4403
  const pubBytes = Buffer4.from(jwk.x, "base64url");
4054
4404
  const pubKeyStr = buildSshEd25519Line(pubBytes);
4055
- writeFileSync3(`${resolved}.pub`, `${pubKeyStr}
4405
+ writeFileSync7(`${resolved}.pub`, `${pubKeyStr}
4056
4406
  `, { mode: 420 });
4057
4407
  return pubKeyStr;
4058
4408
  }
@@ -4071,15 +4421,15 @@ function generateKeyPairInMemory() {
4071
4421
  }
4072
4422
 
4073
4423
  // src/lib/llm-bridge.ts
4074
- import { execFileSync as execFileSync7 } from "child_process";
4075
- import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
4424
+ import { execFileSync as execFileSync13 } from "child_process";
4425
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
4076
4426
  import { homedir as homedir10 } from "os";
4077
- import { dirname as dirname2, join as join8 } from "path";
4427
+ import { dirname as dirname2, join as join11 } from "path";
4078
4428
  var PLIST_LABEL_PREFIX = "eco.hofmann.apes.bridge";
4079
- function readLitellmEnv(envPath = join8(homedir10(), "litellm", ".env")) {
4080
- if (!existsSync11(envPath)) return null;
4429
+ function readLitellmEnv(envPath = join11(homedir10(), "litellm", ".env")) {
4430
+ if (!existsSync14(envPath)) return null;
4081
4431
  try {
4082
- const text = readFileSync10(envPath, "utf8");
4432
+ const text = readFileSync13(envPath, "utf8");
4083
4433
  const out = {};
4084
4434
  for (const line of text.split("\n")) {
4085
4435
  const trimmed = line.trim();
@@ -4115,7 +4465,7 @@ function captureHostBinDirs() {
4115
4465
  for (const bin of ["node", "ape-agent", "apes"]) {
4116
4466
  let resolved;
4117
4467
  try {
4118
- resolved = execFileSync7("/usr/bin/which", [bin], { encoding: "utf8" }).trim();
4468
+ resolved = execFileSync13("/usr/bin/which", [bin], { encoding: "utf8" }).trim();
4119
4469
  } catch {
4120
4470
  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)";
4121
4471
  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.`);
@@ -4210,7 +4560,7 @@ function buildBridgePlist(agentName, homeDir, ownerEmail, hostBinDirs) {
4210
4560
  // src/commands/agents/spawn.ts
4211
4561
  function readMacOSUidOrNull(name) {
4212
4562
  try {
4213
- const u = readMacOSUser(name);
4563
+ const u = getHostPlatform().readAgentUser(name);
4214
4564
  return u?.uid ?? null;
4215
4565
  } catch {
4216
4566
  return null;
@@ -4267,7 +4617,7 @@ var spawnAgentCommand = defineCommand30({
4267
4617
  `Invalid agent name "${name}". Must match /^[a-z][a-z0-9-]{0,23}$/ \u2014 lowercase letters, digits and hyphens, 1\u201324 chars, must start with a letter.`
4268
4618
  );
4269
4619
  }
4270
- if (!isDarwin()) {
4620
+ if (!isDarwin2()) {
4271
4621
  throw new CliError(
4272
4622
  `\`apes agents spawn\` is currently macOS-only. Detected platform: ${process.platform}. Linux support is a follow-up; for now, use \`apes agents register\` plus a manually provisioned user.`
4273
4623
  );
@@ -4298,14 +4648,13 @@ var spawnAgentCommand = defineCommand30({
4298
4648
  and try again.`
4299
4649
  );
4300
4650
  }
4301
- const macOSUsername = macOSUsernameForAgent(name);
4302
- const existing = readMacOSUser(macOSUsername) ?? readMacOSUser(name);
4651
+ const platform = getHostPlatform();
4652
+ const macOSUsername = platform.agentUsername(name);
4653
+ const existing = platform.readAgentUser(macOSUsername) ?? platform.readAgentUser(name);
4303
4654
  if (existing) {
4304
4655
  throw new CliError(`macOS user "${existing.name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
4305
4656
  }
4306
4657
  const homeDir = `/var/openape/homes/${macOSUsername}`;
4307
- const scratch = mkdtempSync2(join9(tmpdir2(), `apes-spawn-${name}-`));
4308
- const scriptPath = join9(scratch, "setup.sh");
4309
4658
  try {
4310
4659
  consola26.start(`Generating keypair for ${name}\u2026`);
4311
4660
  const { privatePem, publicSshLine, x25519PrivateKey, x25519PublicKey } = generateKeyPairInMemory();
@@ -4379,16 +4728,11 @@ and try again.`
4379
4728
  bridge,
4380
4729
  troop
4381
4730
  });
4382
- writeFileSync4(scriptPath, script, { mode: 448 });
4383
- const alreadyRoot = process.getuid?.() === 0;
4384
- if (alreadyRoot) {
4385
- consola26.start("Running privileged setup directly (already root)\u2026");
4386
- execFileSync8("bash", [scriptPath], { stdio: "inherit" });
4387
- } else {
4388
- consola26.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
4731
+ consola26.start("Running privileged setup\u2026");
4732
+ if (process.getuid?.() !== 0) {
4389
4733
  consola26.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
4390
- execFileSync8(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
4391
4734
  }
4735
+ await platform.runPrivilegedBash(script);
4392
4736
  try {
4393
4737
  const uid = readMacOSUidOrNull(macOSUsername) ?? readMacOSUidOrNull(name);
4394
4738
  upsertNestAgent({
@@ -4416,7 +4760,6 @@ and try again.`
4416
4760
  console.log("Run as the agent with:");
4417
4761
  console.log(` apes run --as ${name} -- claude --session-name ${name} --dangerously-skip-permissions`);
4418
4762
  } finally {
4419
- rmSync3(scratch, { recursive: true, force: true });
4420
4763
  }
4421
4764
  }
4422
4765
  });
@@ -4444,46 +4787,20 @@ async function resolveClaudeToken(opts) {
4444
4787
  }
4445
4788
 
4446
4789
  // src/commands/agents/sync.ts
4447
- import { chownSync, existsSync as existsSync12, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync11, rmSync as rmSync4, statSync, writeFileSync as writeFileSync5 } from "fs";
4790
+ import { chownSync, existsSync as existsSync15, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync14, rmSync as rmSync5, statSync, writeFileSync as writeFileSync8 } from "fs";
4448
4791
  import { homedir as homedir11 } from "os";
4449
- import { join as join10 } from "path";
4792
+ import { join as join12 } from "path";
4450
4793
  import { defineCommand as defineCommand31 } from "citty";
4451
4794
  import consola27 from "consola";
4452
-
4453
- // src/lib/macos-host.ts
4454
- import { execFileSync as execFileSync9 } from "child_process";
4455
- import { hostname as hostname3 } from "os";
4456
- function getHostId() {
4457
- try {
4458
- const output = execFileSync9(
4459
- "/usr/sbin/ioreg",
4460
- ["-d2", "-c", "IOPlatformExpertDevice"],
4461
- { encoding: "utf8", timeout: 2e3 }
4462
- );
4463
- const match = output.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
4464
- return match ? match[1].trim().toLowerCase() : "";
4465
- } catch {
4466
- return "";
4467
- }
4468
- }
4469
- function getHostname() {
4470
- try {
4471
- return hostname3();
4472
- } catch {
4473
- return "";
4474
- }
4475
- }
4476
-
4477
- // src/commands/agents/sync.ts
4478
- var AUTH_PATH3 = join10(homedir11(), ".config", "apes", "auth.json");
4479
- var TASK_CACHE_DIR2 = join10(homedir11(), ".openape", "agent", "tasks");
4795
+ var AUTH_PATH3 = join12(homedir11(), ".config", "apes", "auth.json");
4796
+ var TASK_CACHE_DIR2 = join12(homedir11(), ".openape", "agent", "tasks");
4480
4797
  function readAuthJson() {
4481
- if (!existsSync12(AUTH_PATH3)) {
4798
+ if (!existsSync15(AUTH_PATH3)) {
4482
4799
  throw new CliError(
4483
4800
  `No agent auth found at ${AUTH_PATH3}. Run \`apes agents spawn <name>\` to provision an agent first.`
4484
4801
  );
4485
4802
  }
4486
- const raw = readFileSync11(AUTH_PATH3, "utf8");
4803
+ const raw = readFileSync14(AUTH_PATH3, "utf8");
4487
4804
  let parsed;
4488
4805
  try {
4489
4806
  parsed = JSON.parse(raw);
@@ -4523,8 +4840,9 @@ var syncAgentCommand = defineCommand31({
4523
4840
  const agentName = agentNameFromEmail(auth.email);
4524
4841
  const troopUrl = resolveTroopUrl(args["troop-url"]);
4525
4842
  const client = new TroopClient(troopUrl, auth.access_token);
4526
- const hostId = getHostId();
4527
- const host = getHostname();
4843
+ const platform = getHostPlatform();
4844
+ const hostId = platform.getHostId();
4845
+ const host = platform.getHostname();
4528
4846
  if (!hostId) {
4529
4847
  throw new CliError("Could not read IOPlatformUUID \u2014 is this macOS? Troop sync only works on macOS for v1.");
4530
4848
  }
@@ -4563,46 +4881,46 @@ var syncAgentCommand = defineCommand31({
4563
4881
  }
4564
4882
  }
4565
4883
  }
4566
- const agentDir = join10(homedir11(), ".openape", "agent");
4567
- mkdirSync3(agentDir, { recursive: true });
4568
- chownToAgent(join10(homedir11(), ".openape"));
4884
+ const agentDir = join12(homedir11(), ".openape", "agent");
4885
+ mkdirSync4(agentDir, { recursive: true });
4886
+ chownToAgent(join12(homedir11(), ".openape"));
4569
4887
  chownToAgent(agentDir);
4570
- const agentJsonPath = join10(agentDir, "agent.json");
4571
- writeFileSync5(
4888
+ const agentJsonPath = join12(agentDir, "agent.json");
4889
+ writeFileSync8(
4572
4890
  agentJsonPath,
4573
4891
  `${JSON.stringify({ systemPrompt, tools }, null, 2)}
4574
4892
  `,
4575
4893
  { mode: 384 }
4576
4894
  );
4577
4895
  chownToAgent(agentJsonPath);
4578
- mkdirSync3(TASK_CACHE_DIR2, { recursive: true });
4896
+ mkdirSync4(TASK_CACHE_DIR2, { recursive: true });
4579
4897
  chownToAgent(TASK_CACHE_DIR2);
4580
4898
  for (const task of tasks) {
4581
- const path2 = join10(TASK_CACHE_DIR2, `${task.taskId}.json`);
4582
- writeFileSync5(path2, `${JSON.stringify(task, null, 2)}
4899
+ const path2 = join12(TASK_CACHE_DIR2, `${task.taskId}.json`);
4900
+ writeFileSync8(path2, `${JSON.stringify(task, null, 2)}
4583
4901
  `, { mode: 384 });
4584
4902
  chownToAgent(path2);
4585
4903
  }
4586
- const skillsDir = join10(agentDir, "skills");
4587
- mkdirSync3(skillsDir, { recursive: true });
4904
+ const skillsDir = join12(agentDir, "skills");
4905
+ mkdirSync4(skillsDir, { recursive: true });
4588
4906
  chownToAgent(skillsDir);
4589
4907
  const incomingNames = new Set(skills.map((s) => s.name));
4590
4908
  try {
4591
4909
  for (const entry of readdirSync2(skillsDir)) {
4592
4910
  if (incomingNames.has(entry)) continue;
4593
4911
  try {
4594
- rmSync4(join10(skillsDir, entry), { recursive: true, force: true });
4912
+ rmSync5(join12(skillsDir, entry), { recursive: true, force: true });
4595
4913
  } catch {
4596
4914
  }
4597
4915
  }
4598
4916
  } catch {
4599
4917
  }
4600
4918
  for (const skill of skills) {
4601
- const skillDir = join10(skillsDir, skill.name);
4602
- mkdirSync3(skillDir, { recursive: true });
4919
+ const skillDir = join12(skillsDir, skill.name);
4920
+ mkdirSync4(skillDir, { recursive: true });
4603
4921
  chownToAgent(skillDir);
4604
- const skillPath = join10(skillDir, "SKILL.md");
4605
- writeFileSync5(skillPath, skill.body.endsWith("\n") ? skill.body : `${skill.body}
4922
+ const skillPath = join12(skillDir, "SKILL.md");
4923
+ writeFileSync8(skillPath, skill.body.endsWith("\n") ? skill.body : `${skill.body}
4606
4924
  `, { mode: 384 });
4607
4925
  chownToAgent(skillPath);
4608
4926
  }
@@ -4634,21 +4952,21 @@ var agentsCommand = defineCommand32({
4634
4952
  import { defineCommand as defineCommand40 } from "citty";
4635
4953
 
4636
4954
  // src/commands/nest/authorize.ts
4637
- import { execFileSync as execFileSync10 } from "child_process";
4638
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
4639
- import { join as join12 } from "path";
4955
+ import { execFileSync as execFileSync14 } from "child_process";
4956
+ import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
4957
+ import { join as join14 } from "path";
4640
4958
  import { defineCommand as defineCommand34 } from "citty";
4641
4959
  import consola29 from "consola";
4642
4960
 
4643
4961
  // src/commands/nest/enroll.ts
4644
- import { hostname as hostname4, homedir as homedir12 } from "os";
4645
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, chmodSync } from "fs";
4646
- import { join as join11 } from "path";
4962
+ import { hostname as hostname5, homedir as homedir12 } from "os";
4963
+ import { existsSync as existsSync16, mkdirSync as mkdirSync5, writeFileSync as writeFileSync9, chmodSync } from "fs";
4964
+ import { join as join13 } from "path";
4647
4965
  import { defineCommand as defineCommand33 } from "citty";
4648
4966
  import consola28 from "consola";
4649
- var NEST_DATA_DIR = join11(homedir12(), ".openape", "nest");
4967
+ var NEST_DATA_DIR = join13(homedir12(), ".openape", "nest");
4650
4968
  function nestAgentName() {
4651
- const raw = hostname4().toLowerCase();
4969
+ const raw = hostname5().toLowerCase();
4652
4970
  const head = raw.split(".")[0] ?? raw;
4653
4971
  const safe = head.replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
4654
4972
  const trimmed = safe.slice(0, 16);
@@ -4679,19 +4997,19 @@ var enrollNestCommand = defineCommand33({
4679
4997
  throw new CliError("Run `apes login <email>` first \u2014 nest enroll attaches the new identity to your owner account.");
4680
4998
  }
4681
4999
  const name = args.name || nestAgentName();
4682
- const authPath = join11(NEST_DATA_DIR, ".config", "apes", "auth.json");
4683
- if (existsSync13(authPath) && !args.force) {
5000
+ const authPath = join13(NEST_DATA_DIR, ".config", "apes", "auth.json");
5001
+ if (existsSync16(authPath) && !args.force) {
4684
5002
  throw new CliError(`Nest already enrolled at ${authPath}. Pass --force to re-enroll.`);
4685
5003
  }
4686
- const sshDir = join11(NEST_DATA_DIR, ".ssh");
4687
- const configDir = join11(NEST_DATA_DIR, ".config", "apes");
4688
- mkdirSync4(sshDir, { recursive: true });
4689
- mkdirSync4(configDir, { recursive: true });
5004
+ const sshDir = join13(NEST_DATA_DIR, ".ssh");
5005
+ const configDir = join13(NEST_DATA_DIR, ".config", "apes");
5006
+ mkdirSync5(sshDir, { recursive: true });
5007
+ mkdirSync5(configDir, { recursive: true });
4690
5008
  consola28.start(`Generating keypair for ${name}\u2026`);
4691
5009
  const { privatePem, publicSshLine } = generateKeyPairInMemory();
4692
- writeFileSync6(join11(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
5010
+ writeFileSync9(join13(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
4693
5011
  `, { mode: 384 });
4694
- writeFileSync6(join11(sshDir, "id_ed25519.pub"), `${publicSshLine}
5012
+ writeFileSync9(join13(sshDir, "id_ed25519.pub"), `${publicSshLine}
4695
5013
  `, { mode: 420 });
4696
5014
  chmodSync(sshDir, 448);
4697
5015
  consola28.start(`Registering nest at ${idp}\u2026`);
@@ -4708,10 +5026,10 @@ var enrollNestCommand = defineCommand33({
4708
5026
  accessToken: token,
4709
5027
  email: registration.email,
4710
5028
  expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
4711
- keyPath: join11(sshDir, "id_ed25519"),
5029
+ keyPath: join13(sshDir, "id_ed25519"),
4712
5030
  ownerEmail: ownerAuth.email
4713
5031
  });
4714
- writeFileSync6(authPath, authJson, { mode: 384 });
5032
+ writeFileSync9(authPath, authJson, { mode: 384 });
4715
5033
  chmodSync(configDir, 448);
4716
5034
  consola28.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
4717
5035
  consola28.info("");
@@ -4780,11 +5098,11 @@ var authorizeNestCommand = defineCommand34({
4780
5098
  }
4781
5099
  },
4782
5100
  async run({ args }) {
4783
- const nestAuthPath = join12(NEST_DATA_DIR, ".config", "apes", "auth.json");
4784
- if (!existsSync14(nestAuthPath)) {
5101
+ const nestAuthPath = join14(NEST_DATA_DIR, ".config", "apes", "auth.json");
5102
+ if (!existsSync17(nestAuthPath)) {
4785
5103
  throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
4786
5104
  }
4787
- const nestAuth = JSON.parse(readFileSync12(nestAuthPath, "utf8"));
5105
+ const nestAuth = JSON.parse(readFileSync15(nestAuthPath, "utf8"));
4788
5106
  if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
4789
5107
  const allow = args.allow ?? DEFAULT_ALLOW_PATTERNS.join(",");
4790
5108
  consola29.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
@@ -4801,7 +5119,7 @@ var authorizeNestCommand = defineCommand34({
4801
5119
  cmdArgs.push("--expires-in", args["expires-in"]);
4802
5120
  }
4803
5121
  try {
4804
- execFileSync10("apes", cmdArgs, { stdio: "inherit" });
5122
+ execFileSync14("apes", cmdArgs, { stdio: "inherit" });
4805
5123
  } catch (err) {
4806
5124
  throw new CliError(err instanceof Error ? err.message : String(err));
4807
5125
  }
@@ -4811,7 +5129,7 @@ var authorizeNestCommand = defineCommand34({
4811
5129
  });
4812
5130
 
4813
5131
  // src/commands/nest/destroy.ts
4814
- import { execFileSync as execFileSync11 } from "child_process";
5132
+ import { execFileSync as execFileSync15 } from "child_process";
4815
5133
  import { defineCommand as defineCommand35 } from "citty";
4816
5134
  import consola30 from "consola";
4817
5135
  var destroyNestCommand = defineCommand35({
@@ -4825,7 +5143,7 @@ var destroyNestCommand = defineCommand35({
4825
5143
  async run({ args }) {
4826
5144
  const name = String(args.name);
4827
5145
  try {
4828
- execFileSync11("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
5146
+ execFileSync15("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
4829
5147
  consola30.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
4830
5148
  } catch (err) {
4831
5149
  const status = err.status ?? 1;
@@ -4835,10 +5153,9 @@ var destroyNestCommand = defineCommand35({
4835
5153
  });
4836
5154
 
4837
5155
  // src/commands/nest/install.ts
4838
- import { execFileSync as execFileSync12 } from "child_process";
4839
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
4840
- import { homedir as homedir13, userInfo as userInfo2 } from "os";
4841
- import { dirname as dirname3, join as join13 } from "path";
5156
+ import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
5157
+ import { homedir as homedir13 } from "os";
5158
+ import { dirname as dirname3, join as join15 } from "path";
4842
5159
  import { defineCommand as defineCommand36 } from "citty";
4843
5160
  import consola31 from "consola";
4844
5161
 
@@ -4905,84 +5222,42 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
4905
5222
  `;
4906
5223
 
4907
5224
  // src/commands/nest/install.ts
4908
- var PLIST_LABEL = "ai.openape.nest";
4909
- function plistPath() {
4910
- return join13(homedir13(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4911
- }
4912
- function escape2(s) {
4913
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4914
- }
4915
- function buildPlist(args) {
4916
- const logsDir = join13(args.userHome, "Library", "Logs");
4917
- return `<?xml version="1.0" encoding="UTF-8"?>
4918
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4919
- <plist version="1.0">
4920
- <dict>
4921
- <key>Label</key>
4922
- <string>${escape2(PLIST_LABEL)}</string>
4923
- <key>ProgramArguments</key>
4924
- <array>
4925
- <string>${escape2(args.nestBin)}</string>
4926
- </array>
4927
- <key>WorkingDirectory</key>
4928
- <string>${escape2(args.nestHome)}</string>
4929
- <key>RunAtLoad</key>
4930
- <true/>
4931
- <key>KeepAlive</key>
4932
- <true/>
4933
- <key>ThrottleInterval</key>
4934
- <integer>10</integer>
4935
- <key>EnvironmentVariables</key>
4936
- <dict>
4937
- <key>HOME</key><string>${escape2(args.nestHome)}</string>
4938
- <key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
4939
- <key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
4940
- <key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
4941
- </dict>
4942
- <key>StandardOutPath</key>
4943
- <string>${escape2(logsDir)}/openape-nest.log</string>
4944
- <key>StandardErrorPath</key>
4945
- <string>${escape2(logsDir)}/openape-nest.log</string>
4946
- </dict>
4947
- </plist>
4948
- `;
4949
- }
4950
5225
  function installAdapter2() {
4951
- const target = join13(homedir13(), ".openape", "shapes", "adapters", "apes-agents.toml");
4952
- mkdirSync5(dirname3(target), { recursive: true });
5226
+ const target = join15(homedir13(), ".openape", "shapes", "adapters", "apes-agents.toml");
5227
+ mkdirSync6(dirname3(target), { recursive: true });
4953
5228
  let existing = "";
4954
5229
  try {
4955
- existing = readFileSync13(target, "utf8");
5230
+ existing = readFileSync16(target, "utf8");
4956
5231
  } catch {
4957
5232
  }
4958
5233
  if (existing === APES_AGENTS_ADAPTER_TOML) return false;
4959
- writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
5234
+ writeFileSync10(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4960
5235
  consola31.success(`Wrote shapes adapter ${target}`);
4961
5236
  return true;
4962
5237
  }
4963
5238
  function writeBridgeModelDefault(model) {
4964
- for (const envDir of [join13(homedir13(), "litellm"), join13(NEST_DATA_DIR, "litellm")]) {
4965
- const envFile = join13(envDir, ".env");
4966
- mkdirSync5(envDir, { recursive: true });
5239
+ for (const envDir of [join15(homedir13(), "litellm"), join15(NEST_DATA_DIR, "litellm")]) {
5240
+ const envFile = join15(envDir, ".env");
5241
+ mkdirSync6(envDir, { recursive: true });
4967
5242
  let lines = [];
4968
- if (existsSync15(envFile)) {
4969
- lines = readFileSync13(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
5243
+ if (existsSync18(envFile)) {
5244
+ lines = readFileSync16(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
4970
5245
  }
4971
5246
  lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
4972
5247
  while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
4973
- writeFileSync7(envFile, `${lines.join("\n")}
5248
+ writeFileSync10(envFile, `${lines.join("\n")}
4974
5249
  `, { mode: 384 });
4975
5250
  }
4976
5251
  }
4977
5252
  function findBinary(name) {
4978
5253
  for (const dir of [
4979
- join13(homedir13(), ".bun", "bin"),
5254
+ join15(homedir13(), ".bun", "bin"),
4980
5255
  "/opt/homebrew/bin",
4981
5256
  "/usr/local/bin",
4982
5257
  "/usr/bin"
4983
5258
  ]) {
4984
- const p = join13(dir, name);
4985
- if (existsSync15(p)) return p;
5259
+ const p = join15(dir, name);
5260
+ if (existsSync18(p)) return p;
4986
5261
  }
4987
5262
  throw new Error(`could not locate ${name} on PATH; install it first`);
4988
5263
  }
@@ -5009,7 +5284,7 @@ var installNestCommand = defineCommand36({
5009
5284
  }
5010
5285
  const nestBin = findBinary("openape-nest");
5011
5286
  const apesBin = findBinary("apes");
5012
- consola31.info(`Installing nest at ${plistPath()}`);
5287
+ consola31.info(`Installing nest supervisor`);
5013
5288
  consola31.info(` nest binary: ${nestBin}`);
5014
5289
  consola31.info(` apes binary: ${apesBin}`);
5015
5290
  consola31.info(` HTTP port: ${port}`);
@@ -5018,26 +5293,14 @@ var installNestCommand = defineCommand36({
5018
5293
  consola31.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
5019
5294
  }
5020
5295
  installAdapter2();
5021
- mkdirSync5(join13(homeDir, "Library", "LaunchAgents"), { recursive: true });
5022
- mkdirSync5(NEST_DATA_DIR, { recursive: true });
5023
- const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
5024
- let existing = "";
5025
- try {
5026
- existing = readFileSync13(plistPath(), "utf8");
5027
- } catch {
5028
- }
5029
- if (existing !== desired) {
5030
- writeFileSync7(plistPath(), desired, { mode: 420 });
5031
- consola31.success("Wrote launchd plist");
5032
- } else {
5033
- consola31.info("plist already up to date");
5034
- }
5035
- const uid = userInfo2().uid;
5036
- try {
5037
- execFileSync12("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
5038
- } catch {
5039
- }
5040
- execFileSync12("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
5296
+ mkdirSync6(NEST_DATA_DIR, { recursive: true });
5297
+ await getHostPlatform().installNestSupervisor({
5298
+ nestBin,
5299
+ apesBin,
5300
+ userHome: homeDir,
5301
+ nestHome: NEST_DATA_DIR,
5302
+ port
5303
+ });
5041
5304
  consola31.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
5042
5305
  consola31.info("");
5043
5306
  consola31.info("Next steps for zero-prompt spawn \u2014 both one-time:");
@@ -5079,7 +5342,7 @@ var listNestCommand = defineCommand37({
5079
5342
  });
5080
5343
 
5081
5344
  // src/commands/nest/spawn.ts
5082
- import { execFileSync as execFileSync13 } from "child_process";
5345
+ import { execFileSync as execFileSync16 } from "child_process";
5083
5346
  import { defineCommand as defineCommand38 } from "citty";
5084
5347
  import consola33 from "consola";
5085
5348
  var spawnNestCommand = defineCommand38({
@@ -5112,7 +5375,7 @@ var spawnNestCommand = defineCommand38({
5112
5375
  if (typeof args["bridge-base-url"] === "string") apesArgs.push("--bridge-base-url", args["bridge-base-url"]);
5113
5376
  if (typeof args["bridge-model"] === "string") apesArgs.push("--bridge-model", args["bridge-model"]);
5114
5377
  try {
5115
- execFileSync13("apes", apesArgs, { stdio: "inherit" });
5378
+ execFileSync16("apes", apesArgs, { stdio: "inherit" });
5116
5379
  consola33.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
5117
5380
  } catch (err) {
5118
5381
  const status = err.status ?? 1;
@@ -5122,31 +5385,16 @@ var spawnNestCommand = defineCommand38({
5122
5385
  });
5123
5386
 
5124
5387
  // src/commands/nest/uninstall.ts
5125
- import { execFileSync as execFileSync14 } from "child_process";
5126
- import { existsSync as existsSync16, unlinkSync } from "fs";
5127
- import { homedir as homedir14, userInfo as userInfo3 } from "os";
5128
- import { join as join14 } from "path";
5129
5388
  import { defineCommand as defineCommand39 } from "citty";
5130
5389
  import consola34 from "consola";
5131
- var PLIST_LABEL2 = "ai.openape.nest";
5132
5390
  var uninstallNestCommand = defineCommand39({
5133
5391
  meta: {
5134
5392
  name: "uninstall",
5135
5393
  description: "Stop + remove the local nest-daemon (registry + agents preserved)"
5136
5394
  },
5137
5395
  async run() {
5138
- const uid = userInfo3().uid;
5139
- const path2 = join14(homedir14(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
5140
- try {
5141
- execFileSync14("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
5142
- consola34.success("Nest daemon stopped");
5143
- } catch {
5144
- consola34.info("Nest daemon was not loaded");
5145
- }
5146
- if (existsSync16(path2)) {
5147
- unlinkSync(path2);
5148
- consola34.success(`Removed ${path2}`);
5149
- }
5396
+ await getHostPlatform().uninstallNestSupervisor();
5397
+ consola34.success("Nest daemon stopped + supervisor unit removed");
5150
5398
  consola34.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
5151
5399
  }
5152
5400
  });
@@ -5692,18 +5940,19 @@ var adapterCommand = defineCommand45({
5692
5940
  });
5693
5941
 
5694
5942
  // src/commands/run.ts
5695
- import { execFileSync as execFileSync15 } from "child_process";
5696
- import { hostname as hostname5 } from "os";
5943
+ import { execFileSync as execFileSync17 } from "child_process";
5944
+ import { hostname as hostname6 } from "os";
5697
5945
  import { basename } from "path";
5698
5946
  import { defineCommand as defineCommand46 } from "citty";
5699
5947
  import consola39 from "consola";
5700
5948
  function resolveRunAsTarget(runAs) {
5701
5949
  if (!runAs) return runAs;
5702
- if (!isDarwin()) return runAs;
5950
+ if (!isDarwin2()) return runAs;
5703
5951
  if (!AGENT_NAME_REGEX.test(runAs)) return runAs;
5704
5952
  if (runAs.startsWith("openape-agent-")) return runAs;
5705
- const prefixed = macOSUsernameForAgent(runAs);
5706
- if (readMacOSUser(prefixed)) return prefixed;
5953
+ const platform = getHostPlatform();
5954
+ const prefixed = platform.agentUsername(runAs);
5955
+ if (platform.readAgentUser(prefixed)) return prefixed;
5707
5956
  return runAs;
5708
5957
  }
5709
5958
  function shouldWaitForGrant(args) {
@@ -5876,7 +6125,7 @@ async function runShellMode(command, args) {
5876
6125
  const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
5877
6126
  if (adapterHandled) return;
5878
6127
  const grantsUrl = await getGrantsEndpoint(idp);
5879
- const targetHost = args.host || hostname5();
6128
+ const targetHost = args.host || hostname6();
5880
6129
  try {
5881
6130
  const grants = await apiFetch(
5882
6131
  `${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
@@ -5972,7 +6221,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
5972
6221
  approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
5973
6222
  command: resolved.detail?.display || parsed?.raw || "unknown",
5974
6223
  audience: resolved.adapter?.cli?.audience ?? "shapes",
5975
- host: args.host || hostname5()
6224
+ host: args.host || hostname6()
5976
6225
  });
5977
6226
  if (shouldWaitForGrant(args)) {
5978
6227
  consola39.info(`Grant requested: ${grant.id}`);
@@ -5992,7 +6241,7 @@ function execShellCommand(command) {
5992
6241
  throw new CliError("No command to execute");
5993
6242
  try {
5994
6243
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5995
- execFileSync15(command[0], command.slice(1), {
6244
+ execFileSync17(command[0], command.slice(1), {
5996
6245
  stdio: "inherit",
5997
6246
  env: inheritedEnv
5998
6247
  });
@@ -6093,7 +6342,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
6093
6342
  const idp = getIdpUrl(args.idp);
6094
6343
  const grantsUrl = await getGrantsEndpoint(idp);
6095
6344
  const command = commandArgv ?? action.split(" ");
6096
- const targetHost = args.host || hostname5();
6345
+ const targetHost = args.host || hostname6();
6097
6346
  const runAs = resolveRunAsTarget(args.as ?? void 0);
6098
6347
  const reusableId = await findReusableAudienceGrant({
6099
6348
  grantsUrl,
@@ -6161,7 +6410,7 @@ function executeWithGrantToken(opts) {
6161
6410
  consola39.info(`Executing: ${command.join(" ")}`);
6162
6411
  try {
6163
6412
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
6164
- execFileSync15(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
6413
+ execFileSync17(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
6165
6414
  stdio: "inherit",
6166
6415
  env: inheritedEnv
6167
6416
  });
@@ -6197,9 +6446,9 @@ async function findReusableAudienceGrant(opts) {
6197
6446
 
6198
6447
  // src/commands/proxy.ts
6199
6448
  import { spawn as spawn2 } from "child_process";
6200
- import { existsSync as existsSync18 } from "fs";
6201
- import { homedir as homedir15 } from "os";
6202
- import { join as join17 } from "path";
6449
+ import { existsSync as existsSync20 } from "fs";
6450
+ import { homedir as homedir14 } from "os";
6451
+ import { join as join18 } from "path";
6203
6452
  import { defineCommand as defineCommand47 } from "citty";
6204
6453
  import consola40 from "consola";
6205
6454
 
@@ -6235,10 +6484,10 @@ note = "VPC-internal hostname suffix"
6235
6484
 
6236
6485
  // src/proxy/local-proxy.ts
6237
6486
  import { spawn } from "child_process";
6238
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
6487
+ import { mkdtempSync as mkdtempSync4, rmSync as rmSync6, writeFileSync as writeFileSync11 } from "fs";
6239
6488
  import { createRequire } from "module";
6240
- import { tmpdir as tmpdir3 } from "os";
6241
- import { dirname as dirname4, join as join15, resolve as resolve3 } from "path";
6489
+ import { tmpdir as tmpdir4 } from "os";
6490
+ import { dirname as dirname4, join as join16, resolve as resolve3 } from "path";
6242
6491
  var require2 = createRequire(import.meta.url);
6243
6492
  function findProxyBin() {
6244
6493
  const pkgPath = require2.resolve("@openape/proxy/package.json");
@@ -6250,9 +6499,9 @@ function findProxyBin() {
6250
6499
  return resolve3(dirname4(pkgPath), binRel);
6251
6500
  }
6252
6501
  async function startEphemeralProxy(configToml) {
6253
- const tmpDir = mkdtempSync3(join15(tmpdir3(), "openape-proxy-"));
6254
- const configPath = join15(tmpDir, "config.toml");
6255
- writeFileSync8(configPath, configToml, { mode: 384 });
6502
+ const tmpDir = mkdtempSync4(join16(tmpdir4(), "openape-proxy-"));
6503
+ const configPath = join16(tmpDir, "config.toml");
6504
+ writeFileSync11(configPath, configToml, { mode: 384 });
6256
6505
  const binPath = findProxyBin();
6257
6506
  const child = spawn(process.execPath, [binPath, "-c", configPath], {
6258
6507
  stdio: ["ignore", "pipe", "pipe"],
@@ -6260,7 +6509,7 @@ async function startEphemeralProxy(configToml) {
6260
6509
  });
6261
6510
  const cleanupTmp = () => {
6262
6511
  try {
6263
- rmSync5(tmpDir, { recursive: true, force: true });
6512
+ rmSync6(tmpDir, { recursive: true, force: true });
6264
6513
  } catch {
6265
6514
  }
6266
6515
  };
@@ -6334,9 +6583,9 @@ function waitForListenLine(child) {
6334
6583
  }
6335
6584
 
6336
6585
  // src/proxy/trust-bundle.ts
6337
- import { existsSync as existsSync17, mkdtempSync as mkdtempSync4, readFileSync as readFileSync14, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
6338
- import { tmpdir as tmpdir4 } from "os";
6339
- import { join as join16 } from "path";
6586
+ import { existsSync as existsSync19, mkdtempSync as mkdtempSync5, readFileSync as readFileSync17, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
6587
+ import { tmpdir as tmpdir5 } from "os";
6588
+ import { join as join17 } from "path";
6340
6589
  var CANDIDATES = [
6341
6590
  "/etc/ssl/cert.pem",
6342
6591
  // macOS
@@ -6349,25 +6598,25 @@ var CANDIDATES = [
6349
6598
  ];
6350
6599
  function detectSystemCaPath() {
6351
6600
  for (const p of CANDIDATES) {
6352
- if (existsSync17(p)) return p;
6601
+ if (existsSync19(p)) return p;
6353
6602
  }
6354
6603
  throw new Error(
6355
6604
  `Could not locate a system CA bundle. Tried: ${CANDIDATES.join(", ")}. Set NODE_EXTRA_CA_CERTS yourself or pass --allow-no-system-ca.`
6356
6605
  );
6357
6606
  }
6358
6607
  function buildTrustBundle(opts) {
6359
- const dir = mkdtempSync4(join16(tmpdir4(), "openape-trust-"));
6360
- const path2 = join16(dir, "bundle.pem");
6361
- const sys = readFileSync14(opts.systemCaPath, "utf-8");
6362
- const local = readFileSync14(opts.localCaPath, "utf-8");
6363
- writeFileSync9(path2, `${sys.trimEnd()}
6608
+ const dir = mkdtempSync5(join17(tmpdir5(), "openape-trust-"));
6609
+ const path2 = join17(dir, "bundle.pem");
6610
+ const sys = readFileSync17(opts.systemCaPath, "utf-8");
6611
+ const local = readFileSync17(opts.localCaPath, "utf-8");
6612
+ writeFileSync12(path2, `${sys.trimEnd()}
6364
6613
  ${local.trimEnd()}
6365
6614
  `, { mode: 384 });
6366
6615
  return {
6367
6616
  path: path2,
6368
6617
  cleanup: () => {
6369
6618
  try {
6370
- unlinkSync2(path2);
6619
+ unlinkSync3(path2);
6371
6620
  } catch {
6372
6621
  }
6373
6622
  try {
@@ -6417,8 +6666,8 @@ var proxyCommand = defineCommand47({
6417
6666
  if (reuseHostPort) {
6418
6667
  proxyUrl = `http://${reuseHostPort}`;
6419
6668
  consola40.info(`[apes proxy] using long-running daemon at ${proxyUrl}`);
6420
- const localCaPath = join17(homedir15(), ".openape", "proxy", "ca.crt");
6421
- if (!existsSync18(localCaPath)) {
6669
+ const localCaPath = join18(homedir14(), ".openape", "proxy", "ca.crt");
6670
+ if (!existsSync20(localCaPath)) {
6422
6671
  throw new CliError(
6423
6672
  `OPENAPE_PROXY is set but no local CA found at ${localCaPath}. Start the daemon (sudo -u <agent> apes proxy --global < secrets.toml) first.`
6424
6673
  );
@@ -6760,16 +7009,16 @@ var mcpCommand = defineCommand52({
6760
7009
  if (transport !== "stdio" && transport !== "sse") {
6761
7010
  throw new Error('Transport must be "stdio" or "sse"');
6762
7011
  }
6763
- const { startMcpServer } = await import("./server-WB77GNKJ.js");
7012
+ const { startMcpServer } = await import("./server-JGX5FIDP.js");
6764
7013
  await startMcpServer(transport, port);
6765
7014
  }
6766
7015
  });
6767
7016
 
6768
7017
  // src/commands/init/index.ts
6769
- import { existsSync as existsSync19, copyFileSync, writeFileSync as writeFileSync10 } from "fs";
7018
+ import { existsSync as existsSync21, copyFileSync, writeFileSync as writeFileSync13 } from "fs";
6770
7019
  import { randomBytes } from "crypto";
6771
- import { execFileSync as execFileSync16 } from "child_process";
6772
- import { join as join18 } from "path";
7020
+ import { execFileSync as execFileSync18 } from "child_process";
7021
+ import { join as join19 } from "path";
6773
7022
  import { defineCommand as defineCommand53 } from "citty";
6774
7023
  import consola43 from "consola";
6775
7024
  var DEFAULT_IDP_URL = "https://id.openape.at";
@@ -6778,13 +7027,13 @@ async function downloadTemplate(repo, targetDir) {
6778
7027
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
6779
7028
  }
6780
7029
  function installDeps(dir) {
6781
- const hasLockFile = (name) => existsSync19(join18(dir, name));
7030
+ const hasLockFile = (name) => existsSync21(join19(dir, name));
6782
7031
  if (hasLockFile("pnpm-lock.yaml")) {
6783
- execFileSync16("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
7032
+ execFileSync18("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6784
7033
  } else if (hasLockFile("bun.lockb")) {
6785
- execFileSync16("bun", ["install"], { cwd: dir, stdio: "inherit" });
7034
+ execFileSync18("bun", ["install"], { cwd: dir, stdio: "inherit" });
6786
7035
  } else {
6787
- execFileSync16("npm", ["install"], { cwd: dir, stdio: "inherit" });
7036
+ execFileSync18("npm", ["install"], { cwd: dir, stdio: "inherit" });
6788
7037
  }
6789
7038
  }
6790
7039
  async function promptChoice(message, choices) {
@@ -6843,7 +7092,7 @@ var initCommand = defineCommand53({
6843
7092
  });
6844
7093
  async function initSP(targetDir) {
6845
7094
  const dir = targetDir || "my-app";
6846
- if (existsSync19(join18(dir, "package.json"))) {
7095
+ if (existsSync21(join19(dir, "package.json"))) {
6847
7096
  throw new CliError(`Directory "${dir}" already contains a project.`);
6848
7097
  }
6849
7098
  consola43.start("Scaffolding SP starter...");
@@ -6852,9 +7101,9 @@ async function initSP(targetDir) {
6852
7101
  consola43.start("Installing dependencies...");
6853
7102
  installDeps(dir);
6854
7103
  consola43.success("Dependencies installed");
6855
- const envExample = join18(dir, ".env.example");
6856
- const envFile = join18(dir, ".env");
6857
- if (existsSync19(envExample) && !existsSync19(envFile)) {
7104
+ const envExample = join19(dir, ".env.example");
7105
+ const envFile = join19(dir, ".env");
7106
+ if (existsSync21(envExample) && !existsSync21(envFile)) {
6858
7107
  copyFileSync(envExample, envFile);
6859
7108
  consola43.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
6860
7109
  }
@@ -6868,7 +7117,7 @@ async function initSP(targetDir) {
6868
7117
  }
6869
7118
  async function initIdP(targetDir) {
6870
7119
  const dir = targetDir || "my-idp";
6871
- if (existsSync19(join18(dir, "package.json"))) {
7120
+ if (existsSync21(join19(dir, "package.json"))) {
6872
7121
  throw new CliError(`Directory "${dir}" already contains a project.`);
6873
7122
  }
6874
7123
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -6900,7 +7149,7 @@ async function initIdP(targetDir) {
6900
7149
  `NUXT_OPENAPE_RP_ID=${domain}`,
6901
7150
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
6902
7151
  ].join("\n");
6903
- writeFileSync10(join18(dir, ".env"), `${envContent}
7152
+ writeFileSync13(join19(dir, ".env"), `${envContent}
6904
7153
  `, { mode: 384 });
6905
7154
  consola43.success(".env created");
6906
7155
  console.log("");
@@ -6921,7 +7170,7 @@ async function initIdP(targetDir) {
6921
7170
 
6922
7171
  // src/commands/enroll.ts
6923
7172
  import { Buffer as Buffer5 } from "buffer";
6924
- import { existsSync as existsSync20, readFileSync as readFileSync15 } from "fs";
7173
+ import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
6925
7174
  import { execFile as execFile2 } from "child_process";
6926
7175
  import { sign as sign2 } from "crypto";
6927
7176
  import { defineCommand as defineCommand54 } from "citty";
@@ -6937,7 +7186,7 @@ function openBrowser2(url) {
6937
7186
  }
6938
7187
  async function pollForEnrollment(idp, agentEmail, keyPath) {
6939
7188
  const resolvedKey = resolveKeyPath(keyPath);
6940
- const keyContent = readFileSync15(resolvedKey, "utf-8");
7189
+ const keyContent = readFileSync18(resolvedKey, "utf-8");
6941
7190
  const privateKey = loadEd25519PrivateKey(keyContent);
6942
7191
  const challengeUrl = await getAgentChallengeEndpoint(idp);
6943
7192
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -7005,7 +7254,7 @@ var enrollCommand = defineCommand54({
7005
7254
  }) || DEFAULT_KEY_PATH;
7006
7255
  const resolvedKey = resolveKeyPath(keyPath);
7007
7256
  let publicKey;
7008
- if (existsSync20(resolvedKey)) {
7257
+ if (existsSync22(resolvedKey)) {
7009
7258
  publicKey = readPublicKey(resolvedKey);
7010
7259
  consola44.success(`Using existing key ${keyPath}`);
7011
7260
  } else {
@@ -7049,7 +7298,7 @@ var enrollCommand = defineCommand54({
7049
7298
  });
7050
7299
 
7051
7300
  // src/commands/register-user.ts
7052
- import { existsSync as existsSync21, readFileSync as readFileSync16 } from "fs";
7301
+ import { existsSync as existsSync23, readFileSync as readFileSync19 } from "fs";
7053
7302
  import { defineCommand as defineCommand55 } from "citty";
7054
7303
  import consola45 from "consola";
7055
7304
  var registerUserCommand = defineCommand55({
@@ -7088,8 +7337,8 @@ var registerUserCommand = defineCommand55({
7088
7337
  throw new CliError("No IdP URL configured. Run `apes login` first.");
7089
7338
  }
7090
7339
  let publicKey = args.key;
7091
- if (existsSync21(args.key)) {
7092
- publicKey = readFileSync16(args.key, "utf-8").trim();
7340
+ if (existsSync23(args.key)) {
7341
+ publicKey = readFileSync19(args.key, "utf-8").trim();
7093
7342
  }
7094
7343
  if (!publicKey.startsWith("ssh-ed25519 ")) {
7095
7344
  throw new CliError("Public key must be in ssh-ed25519 format.");
@@ -7398,7 +7647,7 @@ async function bestEffortGrantCount(idp) {
7398
7647
  }
7399
7648
  }
7400
7649
  async function runHealth(args) {
7401
- const version = true ? "1.28.11" : "0.0.0";
7650
+ const version = true ? "1.28.13" : "0.0.0";
7402
7651
  const auth = loadAuth();
7403
7652
  if (!auth) {
7404
7653
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -7591,26 +7840,26 @@ var workflowsCommand = defineCommand63({
7591
7840
  });
7592
7841
 
7593
7842
  // src/version-check.ts
7594
- import { existsSync as existsSync22, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
7595
- import { homedir as homedir16 } from "os";
7596
- import { join as join19 } from "path";
7843
+ import { existsSync as existsSync24, mkdirSync as mkdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
7844
+ import { homedir as homedir15 } from "os";
7845
+ import { join as join20 } from "path";
7597
7846
  import consola51 from "consola";
7598
7847
  var PACKAGE_NAME = "@openape/apes";
7599
7848
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
7600
- var CACHE_FILE = join19(homedir16(), ".config", "apes", ".version-check.json");
7849
+ var CACHE_FILE = join20(homedir15(), ".config", "apes", ".version-check.json");
7601
7850
  function readCache() {
7602
- if (!existsSync22(CACHE_FILE)) return null;
7851
+ if (!existsSync24(CACHE_FILE)) return null;
7603
7852
  try {
7604
- return JSON.parse(readFileSync17(CACHE_FILE, "utf-8"));
7853
+ return JSON.parse(readFileSync20(CACHE_FILE, "utf-8"));
7605
7854
  } catch {
7606
7855
  return null;
7607
7856
  }
7608
7857
  }
7609
7858
  function writeCache(entry) {
7610
7859
  try {
7611
- const dir = join19(homedir16(), ".config", "apes");
7612
- if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
7613
- writeFileSync11(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
7860
+ const dir = join20(homedir15(), ".config", "apes");
7861
+ if (!existsSync24(dir)) mkdirSync7(dir, { recursive: true, mode: 448 });
7862
+ writeFileSync14(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
7614
7863
  } catch {
7615
7864
  }
7616
7865
  }
@@ -7671,10 +7920,10 @@ if (shellRewrite) {
7671
7920
  if (shellRewrite.action === "rewrite") {
7672
7921
  process.argv = shellRewrite.argv;
7673
7922
  } else if (shellRewrite.action === "version") {
7674
- console.log(`ape-shell ${"1.28.11"} (OpenApe DDISA shell wrapper)`);
7923
+ console.log(`ape-shell ${"1.28.13"} (OpenApe DDISA shell wrapper)`);
7675
7924
  process.exit(0);
7676
7925
  } else if (shellRewrite.action === "help") {
7677
- console.log(`ape-shell ${"1.28.11"} \u2014 OpenApe DDISA shell wrapper`);
7926
+ console.log(`ape-shell ${"1.28.13"} \u2014 OpenApe DDISA shell wrapper`);
7678
7927
  console.log("");
7679
7928
  console.log("Usage:");
7680
7929
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -7689,7 +7938,7 @@ if (shellRewrite) {
7689
7938
  console.log(" --help, -h Show this help message");
7690
7939
  process.exit(0);
7691
7940
  } else if (shellRewrite.action === "interactive") {
7692
- const { runInteractiveShell } = await import("./orchestrator-CIDV7OGM.js");
7941
+ const { runInteractiveShell } = await import("./orchestrator-REICEX3F.js");
7693
7942
  await runInteractiveShell();
7694
7943
  process.exit(0);
7695
7944
  } else {
@@ -7732,7 +7981,7 @@ var configCommand = defineCommand64({
7732
7981
  var main = defineCommand64({
7733
7982
  meta: {
7734
7983
  name: "apes",
7735
- version: "1.28.11",
7984
+ version: "1.28.13",
7736
7985
  description: "Unified CLI for OpenApe"
7737
7986
  },
7738
7987
  subCommands: {
@@ -7784,13 +8033,13 @@ async function maybeRefreshAuth() {
7784
8033
  const { loadAuth: loadAuth2 } = await import("./config-MOB5DJ6H.js");
7785
8034
  if (!loadAuth2()) return;
7786
8035
  try {
7787
- const { ensureFreshToken } = await import("./http-6OKWT52Z.js");
8036
+ const { ensureFreshToken } = await import("./http-UPOTOYQV.js");
7788
8037
  await ensureFreshToken();
7789
8038
  } catch {
7790
8039
  }
7791
8040
  }
7792
8041
  await maybeRefreshAuth();
7793
- await maybeWarnStaleVersion("1.28.11").catch(() => {
8042
+ await maybeWarnStaleVersion("1.28.13").catch(() => {
7794
8043
  });
7795
8044
  runMain(main).catch((err) => {
7796
8045
  if (err instanceof CliExit) {