@openape/apes 1.25.1 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,10 +3,17 @@ import {
3
3
  CliError,
4
4
  CliExit,
5
5
  RpcSessionMap,
6
+ buildCreateCommand,
7
+ buildIssueGet,
8
+ buildPrCreate,
9
+ buildPrMerge,
10
+ detectForge,
6
11
  parseDuration,
12
+ runApeShell,
7
13
  runLoop,
8
- taskTools
9
- } from "./chunk-L2V3CW5B.js";
14
+ taskTools,
15
+ worktreePathFor
16
+ } from "./chunk-AFTJZVOQ.js";
10
17
  import {
11
18
  loadEd25519PrivateKey,
12
19
  readPublicKeyComment
@@ -66,7 +73,7 @@ import {
66
73
  } from "./chunk-OBF7IMQ2.js";
67
74
 
68
75
  // src/cli.ts
69
- import consola51 from "consola";
76
+ import consola52 from "consola";
70
77
 
71
78
  // src/ape-shell.ts
72
79
  import path from "path";
@@ -96,7 +103,7 @@ function rewriteApeShellArgs(argv, argv0) {
96
103
  }
97
104
 
98
105
  // src/cli.ts
99
- import { defineCommand as defineCommand63, runMain } from "citty";
106
+ import { defineCommand as defineCommand64, runMain } from "citty";
100
107
 
101
108
  // src/commands/auth/login.ts
102
109
  import { Buffer as Buffer2 } from "buffer";
@@ -382,7 +389,7 @@ async function loginWithPKCE(idp) {
382
389
  consola2.success(`Logged in as ${payload.email || payload.sub}`);
383
390
  }
384
391
  async function loginWithKey(idp, keyPath, agentEmail) {
385
- const { readFileSync: readFileSync17 } = await import("fs");
392
+ const { readFileSync: readFileSync18 } = await import("fs");
386
393
  const { sign: sign3 } = await import("crypto");
387
394
  const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
388
395
  const challengeUrl = await getAgentChallengeEndpoint(idp);
@@ -395,7 +402,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
395
402
  throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
396
403
  }
397
404
  const { challenge } = await challengeResp.json();
398
- const keyContent = readFileSync17(keyPath, "utf-8");
405
+ const keyContent = readFileSync18(keyPath, "utf-8");
399
406
  const privateKey = loadEd25519PrivateKey2(keyContent);
400
407
  const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
401
408
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -1955,7 +1962,7 @@ var agentCommand = defineCommand21({
1955
1962
  });
1956
1963
 
1957
1964
  // src/commands/agents/index.ts
1958
- import { defineCommand as defineCommand31 } from "citty";
1965
+ import { defineCommand as defineCommand32 } from "citty";
1959
1966
 
1960
1967
  // src/commands/agents/allow.ts
1961
1968
  import { execFileSync as execFileSync4 } from "child_process";
@@ -2619,31 +2626,483 @@ var cleanupOrphansCommand = defineCommand23({
2619
2626
  }
2620
2627
  });
2621
2628
 
2629
+ // src/commands/agents/code.ts
2630
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
2631
+ import { homedir as homedir4 } from "os";
2632
+ import { join as join2 } from "path";
2633
+ import process2 from "process";
2634
+ import { defineCommand as defineCommand24 } from "citty";
2635
+ import { consola as consola21 } from "consola";
2636
+
2637
+ // src/lib/coding/issue-task.ts
2638
+ var DEFAULT_TEMPLATE = "{type}/issue-{number}-{slug}";
2639
+ var DEFAULT_TYPE = "fix";
2640
+ var DEFAULT_SLUG_MAX = 48;
2641
+ var DEFAULT_INSTRUCTIONS = [
2642
+ "Work in the provided worktree. When done:",
2643
+ "- ensure the verification command passes (no PR on red)",
2644
+ "- open a PR that references this issue",
2645
+ "- leave risk-path / agent-judged-risky changes for human approval"
2646
+ ].join("\n");
2647
+ var TYPE_RE = /^[a-z]{2,12}$/;
2648
+ function slugify(title, max = DEFAULT_SLUG_MAX) {
2649
+ return title.toLowerCase().normalize("NFKD").replace(/[^\w\s-]/g, "").trim().replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, max).replace(/-$/, "");
2650
+ }
2651
+ function buildBranchName(issue, naming = {}) {
2652
+ const num = String(issue.number).replace(/\D/g, "");
2653
+ if (!num) throw new Error("issue number required");
2654
+ const type = issue.type && TYPE_RE.test(issue.type) ? issue.type : naming.defaultType ?? DEFAULT_TYPE;
2655
+ const slug = slugify(issue.title, naming.slugMax ?? DEFAULT_SLUG_MAX) || "task";
2656
+ return (naming.template ?? DEFAULT_TEMPLATE).replace(/\{type\}/g, type).replace(/\{number\}/g, num).replace(/\{slug\}/g, slug);
2657
+ }
2658
+ function buildTaskPrompt(issue, framing = {}) {
2659
+ const num = String(issue.number);
2660
+ const parts = [];
2661
+ if (framing.persona?.trim()) parts.push(framing.persona.trim());
2662
+ parts.push(`Issue #${num}: ${issue.title}`);
2663
+ parts.push(issue.body?.trim() ? issue.body.trim() : "(no description provided)");
2664
+ parts.push(framing.instructions?.trim() ? framing.instructions.trim() : DEFAULT_INSTRUCTIONS);
2665
+ return parts.join("\n\n");
2666
+ }
2667
+
2668
+ // src/lib/coding/merge-policy.ts
2669
+ var SECURE_DEFAULT_POLICY = {
2670
+ autoMergeEnabled: false,
2671
+ autoPaths: [],
2672
+ riskPaths: []
2673
+ };
2674
+ function globToRegExp(glob) {
2675
+ let re = "";
2676
+ for (let i = 0; i < glob.length; i++) {
2677
+ const c = glob[i];
2678
+ if (c === "*") {
2679
+ if (glob[i + 1] === "*") {
2680
+ re += ".*";
2681
+ i++;
2682
+ if (glob[i + 1] === "/") i++;
2683
+ } else {
2684
+ re += "[^/]*";
2685
+ }
2686
+ } else if (c === "?") {
2687
+ re += "[^/]";
2688
+ } else if (".+^${}()|[]\\".includes(c)) {
2689
+ re += `\\${c}`;
2690
+ } else {
2691
+ re += c;
2692
+ }
2693
+ }
2694
+ return new RegExp(`^${re}$`);
2695
+ }
2696
+ function matchesAny(path2, globs) {
2697
+ return globs.some((g) => globToRegExp(g).test(path2));
2698
+ }
2699
+ function classifyChange(paths, policy = SECURE_DEFAULT_POLICY) {
2700
+ if (paths.length === 0) return "code";
2701
+ if (paths.some((p) => matchesAny(p, policy.riskPaths))) return "risk";
2702
+ if (policy.autoPaths.length > 0 && paths.every((p) => matchesAny(p, policy.autoPaths))) return "chore";
2703
+ return "code";
2704
+ }
2705
+ function decideMerge(paths, policy = SECURE_DEFAULT_POLICY, agentRisk) {
2706
+ const globClass = classifyChange(paths, policy);
2707
+ const isRisk = globClass === "risk" || agentRisk?.risky === true;
2708
+ const classification = isRisk ? "risk" : globClass;
2709
+ if (!policy.autoMergeEnabled) {
2710
+ return { classification, autoMerge: false, needsReview: false, needsHuman: true, reason: "auto-merge not enabled for this repo \u2014 human approval required (set autoMergeEnabled in .openape/coding.json to opt in)" };
2711
+ }
2712
+ if (isRisk) {
2713
+ const why = agentRisk?.risky ? `agent judged this risky${agentRisk.reason ? `: ${agentRisk.reason}` : ""}` : "matches a repo/derived risk path";
2714
+ return { classification, autoMerge: false, needsReview: false, needsHuman: true, reason: `${why} \u2014 human approval required` };
2715
+ }
2716
+ if (classification === "chore") {
2717
+ return { classification, autoMerge: true, needsReview: false, needsHuman: false, reason: "chore/docs only \u2014 auto-merge on green CI" };
2718
+ }
2719
+ return { classification, autoMerge: true, needsReview: true, needsHuman: false, reason: "code change \u2014 auto-merge after reviewer-agent approval" };
2720
+ }
2721
+ async function loadMergePolicy(worktreeDir) {
2722
+ const { readFile } = await import("fs/promises");
2723
+ const { join: join20 } = await import("path");
2724
+ try {
2725
+ const raw = await readFile(join20(worktreeDir, ".openape", "coding.json"), "utf8");
2726
+ const parsed = JSON.parse(raw);
2727
+ const mp = parsed.mergePolicy;
2728
+ if (!mp) return SECURE_DEFAULT_POLICY;
2729
+ return {
2730
+ autoMergeEnabled: mp.autoMergeEnabled === true,
2731
+ autoPaths: Array.isArray(mp.autoPaths) ? mp.autoPaths.filter((g) => typeof g === "string") : [],
2732
+ riskPaths: Array.isArray(mp.riskPaths) ? mp.riskPaths.filter((g) => typeof g === "string") : []
2733
+ };
2734
+ } catch {
2735
+ return SECURE_DEFAULT_POLICY;
2736
+ }
2737
+ }
2738
+
2739
+ // src/lib/coding/review-gate.ts
2740
+ async function gateMerge(decision, req, reviewer) {
2741
+ if (decision.needsHuman) {
2742
+ return { armMerge: false, awaitingHuman: true, reason: "risk-path change \u2014 handed off to human reviewer" };
2743
+ }
2744
+ if (!decision.needsReview) {
2745
+ return { armMerge: true, awaitingHuman: false, reason: "chore change \u2014 no reviewer gate" };
2746
+ }
2747
+ const verdict = await reviewer({ ...req, classification: decision.classification });
2748
+ if (verdict.approved) {
2749
+ return { armMerge: true, awaitingHuman: false, reason: `reviewer approved${verdict.reason ? `: ${verdict.reason}` : ""}` };
2750
+ }
2751
+ return { armMerge: false, awaitingHuman: false, reason: `reviewer blocked${verdict.reason ? `: ${verdict.reason}` : ""}` };
2752
+ }
2753
+
2754
+ // src/lib/coding/coding-loop.ts
2755
+ var DIFF_CAP = 60 * 1024;
2756
+ function taskIdFromIssue(issue) {
2757
+ const num = String(issue.number).replace(/\D/g, "");
2758
+ return `issue-${num || "x"}`;
2759
+ }
2760
+ async function runCodingTask(input, deps) {
2761
+ const shell = deps.shell ?? (async (cmd, t) => {
2762
+ const r = await runApeShell(cmd, t);
2763
+ return { stdout: r.stdout, stderr: r.stderr, exit_code: r.exit_code };
2764
+ });
2765
+ const loop = deps.runLoopImpl ?? runLoop;
2766
+ const log = deps.log ?? (() => {
2767
+ });
2768
+ const branch = buildBranchName(input.issue, deps.branchNaming);
2769
+ const taskId = taskIdFromIssue(input.issue);
2770
+ const worktree = worktreePathFor(taskId);
2771
+ log(`[coding] creating worktree ${worktree} on ${branch}`);
2772
+ const wt = await shell(buildCreateCommand(input.repo, taskId, branch));
2773
+ if (wt.exit_code !== 0) {
2774
+ return { branch, worktree, runStatus: "error", changedFiles: [], outcome: "run-failed", reason: `worktree create failed: ${wt.stderr.slice(0, 300)}` };
2775
+ }
2776
+ const policy = deps.resolvePolicy ? await deps.resolvePolicy(worktree) : deps.policy ?? SECURE_DEFAULT_POLICY;
2777
+ const prompt = buildTaskPrompt(input.issue, { persona: deps.persona });
2778
+ const run = await loop({
2779
+ config: deps.runtimeConfig,
2780
+ systemPrompt: deps.persona,
2781
+ userMessage: `${prompt}
2782
+
2783
+ Worktree: ${worktree}`,
2784
+ tools: deps.tools,
2785
+ maxSteps: deps.maxSteps
2786
+ });
2787
+ if (run.status !== "ok") {
2788
+ return { branch, worktree, runStatus: "error", changedFiles: [], outcome: "run-failed", reason: `coding loop errored after ${run.stepCount} steps` };
2789
+ }
2790
+ const namesRes = await shell(`git -C '${worktree}' add -A && git -C '${worktree}' diff --cached --name-only`);
2791
+ const changedFiles = namesRes.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
2792
+ if (changedFiles.length === 0) {
2793
+ return { branch, worktree, runStatus: "ok", changedFiles: [], outcome: "run-failed", reason: "no changes produced \u2014 nothing to PR" };
2794
+ }
2795
+ const diffRes = await shell(`git -C '${worktree}' diff --cached`);
2796
+ const diff = diffRes.stdout.slice(0, DIFF_CAP);
2797
+ const agentRisk = await deps.riskAssessor({ paths: changedFiles, diff });
2798
+ const decision = decideMerge(changedFiles, policy, agentRisk);
2799
+ log(`[coding] decision=${decision.classification} (${decision.reason})`);
2800
+ await shell(`git -C '${worktree}' commit -m ${shqMsg(input.issue)} && git -C '${worktree}' push -u origin '${branch}'`);
2801
+ const prCmd = buildPrCreate({ forge: input.forge, title: prTitle(input.issue), body: prBody(input.issue), head: branch });
2802
+ const prRes = await shell(`cd '${worktree}' && ${prCmd}`);
2803
+ const prRef = prRes.stdout.match(/\/pull\/(\d+)|!(\d+)|\bpr\/(\d+)/i)?.slice(1).find(Boolean) ?? branch;
2804
+ if (decision.needsHuman) {
2805
+ return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "awaiting-human", prRef, reason: decision.reason };
2806
+ }
2807
+ const gate = await gateMerge(decision, { prRef, diff }, deps.reviewer);
2808
+ if (!gate.armMerge) {
2809
+ return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "reviewer-blocked", prRef, reason: gate.reason };
2810
+ }
2811
+ const mergeCmd = buildPrMerge({ forge: input.forge, ref: prRef, auto: true, squash: deps.squash, deleteBranch: true });
2812
+ await shell(`cd '${worktree}' && ${mergeCmd}`);
2813
+ return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "auto-armed", prRef, reason: gate.reason };
2814
+ }
2815
+ function prTitle(issue) {
2816
+ return `${issue.type ?? "fix"}: ${issue.title} (#${String(issue.number).replace(/\D/g, "")})`;
2817
+ }
2818
+ function prBody(issue) {
2819
+ return `Resolves #${String(issue.number).replace(/\D/g, "")}.
2820
+
2821
+ Automated by the OpenApe coding agent.`;
2822
+ }
2823
+ function shqMsg(issue) {
2824
+ return `'${prTitle(issue).replace(/'/g, "'\\''")}'`;
2825
+ }
2826
+
2827
+ // src/lib/coding/llm-review.ts
2828
+ var DIFF_CAP2 = 48 * 1024;
2829
+ async function jsonCompletion(config, system, user, fetchImpl = fetch) {
2830
+ try {
2831
+ const res = await fetchImpl(`${config.apiBase}/chat/completions`, {
2832
+ method: "POST",
2833
+ headers: { "authorization": `Bearer ${config.apiKey}`, "content-type": "application/json" },
2834
+ body: JSON.stringify({
2835
+ model: config.model,
2836
+ messages: [{ role: "system", content: system }, { role: "user", content: user }],
2837
+ response_format: { type: "json_object" }
2838
+ })
2839
+ });
2840
+ if (!res.ok) return null;
2841
+ const data = await res.json();
2842
+ const content = data.choices?.[0]?.message?.content;
2843
+ if (!content) return null;
2844
+ return JSON.parse(content);
2845
+ } catch {
2846
+ return null;
2847
+ }
2848
+ }
2849
+ var RISK_SYSTEM = [
2850
+ "You are a security/risk classifier for an autonomous coding agent.",
2851
+ "Given a diff + changed file paths, decide whether merging it WITHOUT a human is risky.",
2852
+ "Risky = touches authentication, authorization, secrets/credentials, payment, data migrations,",
2853
+ "deploy/release/CI config, cryptography, deletion of data, or anything whose failure is hard to",
2854
+ "reverse in production. Routine code/tests/docs/refactors are NOT risky.",
2855
+ 'Respond ONLY as JSON: {"risky": boolean, "reason": string}.'
2856
+ ].join(" ");
2857
+ function createLlmRiskAssessor(config, fetchImpl) {
2858
+ return async ({ paths, diff }) => {
2859
+ const user = `Changed files:
2860
+ ${paths.join("\n")}
2861
+
2862
+ Diff (truncated):
2863
+ ${diff.slice(0, DIFF_CAP2)}`;
2864
+ const out = await jsonCompletion(config, RISK_SYSTEM, user, fetchImpl);
2865
+ if (!out || typeof out.risky !== "boolean") {
2866
+ return { risky: true, reason: "risk classifier unavailable/unparseable \u2014 treating as risky (fail-safe)" };
2867
+ }
2868
+ return { risky: out.risky, reason: typeof out.reason === "string" ? out.reason : void 0 };
2869
+ };
2870
+ }
2871
+ var REVIEW_SYSTEM = [
2872
+ "You are a code reviewer for an autonomous coding agent.",
2873
+ "Given a PR diff, decide whether it is correct, safe, and complete enough to auto-merge.",
2874
+ "Approve only if you would be comfortable shipping it without further human review.",
2875
+ "Block if you see bugs, missing tests, security issues, or incomplete work.",
2876
+ 'Respond ONLY as JSON: {"approved": boolean, "reason": string}.'
2877
+ ].join(" ");
2878
+ function createLlmReviewer(config, fetchImpl) {
2879
+ return async ({ prRef, diff }) => {
2880
+ const user = `PR ${String(prRef)} diff (truncated):
2881
+ ${diff.slice(0, DIFF_CAP2)}`;
2882
+ const out = await jsonCompletion(config, REVIEW_SYSTEM, user, fetchImpl);
2883
+ if (!out || typeof out.approved !== "boolean") {
2884
+ return { approved: false, reason: "reviewer unavailable/unparseable \u2014 blocking (fail-safe)" };
2885
+ }
2886
+ return { approved: out.approved, reason: typeof out.reason === "string" ? out.reason : void 0 };
2887
+ };
2888
+ }
2889
+
2890
+ // src/lib/coding/derive-policy.ts
2891
+ function indentOf(line) {
2892
+ return line.length - line.trimStart().length;
2893
+ }
2894
+ function unquote(s) {
2895
+ return s.trim().replace(/^['"]|['"]$/g, "").trim();
2896
+ }
2897
+ function extractWorkflowPaths(yamlText) {
2898
+ const out = [];
2899
+ const lines = yamlText.split("\n");
2900
+ let inBlock = false;
2901
+ let keyIndent = -1;
2902
+ for (const line of lines) {
2903
+ if (inBlock) {
2904
+ const m = /^\s*-\s*(\S.*)$/.exec(line);
2905
+ if (m && indentOf(line) > keyIndent) {
2906
+ out.push(unquote(m[1]));
2907
+ continue;
2908
+ }
2909
+ if (line.trim() !== "") inBlock = false;
2910
+ }
2911
+ const inline = /^\s*paths:\s*\[(.+)\]\s*$/.exec(line);
2912
+ if (inline) {
2913
+ for (const tok of inline[1].split(",")) {
2914
+ const g = unquote(tok);
2915
+ if (g) out.push(g);
2916
+ }
2917
+ continue;
2918
+ }
2919
+ if (/^\s*paths:\s*$/.test(line)) {
2920
+ inBlock = true;
2921
+ keyIndent = indentOf(line);
2922
+ }
2923
+ }
2924
+ return [...new Set(out)];
2925
+ }
2926
+ function parseCodeowners(text) {
2927
+ const out = [];
2928
+ for (const raw of text.split("\n")) {
2929
+ const line = raw.trim();
2930
+ if (!line || line.startsWith("#")) continue;
2931
+ const pattern = line.split(/\s+/)[0];
2932
+ if (!pattern || pattern.startsWith("@")) continue;
2933
+ let g = pattern.replace(/^\//, "");
2934
+ if (g.endsWith("/")) g += "**";
2935
+ if (g) out.push(g);
2936
+ }
2937
+ return [...new Set(out)];
2938
+ }
2939
+ async function deriveRiskGlobs(worktreeDir) {
2940
+ const { readFile, readdir } = await import("fs/promises");
2941
+ const { join: join20 } = await import("path");
2942
+ const globs = /* @__PURE__ */ new Set();
2943
+ try {
2944
+ const wfDir = join20(worktreeDir, ".github", "workflows");
2945
+ const files = await readdir(wfDir);
2946
+ for (const f of files) {
2947
+ if (!/deploy.*\.ya?ml$/i.test(f)) continue;
2948
+ try {
2949
+ const text = await readFile(join20(wfDir, f), "utf8");
2950
+ for (const g of extractWorkflowPaths(text)) globs.add(g);
2951
+ } catch {
2952
+ }
2953
+ }
2954
+ } catch {
2955
+ }
2956
+ for (const loc of [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"]) {
2957
+ try {
2958
+ const text = await readFile(join20(worktreeDir, loc), "utf8");
2959
+ for (const g of parseCodeowners(text)) globs.add(g);
2960
+ break;
2961
+ } catch {
2962
+ }
2963
+ }
2964
+ return [...globs];
2965
+ }
2966
+ async function resolveMergePolicy(worktreeDir) {
2967
+ const explicit = await loadMergePolicy(worktreeDir).catch(() => SECURE_DEFAULT_POLICY);
2968
+ const derived = await deriveRiskGlobs(worktreeDir).catch(() => []);
2969
+ return {
2970
+ autoMergeEnabled: explicit.autoMergeEnabled,
2971
+ autoPaths: explicit.autoPaths,
2972
+ riskPaths: [.../* @__PURE__ */ new Set([...explicit.riskPaths, ...derived])]
2973
+ };
2974
+ }
2975
+
2976
+ // src/commands/agents/code.ts
2977
+ var CliError2 = class extends Error {
2978
+ };
2979
+ var CODING_TOOLS = ["file.read", "file.write", "file.edit", "bash", "git.worktree", "verify", "forge.issue.get", "forge.pr.status"];
2980
+ var DEFAULT_PERSONA = [
2981
+ "You are an autonomous coding agent. You implement an assigned issue in",
2982
+ "the provided git worktree: read the relevant code, make small targeted",
2983
+ "edits with file.edit, and make the repo verification command pass via",
2984
+ "the verify tool. Do not open or merge PRs \u2014 the orchestrator does that.",
2985
+ "No change is done until verify is green."
2986
+ ].join(" ");
2987
+ function readLitellmConfig(model) {
2988
+ const env = {};
2989
+ const envPath = join2(homedir4(), "litellm", ".env");
2990
+ if (existsSync4(envPath)) {
2991
+ for (const raw of readFileSync3(envPath, "utf8").split("\n")) {
2992
+ const line = raw.trim();
2993
+ const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);
2994
+ if (m) env[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
2995
+ }
2996
+ }
2997
+ for (const k of ["LITELLM_API_KEY", "LITELLM_MASTER_KEY", "LITELLM_BASE_URL"]) {
2998
+ if (process2.env[k]) env[k] = process2.env[k];
2999
+ }
3000
+ const apiKey = env.LITELLM_API_KEY || env.LITELLM_MASTER_KEY;
3001
+ const apiBase = (env.LITELLM_BASE_URL || "http://127.0.0.1:4000/v1").replace(/\/$/, "");
3002
+ if (!apiKey) throw new CliError2("No LITELLM_API_KEY / LITELLM_MASTER_KEY in ~/litellm/.env or env.");
3003
+ return { apiBase, apiKey, model: model || process2.env.APE_CHAT_BRIDGE_MODEL || "claude-haiku-4-5" };
3004
+ }
3005
+ function readPersona(file) {
3006
+ if (file && existsSync4(file)) return readFileSync3(file, "utf8");
3007
+ const agentJson = join2(homedir4(), ".openape", "agent", "agent.json");
3008
+ if (existsSync4(agentJson)) {
3009
+ try {
3010
+ const p = JSON.parse(readFileSync3(agentJson, "utf8"));
3011
+ if (p.systemPrompt?.trim()) return p.systemPrompt;
3012
+ } catch {
3013
+ }
3014
+ }
3015
+ return DEFAULT_PERSONA;
3016
+ }
3017
+ async function fetchIssue(forge, ref) {
3018
+ const res = await runApeShell(buildIssueGet(forge, ref));
3019
+ if (res.exit_code !== 0) throw new CliError2(`could not fetch issue ${ref}: ${res.stderr.slice(0, 200)}`);
3020
+ const j = JSON.parse(res.stdout);
3021
+ const number = j.number ?? j.id ?? Number(ref);
3022
+ const title = j.title ?? j.fields?.["System.Title"] ?? `issue ${ref}`;
3023
+ return { number, title, body: j.body };
3024
+ }
3025
+ var codeAgentCommand = defineCommand24({
3026
+ meta: {
3027
+ name: "code",
3028
+ description: "Run a coding task: issue to worktree to edit to verify to PR (policy-gated merge). The agent never self-merges."
3029
+ },
3030
+ args: {
3031
+ "issue": { type: "string", description: "Issue / work-item ref to work on." },
3032
+ "repo": { type: "string", description: "Git remote URL of the target repo.", required: true },
3033
+ "forge": { type: "string", description: "github | azure | registered forge. Auto-detected from --repo if omitted." },
3034
+ "model": { type: "string", description: "Override LLM model." },
3035
+ "max-steps": { type: "string", description: "Max tool-call rounds (default 40)." },
3036
+ "persona-file": { type: "string", description: "File with the agent persona/system prompt." },
3037
+ "poll-label": { type: "string", description: "Poll mode: work all open issues with this label." }
3038
+ },
3039
+ async run({ args }) {
3040
+ const repo = args.repo;
3041
+ const forge = args.forge || detectForge(repo);
3042
+ const config = readLitellmConfig(args.model);
3043
+ const persona = readPersona(args["persona-file"]);
3044
+ const maxSteps = Number(args["max-steps"]) > 0 ? Number(args["max-steps"]) : 40;
3045
+ const tools = taskTools(CODING_TOOLS);
3046
+ const deps = {
3047
+ runtimeConfig: config,
3048
+ tools,
3049
+ persona,
3050
+ maxSteps,
3051
+ resolvePolicy: (worktree) => resolveMergePolicy(worktree),
3052
+ reviewer: createLlmReviewer(config),
3053
+ riskAssessor: createLlmRiskAssessor(config),
3054
+ log: (l) => consola21.info(l)
3055
+ };
3056
+ const refs = [];
3057
+ if (args["poll-label"]) {
3058
+ const slug = repo.replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
3059
+ const list = await runApeShell(`gh issue list --repo ${slug} --label '${args["poll-label"]}' --state open --json number --jq '.[].number'`);
3060
+ refs.push(...list.stdout.split("\n").map((s) => s.trim()).filter(Boolean));
3061
+ if (refs.length === 0) {
3062
+ consola21.info("no open issues with that label");
3063
+ return;
3064
+ }
3065
+ } else if (args.issue) {
3066
+ refs.push(args.issue);
3067
+ } else {
3068
+ throw new CliError2("provide --issue <ref> or --poll-label <label>");
3069
+ }
3070
+ for (const ref of refs) {
3071
+ const issue = await fetchIssue(forge, ref);
3072
+ consola21.start(`coding issue #${issue.number}: ${issue.title}`);
3073
+ const result = await runCodingTask({ issue, repo, forge }, deps);
3074
+ consola21.box(`#${issue.number} -> ${result.outcome}
3075
+ PR: ${result.prRef ?? "(none)"}
3076
+ ${result.reason}`);
3077
+ }
3078
+ }
3079
+ });
3080
+
2622
3081
  // src/commands/agents/destroy.ts
2623
3082
  import { execFileSync as execFileSync6 } from "child_process";
2624
3083
  import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
2625
3084
  import { tmpdir, userInfo } from "os";
2626
- import { join as join3 } from "path";
2627
- import { defineCommand as defineCommand24 } from "citty";
2628
- import consola21 from "consola";
3085
+ import { join as join4 } from "path";
3086
+ import { defineCommand as defineCommand25 } from "citty";
3087
+ import consola22 from "consola";
2629
3088
 
2630
3089
  // src/lib/nest-registry.ts
2631
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
2632
- import { homedir as homedir4 } from "os";
2633
- import { join as join2 } from "path";
3090
+ import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
3091
+ import { homedir as homedir5 } from "os";
3092
+ import { join as join3 } from "path";
2634
3093
  function resolveRegistryPath() {
2635
- if (existsSync4("/var/openape/nest/agents.json")) return "/var/openape/nest/agents.json";
2636
- if (existsSync4("/var/openape/nest")) return "/var/openape/nest/agents.json";
2637
- return join2(homedir4(), ".openape", "nest", "agents.json");
3094
+ if (existsSync5("/var/openape/nest/agents.json")) return "/var/openape/nest/agents.json";
3095
+ if (existsSync5("/var/openape/nest")) return "/var/openape/nest/agents.json";
3096
+ return join3(homedir5(), ".openape", "nest", "agents.json");
2638
3097
  }
2639
3098
  function emptyRegistry() {
2640
3099
  return { version: 1, agents: [] };
2641
3100
  }
2642
3101
  function readNestRegistry() {
2643
3102
  const path2 = resolveRegistryPath();
2644
- if (!existsSync4(path2)) return emptyRegistry();
3103
+ if (!existsSync5(path2)) return emptyRegistry();
2645
3104
  try {
2646
- const parsed = JSON.parse(readFileSync3(path2, "utf8"));
3105
+ const parsed = JSON.parse(readFileSync4(path2, "utf8"));
2647
3106
  if (parsed?.version !== 1 || !Array.isArray(parsed.agents)) return emptyRegistry();
2648
3107
  return parsed;
2649
3108
  } catch {
@@ -2731,7 +3190,7 @@ function readPasswordSilent(prompt) {
2731
3190
  }
2732
3191
 
2733
3192
  // src/commands/agents/destroy.ts
2734
- var destroyAgentCommand = defineCommand24({
3193
+ var destroyAgentCommand = defineCommand25({
2735
3194
  meta: {
2736
3195
  name: "destroy",
2737
3196
  description: "Tear down an agent: remove macOS user, hard-delete IdP agent, drop all SSH keys"
@@ -2773,7 +3232,7 @@ var destroyAgentCommand = defineCommand24({
2773
3232
  const resolved = lookupMacOSUserForAgent(name);
2774
3233
  const macOSUsername = resolved?.name ?? macOSUsernameForAgent(name);
2775
3234
  const homeDir = resolved?.homeDir ?? `/var/openape/homes/${macOSUsername}`;
2776
- consola21.start(`Running teardown for ${name} (Phase-G, root-stage)\u2026`);
3235
+ consola22.start(`Running teardown for ${name} (Phase-G, root-stage)\u2026`);
2777
3236
  runPhaseGTeardownInProcess({ name, homeDir, macOSUsername });
2778
3237
  return;
2779
3238
  }
@@ -2791,7 +3250,7 @@ var destroyAgentCommand = defineCommand24({
2791
3250
  const osUser = isDarwin() ? lookupMacOSUserForAgent(name) : null;
2792
3251
  const osUserExists = !args["keep-os-user"] && osUser !== null;
2793
3252
  if (!idpExists && !osUserExists) {
2794
- consola21.info(`Nothing to destroy: no IdP agent and no OS user for "${name}".`);
3253
+ consola22.info(`Nothing to destroy: no IdP agent and no OS user for "${name}".`);
2795
3254
  return;
2796
3255
  }
2797
3256
  if (!args.force) {
@@ -2803,14 +3262,14 @@ var destroyAgentCommand = defineCommand24({
2803
3262
  if (idpExists) {
2804
3263
  consequences.push(args.soft ? `\u2022 Deactivate IdP agent ${idpAgent.email} (PATCH isActive=false)` : `\u2022 Hard-delete IdP agent ${idpAgent.email} and all its SSH keys`);
2805
3264
  }
2806
- consola21.warn(`About to destroy "${name}":
3265
+ consola22.warn(`About to destroy "${name}":
2807
3266
  ${consequences.join("\n")}`);
2808
3267
  if (!process.stdin.isTTY) {
2809
3268
  throw new CliError(
2810
3269
  "No TTY available for the interactive confirmation. Re-run with --force to skip the prompt (this is the same flag CI uses)."
2811
3270
  );
2812
3271
  }
2813
- const confirmed = await consola21.prompt("Proceed?", { type: "confirm", initial: false });
3272
+ const confirmed = await consola22.prompt("Proceed?", { type: "confirm", initial: false });
2814
3273
  if (typeof confirmed === "symbol" || !confirmed) {
2815
3274
  throw new CliExit(0);
2816
3275
  }
@@ -2819,13 +3278,13 @@ ${consequences.join("\n")}`);
2819
3278
  const id = encodeURIComponent(idpAgent.email);
2820
3279
  if (args.soft) {
2821
3280
  await apiFetch(`/api/my-agents/${id}`, { method: "PATCH", body: { isActive: false }, idp });
2822
- consola21.success(`Deactivated IdP agent ${idpAgent.email}`);
3281
+ consola22.success(`Deactivated IdP agent ${idpAgent.email}`);
2823
3282
  } else {
2824
3283
  await apiFetch(`/api/my-agents/${id}`, { method: "DELETE", idp });
2825
- consola21.success(`Deleted IdP agent ${idpAgent.email}`);
3284
+ consola22.success(`Deleted IdP agent ${idpAgent.email}`);
2826
3285
  }
2827
3286
  } else {
2828
- consola21.info("No IdP agent to remove (skipped).");
3287
+ consola22.info("No IdP agent to remove (skipped).");
2829
3288
  }
2830
3289
  if (osUserExists) {
2831
3290
  const macOSUsername = osUser?.name ?? macOSUsernameForAgent(name);
@@ -2834,10 +3293,10 @@ ${consequences.join("\n")}`);
2834
3293
  const isPhaseG = homeDir.startsWith("/var/openape/homes/");
2835
3294
  if (isPhaseG) {
2836
3295
  if (process.geteuid?.() === 0) {
2837
- consola21.start("Running teardown (Phase G \u2014 already root, no grant needed)\u2026");
3296
+ consola22.start("Running teardown (Phase G \u2014 already root, no grant needed)\u2026");
2838
3297
  runPhaseGTeardownInProcess({ name, homeDir, macOSUsername });
2839
3298
  } else {
2840
- consola21.start("Running teardown (Phase G \u2014 no admin password needed)\u2026");
3299
+ consola22.start("Running teardown (Phase G \u2014 no admin password needed)\u2026");
2841
3300
  execFileSync6("apes", [
2842
3301
  "run",
2843
3302
  "--as",
@@ -2852,7 +3311,7 @@ ${consequences.join("\n")}`);
2852
3311
  "--root-stage"
2853
3312
  ], { stdio: "inherit" });
2854
3313
  }
2855
- consola21.info(`dscl record /Users/${macOSUsername} kept as tombstone (hidden, no home). Run \`sudo apes agents cleanup-orphans\` to sweep accumulated tombstones.`);
3314
+ consola22.info(`dscl record /Users/${macOSUsername} kept as tombstone (hidden, no home). Run \`sudo apes agents cleanup-orphans\` to sweep accumulated tombstones.`);
2856
3315
  } else {
2857
3316
  const sudo = whichBinary("sudo");
2858
3317
  if (!sudo) {
@@ -2865,19 +3324,19 @@ ${consequences.join("\n")}`);
2865
3324
  } catch (err) {
2866
3325
  const headless = !process.stdin.isTTY && !process.env.APES_ADMIN_PASSWORD;
2867
3326
  if (headless) {
2868
- consola21.warn(`Legacy OS teardown for ${name} requires a TTY or APES_ADMIN_PASSWORD; skipping. Run \`apes agents destroy ${name}\` from a shell later to fully clean up /Users/${name} + dscl record.`);
3327
+ consola22.warn(`Legacy OS teardown for ${name} requires a TTY or APES_ADMIN_PASSWORD; skipping. Run \`apes agents destroy ${name}\` from a shell later to fully clean up /Users/${name} + dscl record.`);
2869
3328
  adminPassword = "";
2870
3329
  } else {
2871
3330
  throw err;
2872
3331
  }
2873
3332
  }
2874
3333
  if (adminPassword) {
2875
- const scratch = mkdtempSync(join3(tmpdir(), `apes-destroy-${name}-`));
2876
- const scriptPath = join3(scratch, "teardown.sh");
3334
+ const scratch = mkdtempSync(join4(tmpdir(), `apes-destroy-${name}-`));
3335
+ const scriptPath = join4(scratch, "teardown.sh");
2877
3336
  try {
2878
3337
  const script = buildDestroyTeardownScript({ name, homeDir, adminUser });
2879
3338
  writeFileSync2(scriptPath, script, { mode: 448 });
2880
- consola21.start("Running teardown via sudo\u2026");
3339
+ consola22.start("Running teardown via sudo\u2026");
2881
3340
  execFileSync6(sudo, ["-S", "--prompt=", "--", "bash", scriptPath], {
2882
3341
  input: `${adminPassword}
2883
3342
  ${adminPassword}
@@ -2890,14 +3349,14 @@ ${adminPassword}
2890
3349
  }
2891
3350
  }
2892
3351
  } else if (!args["keep-os-user"] && isDarwin()) {
2893
- consola21.info("No macOS user to remove (skipped).");
3352
+ consola22.info("No macOS user to remove (skipped).");
2894
3353
  }
2895
3354
  try {
2896
3355
  removeNestAgent(name);
2897
3356
  } catch (err) {
2898
- consola21.warn(`Could not update nest registry: ${err instanceof Error ? err.message : String(err)}`);
3357
+ consola22.warn(`Could not update nest registry: ${err instanceof Error ? err.message : String(err)}`);
2899
3358
  }
2900
- consola21.success(`Destroyed ${name}.`);
3359
+ consola22.success(`Destroyed ${name}.`);
2901
3360
  }
2902
3361
  });
2903
3362
  async function collectAdminPassword(opts) {
@@ -2911,9 +3370,9 @@ async function collectAdminPassword(opts) {
2911
3370
  }
2912
3371
 
2913
3372
  // src/commands/agents/list.ts
2914
- import { defineCommand as defineCommand25 } from "citty";
2915
- import consola22 from "consola";
2916
- var listAgentsCommand = defineCommand25({
3373
+ import { defineCommand as defineCommand26 } from "citty";
3374
+ import consola23 from "consola";
3375
+ var listAgentsCommand = defineCommand26({
2917
3376
  meta: {
2918
3377
  name: "list",
2919
3378
  description: "List agents owned by the current user, with local OS-user status"
@@ -2964,7 +3423,7 @@ var listAgentsCommand = defineCommand25({
2964
3423
  return;
2965
3424
  }
2966
3425
  if (rows.length === 0) {
2967
- consola22.info(args["include-inactive"] ? "No agents found." : "No active agents found. Use --include-inactive to show deactivated.");
3426
+ consola23.info(args["include-inactive"] ? "No agents found." : "No active agents found. Use --include-inactive to show deactivated.");
2968
3427
  return;
2969
3428
  }
2970
3429
  const nameW = Math.max(4, ...rows.map((r) => r.name.length));
@@ -2982,10 +3441,10 @@ var listAgentsCommand = defineCommand25({
2982
3441
  });
2983
3442
 
2984
3443
  // src/commands/agents/register.ts
2985
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
2986
- import { defineCommand as defineCommand26 } from "citty";
2987
- import consola23 from "consola";
2988
- var registerAgentCommand = defineCommand26({
3444
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
3445
+ import { defineCommand as defineCommand27 } from "citty";
3446
+ import consola24 from "consola";
3447
+ var registerAgentCommand = defineCommand27({
2989
3448
  meta: {
2990
3449
  name: "register",
2991
3450
  description: "Register an agent at the IdP using a supplied public key"
@@ -3030,10 +3489,10 @@ var registerAgentCommand = defineCommand26({
3030
3489
  throw new CliError("Pass either --public-key or --public-key-file, not both.");
3031
3490
  }
3032
3491
  if (!publicKey && keyFile) {
3033
- if (!existsSync5(keyFile)) {
3492
+ if (!existsSync6(keyFile)) {
3034
3493
  throw new CliError(`Public-key file not found: ${keyFile}`);
3035
3494
  }
3036
- publicKey = readFileSync4(keyFile, "utf-8").trim();
3495
+ publicKey = readFileSync5(keyFile, "utf-8").trim();
3037
3496
  }
3038
3497
  if (!publicKey) {
3039
3498
  throw new CliError('Provide --public-key "<ssh-ed25519 line>" or --public-key-file <path>.');
@@ -3055,7 +3514,7 @@ var registerAgentCommand = defineCommand26({
3055
3514
  `);
3056
3515
  return;
3057
3516
  }
3058
- consola23.success("Agent registered.");
3517
+ consola24.success("Agent registered.");
3059
3518
  console.log(` Name: ${result.name}`);
3060
3519
  console.log(` Email: ${result.email}`);
3061
3520
  console.log(` IdP: ${idp}`);
@@ -3068,28 +3527,28 @@ var registerAgentCommand = defineCommand26({
3068
3527
  });
3069
3528
 
3070
3529
  // src/commands/agents/run.ts
3071
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
3072
- import { homedir as homedir6 } from "os";
3073
- import { join as join5 } from "path";
3074
- import { defineCommand as defineCommand27 } from "citty";
3075
- import consola24 from "consola";
3530
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
3531
+ import { homedir as homedir7 } from "os";
3532
+ import { join as join6 } from "path";
3533
+ import { defineCommand as defineCommand28 } from "citty";
3534
+ import consola25 from "consola";
3076
3535
 
3077
3536
  // src/lib/agent-secrets-runtime.ts
3078
- import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync5, watch } from "fs";
3079
- import { homedir as homedir5 } from "os";
3080
- import { join as join4 } from "path";
3537
+ import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync6, watch } from "fs";
3538
+ import { homedir as homedir6 } from "os";
3539
+ import { join as join5 } from "path";
3081
3540
  import { openString } from "@openape/core";
3082
- var CONFIG_DIR2 = join4(homedir5(), ".config", "openape");
3083
- var SECRETS_DIR = join4(CONFIG_DIR2, "secrets.d");
3084
- var X25519_KEY_PATH = join4(CONFIG_DIR2, "agent-x25519.key");
3541
+ var CONFIG_DIR2 = join5(homedir6(), ".config", "openape");
3542
+ var SECRETS_DIR = join5(CONFIG_DIR2, "secrets.d");
3543
+ var X25519_KEY_PATH = join5(CONFIG_DIR2, "agent-x25519.key");
3085
3544
  function envNameFromFile(file) {
3086
3545
  if (!file.endsWith(".blob")) return null;
3087
3546
  const env = file.slice(0, -".blob".length);
3088
3547
  return /^[A-Z][A-Z0-9_]*$/.test(env) ? env : null;
3089
3548
  }
3090
3549
  function readAgentEncryptionKey(keyPath = X25519_KEY_PATH) {
3091
- if (!existsSync6(keyPath)) return null;
3092
- const k = readFileSync5(keyPath, "utf8").trim();
3550
+ if (!existsSync7(keyPath)) return null;
3551
+ const k = readFileSync6(keyPath, "utf8").trim();
3093
3552
  return k.length > 0 ? k : null;
3094
3553
  }
3095
3554
  function materializeSecrets(opts = {}) {
@@ -3100,12 +3559,12 @@ function materializeSecrets(opts = {}) {
3100
3559
  const applied = [];
3101
3560
  const failed = [];
3102
3561
  const key = readAgentEncryptionKey(opts.keyPath);
3103
- const files = key && existsSync6(dir) ? readdirSync(dir) : [];
3562
+ const files = key && existsSync7(dir) ? readdirSync(dir) : [];
3104
3563
  for (const file of files) {
3105
3564
  const name = envNameFromFile(file);
3106
3565
  if (!name) continue;
3107
3566
  try {
3108
- const box = JSON.parse(readFileSync5(join4(dir, file), "utf8"));
3567
+ const box = JSON.parse(readFileSync6(join5(dir, file), "utf8"));
3109
3568
  env[name] = openString(box, key);
3110
3569
  applied.push(name);
3111
3570
  } catch (e) {
@@ -3132,7 +3591,7 @@ function startSecretsWatcher(opts = {}) {
3132
3591
  appliedNames = new Set(r.applied);
3133
3592
  };
3134
3593
  run();
3135
- if (!existsSync6(dir)) return () => {
3594
+ if (!existsSync7(dir)) return () => {
3136
3595
  };
3137
3596
  let timer = null;
3138
3597
  const watcher = watch(dir, () => {
@@ -3147,13 +3606,13 @@ function startSecretsWatcher(opts = {}) {
3147
3606
  }
3148
3607
 
3149
3608
  // src/commands/agents/run.ts
3150
- var AUTH_PATH = join5(homedir6(), ".config", "apes", "auth.json");
3151
- var TASK_CACHE_DIR = join5(homedir6(), ".openape", "agent", "tasks");
3609
+ var AUTH_PATH = join6(homedir7(), ".config", "apes", "auth.json");
3610
+ var TASK_CACHE_DIR = join6(homedir7(), ".openape", "agent", "tasks");
3152
3611
  function readAuth() {
3153
- if (!existsSync7(AUTH_PATH)) {
3612
+ if (!existsSync8(AUTH_PATH)) {
3154
3613
  throw new CliError(`No agent auth found at ${AUTH_PATH}. Run \`apes agents spawn <name>\` first.`);
3155
3614
  }
3156
- const parsed = JSON.parse(readFileSync6(AUTH_PATH, "utf8"));
3615
+ const parsed = JSON.parse(readFileSync7(AUTH_PATH, "utf8"));
3157
3616
  if (!parsed.access_token) throw new CliError("auth.json missing access_token");
3158
3617
  return parsed;
3159
3618
  }
@@ -3168,7 +3627,7 @@ async function postRunResultToChat(opts) {
3168
3627
  const ownerLower = opts.ownerEmail.toLowerCase();
3169
3628
  const ownerRow = contacts.find((c) => c.peerEmail.toLowerCase() === ownerLower && c.connected && c.roomId);
3170
3629
  if (!ownerRow?.roomId) {
3171
- consola24.info("chat DM skipped \u2014 no active room with owner (accept the contact request in chat to enable)");
3630
+ consola25.info("chat DM skipped \u2014 no active room with owner (accept the contact request in chat to enable)");
3172
3631
  return;
3173
3632
  }
3174
3633
  const prefix = opts.status === "ok" ? "\u2705" : "\u274C";
@@ -3182,33 +3641,33 @@ ${msg}`.slice(0, 9e3);
3182
3641
  body: JSON.stringify({ body })
3183
3642
  });
3184
3643
  if (!postRes.ok) {
3185
- consola24.warn(`chat DM post failed: ${postRes.status}`);
3644
+ consola25.warn(`chat DM post failed: ${postRes.status}`);
3186
3645
  }
3187
3646
  } catch (err) {
3188
- consola24.warn(`chat DM error: ${err.message}`);
3647
+ consola25.warn(`chat DM error: ${err.message}`);
3189
3648
  }
3190
3649
  }
3191
3650
  function readTaskSpec(taskId) {
3192
- const path2 = join5(TASK_CACHE_DIR, `${taskId}.json`);
3193
- if (!existsSync7(path2)) {
3651
+ const path2 = join6(TASK_CACHE_DIR, `${taskId}.json`);
3652
+ if (!existsSync8(path2)) {
3194
3653
  throw new CliError(`No cached task spec at ${path2}. Run \`apes agents sync\` first to pull the task list from troop.`);
3195
3654
  }
3196
- return JSON.parse(readFileSync6(path2, "utf8"));
3655
+ return JSON.parse(readFileSync7(path2, "utf8"));
3197
3656
  }
3198
- var AGENT_CONFIG_PATH = join5(homedir6(), ".openape", "agent", "agent.json");
3657
+ var AGENT_CONFIG_PATH = join6(homedir7(), ".openape", "agent", "agent.json");
3199
3658
  function readAgentConfig() {
3200
- if (!existsSync7(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
3659
+ if (!existsSync8(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
3201
3660
  try {
3202
- return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf8"));
3661
+ return JSON.parse(readFileSync7(AGENT_CONFIG_PATH, "utf8"));
3203
3662
  } catch {
3204
3663
  return { systemPrompt: "" };
3205
3664
  }
3206
3665
  }
3207
- function readLitellmConfig(model) {
3208
- const envPath = join5(homedir6(), "litellm", ".env");
3666
+ function readLitellmConfig2(model) {
3667
+ const envPath = join6(homedir7(), "litellm", ".env");
3209
3668
  const env = {};
3210
- if (existsSync7(envPath)) {
3211
- for (const line of readFileSync6(envPath, "utf8").split(/\r?\n/)) {
3669
+ if (existsSync8(envPath)) {
3670
+ for (const line of readFileSync7(envPath, "utf8").split(/\r?\n/)) {
3212
3671
  const m = line.match(/^([A-Z_]+)=(.*)$/);
3213
3672
  if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
3214
3673
  }
@@ -3223,7 +3682,7 @@ function readLitellmConfig(model) {
3223
3682
  }
3224
3683
  return { apiBase, apiKey, model: model || "claude-haiku-4-5" };
3225
3684
  }
3226
- var runAgentCommand = defineCommand27({
3685
+ var runAgentCommand = defineCommand28({
3227
3686
  meta: {
3228
3687
  name: "run",
3229
3688
  description: "Execute one task (typically launchd-invoked). Reports the run record to troop."
@@ -3246,10 +3705,10 @@ var runAgentCommand = defineCommand27({
3246
3705
  async run({ args }) {
3247
3706
  const taskId = args["task-id"];
3248
3707
  const auth = readAuth();
3249
- materializeSecrets({ log: (l) => consola24.info(l) });
3708
+ materializeSecrets({ log: (l) => consola25.info(l) });
3250
3709
  const spec = readTaskSpec(taskId);
3251
3710
  const agentCfg = readAgentConfig();
3252
- const config = readLitellmConfig(args.model);
3711
+ const config = readLitellmConfig2(args.model);
3253
3712
  let tools;
3254
3713
  try {
3255
3714
  tools = taskTools(spec.tools);
@@ -3258,7 +3717,7 @@ var runAgentCommand = defineCommand27({
3258
3717
  }
3259
3718
  const troop = new TroopClient(resolveTroopUrl(args["troop-url"]), auth.access_token);
3260
3719
  const { id: runId } = await troop.startRun(taskId);
3261
- consola24.info(`Run ${runId} started for task ${taskId}`);
3720
+ consola25.info(`Run ${runId} started for task ${taskId}`);
3262
3721
  try {
3263
3722
  const result = await runLoop({
3264
3723
  config,
@@ -3277,7 +3736,7 @@ var runAgentCommand = defineCommand27({
3277
3736
  step_count: result.stepCount,
3278
3737
  trace: result.trace
3279
3738
  });
3280
- consola24.success(`Run ${runId} ${result.status} (${result.stepCount} steps)`);
3739
+ consola25.success(`Run ${runId} ${result.status} (${result.stepCount} steps)`);
3281
3740
  if (auth.owner_email) {
3282
3741
  await postRunResultToChat({
3283
3742
  authToken: auth.access_token,
@@ -3314,17 +3773,17 @@ var runAgentCommand = defineCommand27({
3314
3773
  });
3315
3774
 
3316
3775
  // src/commands/agents/serve.ts
3317
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
3318
- import { homedir as homedir7 } from "os";
3319
- import { join as join6 } from "path";
3776
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
3777
+ import { homedir as homedir8 } from "os";
3778
+ import { join as join7 } from "path";
3320
3779
  import { createInterface } from "readline";
3321
- import { defineCommand as defineCommand28 } from "citty";
3322
- var AUTH_PATH2 = join6(homedir7(), ".config", "apes", "auth.json");
3323
- function readLitellmConfig2(model) {
3324
- const envPath = join6(homedir7(), "litellm", ".env");
3780
+ import { defineCommand as defineCommand29 } from "citty";
3781
+ var AUTH_PATH2 = join7(homedir8(), ".config", "apes", "auth.json");
3782
+ function readLitellmConfig3(model) {
3783
+ const envPath = join7(homedir8(), "litellm", ".env");
3325
3784
  const env = {};
3326
- if (existsSync8(envPath)) {
3327
- for (const line of readFileSync7(envPath, "utf8").split(/\r?\n/)) {
3785
+ if (existsSync9(envPath)) {
3786
+ for (const line of readFileSync8(envPath, "utf8").split(/\r?\n/)) {
3328
3787
  const m = line.match(/^([A-Z_]+)=(.*)$/);
3329
3788
  if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
3330
3789
  }
@@ -3341,7 +3800,7 @@ function emit(event) {
3341
3800
  process.stdout.write(`${JSON.stringify(event)}
3342
3801
  `);
3343
3802
  }
3344
- var serveAgentCommand = defineCommand28({
3803
+ var serveAgentCommand = defineCommand29({
3345
3804
  meta: {
3346
3805
  name: "serve",
3347
3806
  description: "Long-running stdio RPC server for chat-bridge subprocess use."
@@ -3356,9 +3815,9 @@ var serveAgentCommand = defineCommand28({
3356
3815
  if (!args.rpc) {
3357
3816
  throw new CliError("apes agents serve currently only supports --rpc mode");
3358
3817
  }
3359
- if (existsSync8(AUTH_PATH2)) {
3818
+ if (existsSync9(AUTH_PATH2)) {
3360
3819
  try {
3361
- JSON.parse(readFileSync7(AUTH_PATH2, "utf8"));
3820
+ JSON.parse(readFileSync8(AUTH_PATH2, "utf8"));
3362
3821
  } catch {
3363
3822
  }
3364
3823
  }
@@ -3396,7 +3855,7 @@ var serveAgentCommand = defineCommand28({
3396
3855
  }
3397
3856
  });
3398
3857
  async function handleInbound(msg, sessions) {
3399
- const config = readLitellmConfig2(msg.model);
3858
+ const config = readLitellmConfig3(msg.model);
3400
3859
  const tools = taskTools(msg.tools ?? []);
3401
3860
  const maxSteps = msg.max_steps ?? 10;
3402
3861
  let session = sessions.get(msg.session_id);
@@ -3441,9 +3900,9 @@ async function handleInbound(msg, sessions) {
3441
3900
  import { execFileSync as execFileSync8 } from "child_process";
3442
3901
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
3443
3902
  import { tmpdir as tmpdir2 } from "os";
3444
- import { join as join8 } from "path";
3445
- import { defineCommand as defineCommand29 } from "citty";
3446
- import consola25 from "consola";
3903
+ import { join as join9 } from "path";
3904
+ import { defineCommand as defineCommand30 } from "citty";
3905
+ import consola26 from "consola";
3447
3906
 
3448
3907
  // src/lib/troop-bootstrap.ts
3449
3908
  var SYNC_LABEL_PREFIX = "openape.troop.sync";
@@ -3499,13 +3958,13 @@ ${envBlock} <key>StartInterval</key>
3499
3958
 
3500
3959
  // src/lib/keygen.ts
3501
3960
  import { Buffer as Buffer4 } from "buffer";
3502
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
3961
+ import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
3503
3962
  import { generateKeyPairSync } from "crypto";
3504
- import { homedir as homedir8 } from "os";
3963
+ import { homedir as homedir9 } from "os";
3505
3964
  import { dirname, resolve as resolve2 } from "path";
3506
3965
  import { generateX25519KeyPair } from "@openape/core";
3507
3966
  function resolveKeyPath(p) {
3508
- return resolve2(p.replace(/^~/, homedir8()));
3967
+ return resolve2(p.replace(/^~/, homedir9()));
3509
3968
  }
3510
3969
  function buildSshEd25519Line(rawPub) {
3511
3970
  const keyTypeStr = "ssh-ed25519";
@@ -3518,10 +3977,10 @@ function buildSshEd25519Line(rawPub) {
3518
3977
  }
3519
3978
  function readPublicKey(keyPath) {
3520
3979
  const pubPath = `${keyPath}.pub`;
3521
- if (existsSync9(pubPath)) {
3522
- return readFileSync8(pubPath, "utf-8").trim();
3980
+ if (existsSync10(pubPath)) {
3981
+ return readFileSync9(pubPath, "utf-8").trim();
3523
3982
  }
3524
- const keyContent = readFileSync8(keyPath, "utf-8");
3983
+ const keyContent = readFileSync9(keyPath, "utf-8");
3525
3984
  const privateKey = loadEd25519PrivateKey(keyContent);
3526
3985
  const jwk = privateKey.export({ format: "jwk" });
3527
3986
  const pubBytes = Buffer4.from(jwk.x, "base64url");
@@ -3530,7 +3989,7 @@ function readPublicKey(keyPath) {
3530
3989
  function generateAndSaveKey(keyPath) {
3531
3990
  const resolved = resolveKeyPath(keyPath);
3532
3991
  const dir = dirname(resolved);
3533
- if (!existsSync9(dir)) {
3992
+ if (!existsSync10(dir)) {
3534
3993
  mkdirSync2(dir, { recursive: true });
3535
3994
  }
3536
3995
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
@@ -3559,14 +4018,14 @@ function generateKeyPairInMemory() {
3559
4018
 
3560
4019
  // src/lib/llm-bridge.ts
3561
4020
  import { execFileSync as execFileSync7 } from "child_process";
3562
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
3563
- import { homedir as homedir9 } from "os";
3564
- import { dirname as dirname2, join as join7 } from "path";
4021
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
4022
+ import { homedir as homedir10 } from "os";
4023
+ import { dirname as dirname2, join as join8 } from "path";
3565
4024
  var PLIST_LABEL_PREFIX = "eco.hofmann.apes.bridge";
3566
- function readLitellmEnv(envPath = join7(homedir9(), "litellm", ".env")) {
3567
- if (!existsSync10(envPath)) return null;
4025
+ function readLitellmEnv(envPath = join8(homedir10(), "litellm", ".env")) {
4026
+ if (!existsSync11(envPath)) return null;
3568
4027
  try {
3569
- const text = readFileSync9(envPath, "utf8");
4028
+ const text = readFileSync10(envPath, "utf8");
3570
4029
  const out = {};
3571
4030
  for (const line of text.split("\n")) {
3572
4031
  const trimmed = line.trim();
@@ -3703,7 +4162,7 @@ function readMacOSUidOrNull(name) {
3703
4162
  return null;
3704
4163
  }
3705
4164
  }
3706
- var spawnAgentCommand = defineCommand29({
4165
+ var spawnAgentCommand = defineCommand30({
3707
4166
  meta: {
3708
4167
  name: "spawn",
3709
4168
  description: "Provision a local macOS agent end-to-end (OS user, keypair, IdP agent, Claude hook)"
@@ -3791,15 +4250,15 @@ and try again.`
3791
4250
  throw new CliError(`macOS user "${existing.name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
3792
4251
  }
3793
4252
  const homeDir = `/var/openape/homes/${macOSUsername}`;
3794
- const scratch = mkdtempSync2(join8(tmpdir2(), `apes-spawn-${name}-`));
3795
- const scriptPath = join8(scratch, "setup.sh");
4253
+ const scratch = mkdtempSync2(join9(tmpdir2(), `apes-spawn-${name}-`));
4254
+ const scriptPath = join9(scratch, "setup.sh");
3796
4255
  try {
3797
- consola25.start(`Generating keypair for ${name}\u2026`);
4256
+ consola26.start(`Generating keypair for ${name}\u2026`);
3798
4257
  const { privatePem, publicSshLine, x25519PrivateKey, x25519PublicKey } = generateKeyPairInMemory();
3799
- consola25.start(`Registering agent at ${idp}\u2026`);
4258
+ consola26.start(`Registering agent at ${idp}\u2026`);
3800
4259
  const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
3801
- consola25.success(`Registered as ${registration.email}`);
3802
- consola25.start("Issuing agent access token\u2026");
4260
+ consola26.success(`Registered as ${registration.email}`);
4261
+ consola26.start("Issuing agent access token\u2026");
3803
4262
  const { token, expiresIn } = await issueAgentToken({
3804
4263
  idp,
3805
4264
  agentEmail: registration.email,
@@ -3869,11 +4328,11 @@ and try again.`
3869
4328
  writeFileSync4(scriptPath, script, { mode: 448 });
3870
4329
  const alreadyRoot = process.getuid?.() === 0;
3871
4330
  if (alreadyRoot) {
3872
- consola25.start("Running privileged setup directly (already root)\u2026");
4331
+ consola26.start("Running privileged setup directly (already root)\u2026");
3873
4332
  execFileSync8("bash", [scriptPath], { stdio: "inherit" });
3874
4333
  } else {
3875
- consola25.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
3876
- consola25.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
4334
+ consola26.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
4335
+ consola26.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
3877
4336
  execFileSync8(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
3878
4337
  }
3879
4338
  try {
@@ -3891,13 +4350,13 @@ and try again.`
3891
4350
  } : void 0
3892
4351
  });
3893
4352
  } catch (err) {
3894
- consola25.warn(`Could not write to nest registry: ${err instanceof Error ? err.message : String(err)}`);
4353
+ consola26.warn(`Could not write to nest registry: ${err instanceof Error ? err.message : String(err)}`);
3895
4354
  }
3896
- consola25.success(`Agent ${name} spawned.`);
3897
- consola25.info(`\u{1F517} Troop: https://troop.openape.ai/agents/${name}`);
4355
+ consola26.success(`Agent ${name} spawned.`);
4356
+ consola26.info(`\u{1F517} Troop: https://troop.openape.ai/agents/${name}`);
3898
4357
  if (withBridge) {
3899
- consola25.info(`On first boot, the bridge will send you a contact request from ${registration.email}.`);
3900
- consola25.info("Open chat.openape.ai and accept it to start chatting with the agent.");
4358
+ consola26.info(`On first boot, the bridge will send you a contact request from ${registration.email}.`);
4359
+ consola26.info("Open chat.openape.ai and accept it to start chatting with the agent.");
3901
4360
  }
3902
4361
  console.log("");
3903
4362
  console.log("Run as the agent with:");
@@ -3931,11 +4390,11 @@ async function resolveClaudeToken(opts) {
3931
4390
  }
3932
4391
 
3933
4392
  // src/commands/agents/sync.ts
3934
- import { chownSync, existsSync as existsSync11, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync10, rmSync as rmSync4, statSync, writeFileSync as writeFileSync5 } from "fs";
3935
- import { homedir as homedir10 } from "os";
3936
- import { join as join9 } from "path";
3937
- import { defineCommand as defineCommand30 } from "citty";
3938
- import consola26 from "consola";
4393
+ import { chownSync, existsSync as existsSync12, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync11, rmSync as rmSync4, statSync, writeFileSync as writeFileSync5 } from "fs";
4394
+ import { homedir as homedir11 } from "os";
4395
+ import { join as join10 } from "path";
4396
+ import { defineCommand as defineCommand31 } from "citty";
4397
+ import consola27 from "consola";
3939
4398
 
3940
4399
  // src/lib/macos-host.ts
3941
4400
  import { execFileSync as execFileSync9 } from "child_process";
@@ -3962,15 +4421,15 @@ function getHostname() {
3962
4421
  }
3963
4422
 
3964
4423
  // src/commands/agents/sync.ts
3965
- var AUTH_PATH3 = join9(homedir10(), ".config", "apes", "auth.json");
3966
- var TASK_CACHE_DIR2 = join9(homedir10(), ".openape", "agent", "tasks");
4424
+ var AUTH_PATH3 = join10(homedir11(), ".config", "apes", "auth.json");
4425
+ var TASK_CACHE_DIR2 = join10(homedir11(), ".openape", "agent", "tasks");
3967
4426
  function readAuthJson() {
3968
- if (!existsSync11(AUTH_PATH3)) {
4427
+ if (!existsSync12(AUTH_PATH3)) {
3969
4428
  throw new CliError(
3970
4429
  `No agent auth found at ${AUTH_PATH3}. Run \`apes agents spawn <name>\` to provision an agent first.`
3971
4430
  );
3972
4431
  }
3973
- const raw = readFileSync10(AUTH_PATH3, "utf8");
4432
+ const raw = readFileSync11(AUTH_PATH3, "utf8");
3974
4433
  let parsed;
3975
4434
  try {
3976
4435
  parsed = JSON.parse(raw);
@@ -3994,7 +4453,7 @@ function agentNameFromEmail(email) {
3994
4453
  }
3995
4454
  return before.slice(0, dashIdx);
3996
4455
  }
3997
- var syncAgentCommand = defineCommand30({
4456
+ var syncAgentCommand = defineCommand31({
3998
4457
  meta: {
3999
4458
  name: "sync",
4000
4459
  description: "Pull this agent's task list from troop.openape.ai and reconcile launchd plists"
@@ -4018,22 +4477,22 @@ var syncAgentCommand = defineCommand30({
4018
4477
  if (!auth.owner_email) {
4019
4478
  throw new CliError(`${AUTH_PATH3} is missing owner_email \u2014 re-run \`apes agents spawn\` to update.`);
4020
4479
  }
4021
- consola26.start(`Syncing ${agentName} (${host}, hostId ${hostId.slice(0, 8)}\u2026) with ${troopUrl}`);
4480
+ consola27.start(`Syncing ${agentName} (${host}, hostId ${hostId.slice(0, 8)}\u2026) with ${troopUrl}`);
4022
4481
  const sync = await client.sync({
4023
4482
  hostname: host,
4024
4483
  hostId,
4025
4484
  ownerEmail: auth.owner_email
4026
4485
  });
4027
- consola26.info(sync.first_sync ? "\u2713 first sync \u2014 agent registered" : "\u2713 presence updated");
4486
+ consola27.info(sync.first_sync ? "\u2713 first sync \u2014 agent registered" : "\u2713 presence updated");
4028
4487
  const { system_prompt: systemPrompt, tools, skills, tasks } = await client.listTasks();
4029
- consola26.info(`Pulled ${tasks.length} task${tasks.length === 1 ? "" : "s"}`);
4030
- consola26.info(`Tools enabled: ${tools.length === 0 ? "(none)" : tools.join(", ")}`);
4031
- consola26.info(`Skills: ${skills.length === 0 ? "(none)" : skills.map((s) => s.name).join(", ")}`);
4488
+ consola27.info(`Pulled ${tasks.length} task${tasks.length === 1 ? "" : "s"}`);
4489
+ consola27.info(`Tools enabled: ${tools.length === 0 ? "(none)" : tools.join(", ")}`);
4490
+ consola27.info(`Skills: ${skills.length === 0 ? "(none)" : skills.map((s) => s.name).join(", ")}`);
4032
4491
  let agentUid = null;
4033
4492
  let agentGid = null;
4034
4493
  if (process.geteuid?.() === 0) {
4035
4494
  try {
4036
- const homeStat = statSync(homedir10());
4495
+ const homeStat = statSync(homedir11());
4037
4496
  agentUid = homeStat.uid;
4038
4497
  agentGid = homeStat.gid;
4039
4498
  } catch {
@@ -4047,11 +4506,11 @@ var syncAgentCommand = defineCommand30({
4047
4506
  }
4048
4507
  }
4049
4508
  }
4050
- const agentDir = join9(homedir10(), ".openape", "agent");
4509
+ const agentDir = join10(homedir11(), ".openape", "agent");
4051
4510
  mkdirSync3(agentDir, { recursive: true });
4052
- chownToAgent(join9(homedir10(), ".openape"));
4511
+ chownToAgent(join10(homedir11(), ".openape"));
4053
4512
  chownToAgent(agentDir);
4054
- const agentJsonPath = join9(agentDir, "agent.json");
4513
+ const agentJsonPath = join10(agentDir, "agent.json");
4055
4514
  writeFileSync5(
4056
4515
  agentJsonPath,
4057
4516
  `${JSON.stringify({ systemPrompt, tools }, null, 2)}
@@ -4062,12 +4521,12 @@ var syncAgentCommand = defineCommand30({
4062
4521
  mkdirSync3(TASK_CACHE_DIR2, { recursive: true });
4063
4522
  chownToAgent(TASK_CACHE_DIR2);
4064
4523
  for (const task of tasks) {
4065
- const path2 = join9(TASK_CACHE_DIR2, `${task.taskId}.json`);
4524
+ const path2 = join10(TASK_CACHE_DIR2, `${task.taskId}.json`);
4066
4525
  writeFileSync5(path2, `${JSON.stringify(task, null, 2)}
4067
4526
  `, { mode: 384 });
4068
4527
  chownToAgent(path2);
4069
4528
  }
4070
- const skillsDir = join9(agentDir, "skills");
4529
+ const skillsDir = join10(agentDir, "skills");
4071
4530
  mkdirSync3(skillsDir, { recursive: true });
4072
4531
  chownToAgent(skillsDir);
4073
4532
  const incomingNames = new Set(skills.map((s) => s.name));
@@ -4075,30 +4534,30 @@ var syncAgentCommand = defineCommand30({
4075
4534
  for (const entry of readdirSync2(skillsDir)) {
4076
4535
  if (incomingNames.has(entry)) continue;
4077
4536
  try {
4078
- rmSync4(join9(skillsDir, entry), { recursive: true, force: true });
4537
+ rmSync4(join10(skillsDir, entry), { recursive: true, force: true });
4079
4538
  } catch {
4080
4539
  }
4081
4540
  }
4082
4541
  } catch {
4083
4542
  }
4084
4543
  for (const skill of skills) {
4085
- const skillDir = join9(skillsDir, skill.name);
4544
+ const skillDir = join10(skillsDir, skill.name);
4086
4545
  mkdirSync3(skillDir, { recursive: true });
4087
4546
  chownToAgent(skillDir);
4088
- const skillPath = join9(skillDir, "SKILL.md");
4547
+ const skillPath = join10(skillDir, "SKILL.md");
4089
4548
  writeFileSync5(skillPath, skill.body.endsWith("\n") ? skill.body : `${skill.body}
4090
4549
  `, { mode: 384 });
4091
4550
  chownToAgent(skillPath);
4092
4551
  }
4093
- consola26.success("Sync complete.");
4552
+ consola27.success("Sync complete.");
4094
4553
  }
4095
4554
  });
4096
4555
 
4097
4556
  // src/commands/agents/index.ts
4098
- var agentsCommand = defineCommand31({
4557
+ var agentsCommand = defineCommand32({
4099
4558
  meta: {
4100
4559
  name: "agents",
4101
- description: "Manage owned agents (register, spawn, list, destroy, allow, sync, run, serve, cleanup-orphans)"
4560
+ description: "Manage owned agents (register, spawn, list, destroy, allow, sync, run, serve, code, cleanup-orphans)"
4102
4561
  },
4103
4562
  subCommands: {
4104
4563
  register: registerAgentCommand,
@@ -4109,27 +4568,28 @@ var agentsCommand = defineCommand31({
4109
4568
  sync: syncAgentCommand,
4110
4569
  run: runAgentCommand,
4111
4570
  serve: serveAgentCommand,
4571
+ code: codeAgentCommand,
4112
4572
  "cleanup-orphans": cleanupOrphansCommand
4113
4573
  }
4114
4574
  });
4115
4575
 
4116
4576
  // src/commands/nest/index.ts
4117
- import { defineCommand as defineCommand39 } from "citty";
4577
+ import { defineCommand as defineCommand40 } from "citty";
4118
4578
 
4119
4579
  // src/commands/nest/authorize.ts
4120
4580
  import { execFileSync as execFileSync10 } from "child_process";
4121
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
4581
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
4582
+ import { join as join12 } from "path";
4583
+ import { defineCommand as defineCommand34 } from "citty";
4584
+ import consola29 from "consola";
4585
+
4586
+ // src/commands/nest/enroll.ts
4587
+ import { hostname as hostname4, homedir as homedir12 } from "os";
4588
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, chmodSync } from "fs";
4122
4589
  import { join as join11 } from "path";
4123
4590
  import { defineCommand as defineCommand33 } from "citty";
4124
4591
  import consola28 from "consola";
4125
-
4126
- // src/commands/nest/enroll.ts
4127
- import { hostname as hostname4, homedir as homedir11 } from "os";
4128
- import { existsSync as existsSync12, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, chmodSync } from "fs";
4129
- import { join as join10 } from "path";
4130
- import { defineCommand as defineCommand32 } from "citty";
4131
- import consola27 from "consola";
4132
- var NEST_DATA_DIR = join10(homedir11(), ".openape", "nest");
4592
+ var NEST_DATA_DIR = join11(homedir12(), ".openape", "nest");
4133
4593
  function nestAgentName() {
4134
4594
  const raw = hostname4().toLowerCase();
4135
4595
  const head = raw.split(".")[0] ?? raw;
@@ -4137,7 +4597,7 @@ function nestAgentName() {
4137
4597
  const trimmed = safe.slice(0, 16);
4138
4598
  return `nest-${trimmed || "host"}`;
4139
4599
  }
4140
- var enrollNestCommand = defineCommand32({
4600
+ var enrollNestCommand = defineCommand33({
4141
4601
  meta: {
4142
4602
  name: "enroll",
4143
4603
  description: "Register the local nest as a DDISA agent at the IdP. One-time per machine. Required before `apes nest authorize` so YOLO-policies have a target identity."
@@ -4162,25 +4622,25 @@ var enrollNestCommand = defineCommand32({
4162
4622
  throw new CliError("Run `apes login <email>` first \u2014 nest enroll attaches the new identity to your owner account.");
4163
4623
  }
4164
4624
  const name = args.name || nestAgentName();
4165
- const authPath = join10(NEST_DATA_DIR, ".config", "apes", "auth.json");
4166
- if (existsSync12(authPath) && !args.force) {
4625
+ const authPath = join11(NEST_DATA_DIR, ".config", "apes", "auth.json");
4626
+ if (existsSync13(authPath) && !args.force) {
4167
4627
  throw new CliError(`Nest already enrolled at ${authPath}. Pass --force to re-enroll.`);
4168
4628
  }
4169
- const sshDir = join10(NEST_DATA_DIR, ".ssh");
4170
- const configDir = join10(NEST_DATA_DIR, ".config", "apes");
4629
+ const sshDir = join11(NEST_DATA_DIR, ".ssh");
4630
+ const configDir = join11(NEST_DATA_DIR, ".config", "apes");
4171
4631
  mkdirSync4(sshDir, { recursive: true });
4172
4632
  mkdirSync4(configDir, { recursive: true });
4173
- consola27.start(`Generating keypair for ${name}\u2026`);
4633
+ consola28.start(`Generating keypair for ${name}\u2026`);
4174
4634
  const { privatePem, publicSshLine } = generateKeyPairInMemory();
4175
- writeFileSync6(join10(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
4635
+ writeFileSync6(join11(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
4176
4636
  `, { mode: 384 });
4177
- writeFileSync6(join10(sshDir, "id_ed25519.pub"), `${publicSshLine}
4637
+ writeFileSync6(join11(sshDir, "id_ed25519.pub"), `${publicSshLine}
4178
4638
  `, { mode: 420 });
4179
4639
  chmodSync(sshDir, 448);
4180
- consola27.start(`Registering nest at ${idp}\u2026`);
4640
+ consola28.start(`Registering nest at ${idp}\u2026`);
4181
4641
  const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
4182
- consola27.success(`Registered as ${registration.email}`);
4183
- consola27.start("Issuing nest access token\u2026");
4642
+ consola28.success(`Registered as ${registration.email}`);
4643
+ consola28.start("Issuing nest access token\u2026");
4184
4644
  const { token, expiresIn } = await issueAgentToken({
4185
4645
  idp,
4186
4646
  agentEmail: registration.email,
@@ -4191,16 +4651,16 @@ var enrollNestCommand = defineCommand32({
4191
4651
  accessToken: token,
4192
4652
  email: registration.email,
4193
4653
  expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
4194
- keyPath: join10(sshDir, "id_ed25519"),
4654
+ keyPath: join11(sshDir, "id_ed25519"),
4195
4655
  ownerEmail: ownerAuth.email
4196
4656
  });
4197
4657
  writeFileSync6(authPath, authJson, { mode: 384 });
4198
4658
  chmodSync(configDir, 448);
4199
- consola27.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
4200
- consola27.info("");
4201
- consola27.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
4202
- consola27.info("");
4203
- consola27.info(" apes nest authorize");
4659
+ consola28.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
4660
+ consola28.info("");
4661
+ consola28.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
4662
+ consola28.info("");
4663
+ consola28.info(" apes nest authorize");
4204
4664
  }
4205
4665
  });
4206
4666
 
@@ -4247,7 +4707,7 @@ var DEFAULT_ALLOW_PATTERNS = [
4247
4707
  "pm2 delete openape-bridge-*",
4248
4708
  "pm2 jlist"
4249
4709
  ];
4250
- var authorizeNestCommand = defineCommand33({
4710
+ var authorizeNestCommand = defineCommand34({
4251
4711
  meta: {
4252
4712
  name: "authorize",
4253
4713
  description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts (wraps `apes yolo set`)"
@@ -4263,14 +4723,14 @@ var authorizeNestCommand = defineCommand33({
4263
4723
  }
4264
4724
  },
4265
4725
  async run({ args }) {
4266
- const nestAuthPath = join11(NEST_DATA_DIR, ".config", "apes", "auth.json");
4267
- if (!existsSync13(nestAuthPath)) {
4726
+ const nestAuthPath = join12(NEST_DATA_DIR, ".config", "apes", "auth.json");
4727
+ if (!existsSync14(nestAuthPath)) {
4268
4728
  throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
4269
4729
  }
4270
- const nestAuth = JSON.parse(readFileSync11(nestAuthPath, "utf8"));
4730
+ const nestAuth = JSON.parse(readFileSync12(nestAuthPath, "utf8"));
4271
4731
  if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
4272
4732
  const allow = args.allow ?? DEFAULT_ALLOW_PATTERNS.join(",");
4273
- consola28.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
4733
+ consola29.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
4274
4734
  const cmdArgs = [
4275
4735
  "yolo",
4276
4736
  "set",
@@ -4288,16 +4748,16 @@ var authorizeNestCommand = defineCommand33({
4288
4748
  } catch (err) {
4289
4749
  throw new CliError(err instanceof Error ? err.message : String(err));
4290
4750
  }
4291
- consola28.success("Nest-driven agent lifecycle is now zero-prompt.");
4292
- consola28.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
4751
+ consola29.success("Nest-driven agent lifecycle is now zero-prompt.");
4752
+ consola29.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
4293
4753
  }
4294
4754
  });
4295
4755
 
4296
4756
  // src/commands/nest/destroy.ts
4297
4757
  import { execFileSync as execFileSync11 } from "child_process";
4298
- import { defineCommand as defineCommand34 } from "citty";
4299
- import consola29 from "consola";
4300
- var destroyNestCommand = defineCommand34({
4758
+ import { defineCommand as defineCommand35 } from "citty";
4759
+ import consola30 from "consola";
4760
+ var destroyNestCommand = defineCommand35({
4301
4761
  meta: {
4302
4762
  name: "destroy",
4303
4763
  description: "Destroy a local agent. Wraps `apes run --as root -- apes agents destroy <name>`; the Nest watches its registry and pm2-deletes the bridge automatically."
@@ -4309,7 +4769,7 @@ var destroyNestCommand = defineCommand34({
4309
4769
  const name = String(args.name);
4310
4770
  try {
4311
4771
  execFileSync11("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
4312
- consola29.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
4772
+ consola30.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
4313
4773
  } catch (err) {
4314
4774
  const status = err.status ?? 1;
4315
4775
  throw new CliExit(status);
@@ -4319,11 +4779,11 @@ var destroyNestCommand = defineCommand34({
4319
4779
 
4320
4780
  // src/commands/nest/install.ts
4321
4781
  import { execFileSync as execFileSync12 } from "child_process";
4322
- import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
4323
- import { homedir as homedir12, userInfo as userInfo2 } from "os";
4324
- import { dirname as dirname3, join as join12 } from "path";
4325
- import { defineCommand as defineCommand35 } from "citty";
4326
- import consola30 from "consola";
4782
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
4783
+ import { homedir as homedir13, userInfo as userInfo2 } from "os";
4784
+ import { dirname as dirname3, join as join13 } from "path";
4785
+ import { defineCommand as defineCommand36 } from "citty";
4786
+ import consola31 from "consola";
4327
4787
 
4328
4788
  // src/commands/nest/apes-agents-adapter.ts
4329
4789
  var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
@@ -4390,13 +4850,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
4390
4850
  // src/commands/nest/install.ts
4391
4851
  var PLIST_LABEL = "ai.openape.nest";
4392
4852
  function plistPath() {
4393
- return join12(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4853
+ return join13(homedir13(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4394
4854
  }
4395
4855
  function escape2(s) {
4396
4856
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4397
4857
  }
4398
4858
  function buildPlist(args) {
4399
- const logsDir = join12(args.userHome, "Library", "Logs");
4859
+ const logsDir = join13(args.userHome, "Library", "Logs");
4400
4860
  return `<?xml version="1.0" encoding="UTF-8"?>
4401
4861
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4402
4862
  <plist version="1.0">
@@ -4431,25 +4891,25 @@ function buildPlist(args) {
4431
4891
  `;
4432
4892
  }
4433
4893
  function installAdapter2() {
4434
- const target = join12(homedir12(), ".openape", "shapes", "adapters", "apes-agents.toml");
4894
+ const target = join13(homedir13(), ".openape", "shapes", "adapters", "apes-agents.toml");
4435
4895
  mkdirSync5(dirname3(target), { recursive: true });
4436
4896
  let existing = "";
4437
4897
  try {
4438
- existing = readFileSync12(target, "utf8");
4898
+ existing = readFileSync13(target, "utf8");
4439
4899
  } catch {
4440
4900
  }
4441
4901
  if (existing === APES_AGENTS_ADAPTER_TOML) return false;
4442
4902
  writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4443
- consola30.success(`Wrote shapes adapter ${target}`);
4903
+ consola31.success(`Wrote shapes adapter ${target}`);
4444
4904
  return true;
4445
4905
  }
4446
4906
  function writeBridgeModelDefault(model) {
4447
- for (const envDir of [join12(homedir12(), "litellm"), join12(NEST_DATA_DIR, "litellm")]) {
4448
- const envFile = join12(envDir, ".env");
4907
+ for (const envDir of [join13(homedir13(), "litellm"), join13(NEST_DATA_DIR, "litellm")]) {
4908
+ const envFile = join13(envDir, ".env");
4449
4909
  mkdirSync5(envDir, { recursive: true });
4450
4910
  let lines = [];
4451
- if (existsSync14(envFile)) {
4452
- lines = readFileSync12(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
4911
+ if (existsSync15(envFile)) {
4912
+ lines = readFileSync13(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
4453
4913
  }
4454
4914
  lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
4455
4915
  while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
@@ -4459,17 +4919,17 @@ function writeBridgeModelDefault(model) {
4459
4919
  }
4460
4920
  function findBinary(name) {
4461
4921
  for (const dir of [
4462
- join12(homedir12(), ".bun", "bin"),
4922
+ join13(homedir13(), ".bun", "bin"),
4463
4923
  "/opt/homebrew/bin",
4464
4924
  "/usr/local/bin",
4465
4925
  "/usr/bin"
4466
4926
  ]) {
4467
- const p = join12(dir, name);
4468
- if (existsSync14(p)) return p;
4927
+ const p = join13(dir, name);
4928
+ if (existsSync15(p)) return p;
4469
4929
  }
4470
4930
  throw new Error(`could not locate ${name} on PATH; install it first`);
4471
4931
  }
4472
- var installNestCommand = defineCommand35({
4932
+ var installNestCommand = defineCommand36({
4473
4933
  meta: {
4474
4934
  name: "install",
4475
4935
  description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
@@ -4485,35 +4945,35 @@ var installNestCommand = defineCommand35({
4485
4945
  }
4486
4946
  },
4487
4947
  async run({ args }) {
4488
- const homeDir = homedir12();
4948
+ const homeDir = homedir13();
4489
4949
  const port = Number(args.port ?? 9091);
4490
4950
  if (!Number.isInteger(port) || port < 1024 || port > 65535) {
4491
4951
  throw new Error(`invalid port ${port}`);
4492
4952
  }
4493
4953
  const nestBin = findBinary("openape-nest");
4494
4954
  const apesBin = findBinary("apes");
4495
- consola30.info(`Installing nest at ${plistPath()}`);
4496
- consola30.info(` nest binary: ${nestBin}`);
4497
- consola30.info(` apes binary: ${apesBin}`);
4498
- consola30.info(` HTTP port: ${port}`);
4955
+ consola31.info(`Installing nest at ${plistPath()}`);
4956
+ consola31.info(` nest binary: ${nestBin}`);
4957
+ consola31.info(` apes binary: ${apesBin}`);
4958
+ consola31.info(` HTTP port: ${port}`);
4499
4959
  if (typeof args["bridge-model"] === "string" && args["bridge-model"]) {
4500
4960
  writeBridgeModelDefault(args["bridge-model"]);
4501
- consola30.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
4961
+ consola31.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
4502
4962
  }
4503
4963
  installAdapter2();
4504
- mkdirSync5(join12(homeDir, "Library", "LaunchAgents"), { recursive: true });
4964
+ mkdirSync5(join13(homeDir, "Library", "LaunchAgents"), { recursive: true });
4505
4965
  mkdirSync5(NEST_DATA_DIR, { recursive: true });
4506
4966
  const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
4507
4967
  let existing = "";
4508
4968
  try {
4509
- existing = readFileSync12(plistPath(), "utf8");
4969
+ existing = readFileSync13(plistPath(), "utf8");
4510
4970
  } catch {
4511
4971
  }
4512
4972
  if (existing !== desired) {
4513
4973
  writeFileSync7(plistPath(), desired, { mode: 420 });
4514
- consola30.success("Wrote launchd plist");
4974
+ consola31.success("Wrote launchd plist");
4515
4975
  } else {
4516
- consola30.info("plist already up to date");
4976
+ consola31.info("plist already up to date");
4517
4977
  }
4518
4978
  const uid = userInfo2().uid;
4519
4979
  try {
@@ -4521,21 +4981,21 @@ var installNestCommand = defineCommand35({
4521
4981
  } catch {
4522
4982
  }
4523
4983
  execFileSync12("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4524
- consola30.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4525
- consola30.info("");
4526
- consola30.info("Next steps for zero-prompt spawn \u2014 both one-time:");
4527
- consola30.info("");
4528
- consola30.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
4529
- consola30.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
4530
- consola30.info("");
4531
- consola30.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
4984
+ consola31.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4985
+ consola31.info("");
4986
+ consola31.info("Next steps for zero-prompt spawn \u2014 both one-time:");
4987
+ consola31.info("");
4988
+ consola31.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
4989
+ consola31.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
4990
+ consola31.info("");
4991
+ consola31.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
4532
4992
  }
4533
4993
  });
4534
4994
 
4535
4995
  // src/commands/nest/list.ts
4536
- import { defineCommand as defineCommand36 } from "citty";
4537
- import consola31 from "consola";
4538
- var listNestCommand = defineCommand36({
4996
+ import { defineCommand as defineCommand37 } from "citty";
4997
+ import consola32 from "consola";
4998
+ var listNestCommand = defineCommand37({
4539
4999
  meta: {
4540
5000
  name: "list",
4541
5001
  description: "List agents registered with the local nest. Reads /var/openape/nest/agents.json directly."
@@ -4550,22 +5010,22 @@ var listNestCommand = defineCommand36({
4550
5010
  return;
4551
5011
  }
4552
5012
  if (reg.agents.length === 0) {
4553
- consola31.info("(no agents registered with this nest)");
5013
+ consola32.info("(no agents registered with this nest)");
4554
5014
  return;
4555
5015
  }
4556
- consola31.info(`${reg.agents.length} agent(s) registered with this nest:`);
5016
+ consola32.info(`${reg.agents.length} agent(s) registered with this nest:`);
4557
5017
  for (const a of reg.agents) {
4558
5018
  const bridge = a.bridge ? " bridge=on" : "";
4559
- consola31.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
5019
+ consola32.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
4560
5020
  }
4561
5021
  }
4562
5022
  });
4563
5023
 
4564
5024
  // src/commands/nest/spawn.ts
4565
5025
  import { execFileSync as execFileSync13 } from "child_process";
4566
- import { defineCommand as defineCommand37 } from "citty";
4567
- import consola32 from "consola";
4568
- var spawnNestCommand = defineCommand37({
5026
+ import { defineCommand as defineCommand38 } from "citty";
5027
+ import consola33 from "consola";
5028
+ var spawnNestCommand = defineCommand38({
4569
5029
  meta: {
4570
5030
  name: "spawn",
4571
5031
  description: "Spawn a new agent locally. Wraps `apes run --as root -- apes agents spawn <name>`; the Nest watches its registry and starts the bridge in pm2 automatically."
@@ -4596,7 +5056,7 @@ var spawnNestCommand = defineCommand37({
4596
5056
  if (typeof args["bridge-model"] === "string") apesArgs.push("--bridge-model", args["bridge-model"]);
4597
5057
  try {
4598
5058
  execFileSync13("apes", apesArgs, { stdio: "inherit" });
4599
- consola32.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
5059
+ consola33.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
4600
5060
  } catch (err) {
4601
5061
  const status = err.status ?? 1;
4602
5062
  throw new CliExit(status);
@@ -4606,36 +5066,36 @@ var spawnNestCommand = defineCommand37({
4606
5066
 
4607
5067
  // src/commands/nest/uninstall.ts
4608
5068
  import { execFileSync as execFileSync14 } from "child_process";
4609
- import { existsSync as existsSync15, unlinkSync } from "fs";
4610
- import { homedir as homedir13, userInfo as userInfo3 } from "os";
4611
- import { join as join13 } from "path";
4612
- import { defineCommand as defineCommand38 } from "citty";
4613
- import consola33 from "consola";
5069
+ import { existsSync as existsSync16, unlinkSync } from "fs";
5070
+ import { homedir as homedir14, userInfo as userInfo3 } from "os";
5071
+ import { join as join14 } from "path";
5072
+ import { defineCommand as defineCommand39 } from "citty";
5073
+ import consola34 from "consola";
4614
5074
  var PLIST_LABEL2 = "ai.openape.nest";
4615
- var uninstallNestCommand = defineCommand38({
5075
+ var uninstallNestCommand = defineCommand39({
4616
5076
  meta: {
4617
5077
  name: "uninstall",
4618
5078
  description: "Stop + remove the local nest-daemon (registry + agents preserved)"
4619
5079
  },
4620
5080
  async run() {
4621
5081
  const uid = userInfo3().uid;
4622
- const path2 = join13(homedir13(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
5082
+ const path2 = join14(homedir14(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4623
5083
  try {
4624
5084
  execFileSync14("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4625
- consola33.success("Nest daemon stopped");
5085
+ consola34.success("Nest daemon stopped");
4626
5086
  } catch {
4627
- consola33.info("Nest daemon was not loaded");
5087
+ consola34.info("Nest daemon was not loaded");
4628
5088
  }
4629
- if (existsSync15(path2)) {
5089
+ if (existsSync16(path2)) {
4630
5090
  unlinkSync(path2);
4631
- consola33.success(`Removed ${path2}`);
5091
+ consola34.success(`Removed ${path2}`);
4632
5092
  }
4633
- consola33.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
5093
+ consola34.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
4634
5094
  }
4635
5095
  });
4636
5096
 
4637
5097
  // src/commands/nest/index.ts
4638
- var nestCommand = defineCommand39({
5098
+ var nestCommand = defineCommand40({
4639
5099
  meta: {
4640
5100
  name: "nest",
4641
5101
  description: "Manage the local Nest control-plane daemon. One-time setup: `install` \u2192 `enroll` \u2192 `authorize`. Day-to-day: `list` / `spawn` / `destroy`. As of Phase D the Nest is a long-running CLIENT \u2014 commands talk to it via filesystem intent files in $NEST_HOME/intents (mode 770, group _openape_nest) instead of HTTP."
@@ -4652,12 +5112,12 @@ var nestCommand = defineCommand39({
4652
5112
  });
4653
5113
 
4654
5114
  // src/commands/yolo/index.ts
4655
- import { defineCommand as defineCommand43 } from "citty";
5115
+ import { defineCommand as defineCommand44 } from "citty";
4656
5116
 
4657
5117
  // src/commands/yolo/clear.ts
4658
- import { defineCommand as defineCommand40 } from "citty";
4659
- import consola34 from "consola";
4660
- var yoloClearCommand = defineCommand40({
5118
+ import { defineCommand as defineCommand41 } from "citty";
5119
+ import consola35 from "consola";
5120
+ var yoloClearCommand = defineCommand41({
4661
5121
  meta: {
4662
5122
  name: "clear",
4663
5123
  description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
@@ -4686,15 +5146,15 @@ var yoloClearCommand = defineCommand40({
4686
5146
  const text = await res.text().catch(() => "");
4687
5147
  throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
4688
5148
  }
4689
- consola34.success(`YOLO-policy cleared on ${email}`);
5149
+ consola35.success(`YOLO-policy cleared on ${email}`);
4690
5150
  }
4691
5151
  });
4692
5152
 
4693
5153
  // src/commands/yolo/set.ts
4694
- import { defineCommand as defineCommand41 } from "citty";
4695
- import consola35 from "consola";
5154
+ import { defineCommand as defineCommand42 } from "citty";
5155
+ import consola36 from "consola";
4696
5156
  var VALID_MODES = ["allow-list", "deny-list"];
4697
- var yoloSetCommand = defineCommand41({
5157
+ var yoloSetCommand = defineCommand42({
4698
5158
  meta: {
4699
5159
  name: "set",
4700
5160
  description: "Write a YOLO-policy on a DDISA agent you own"
@@ -4742,12 +5202,12 @@ var yoloSetCommand = defineCommand41({
4742
5202
  const denyPatterns = parseList(args.deny);
4743
5203
  const denyRiskThreshold = args["deny-risk"] ?? null;
4744
5204
  const expiresAt = parseExpiresIn(args["expires-in"]);
4745
- consola35.info(`Setting YOLO-policy on ${email}`);
4746
- consola35.info(` mode: ${mode}`);
4747
- if (allowPatterns.length) consola35.info(` allow_patterns: ${allowPatterns.join(", ")}`);
4748
- if (denyPatterns.length) consola35.info(` deny_patterns: ${denyPatterns.join(", ")}`);
4749
- if (denyRiskThreshold) consola35.info(` deny_risk: ${denyRiskThreshold}`);
4750
- if (expiresAt) consola35.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
5205
+ consola36.info(`Setting YOLO-policy on ${email}`);
5206
+ consola36.info(` mode: ${mode}`);
5207
+ if (allowPatterns.length) consola36.info(` allow_patterns: ${allowPatterns.join(", ")}`);
5208
+ if (denyPatterns.length) consola36.info(` deny_patterns: ${denyPatterns.join(", ")}`);
5209
+ if (denyRiskThreshold) consola36.info(` deny_risk: ${denyRiskThreshold}`);
5210
+ if (expiresAt) consola36.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
4751
5211
  const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
4752
5212
  const res = await fetch(url, {
4753
5213
  method: "PUT",
@@ -4767,7 +5227,7 @@ var yoloSetCommand = defineCommand41({
4767
5227
  const text = await res.text().catch(() => "");
4768
5228
  throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
4769
5229
  }
4770
- consola35.success(`YOLO-policy applied to ${email}`);
5230
+ consola36.success(`YOLO-policy applied to ${email}`);
4771
5231
  }
4772
5232
  });
4773
5233
  function parseList(s) {
@@ -4785,9 +5245,9 @@ function parseExpiresIn(s) {
4785
5245
  }
4786
5246
 
4787
5247
  // src/commands/yolo/show.ts
4788
- import { defineCommand as defineCommand42 } from "citty";
4789
- import consola36 from "consola";
4790
- var yoloShowCommand = defineCommand42({
5248
+ import { defineCommand as defineCommand43 } from "citty";
5249
+ import consola37 from "consola";
5250
+ var yoloShowCommand = defineCommand43({
4791
5251
  meta: {
4792
5252
  name: "show",
4793
5253
  description: "Print the YOLO-policy currently set on a DDISA agent"
@@ -4824,17 +5284,17 @@ var yoloShowCommand = defineCommand42({
4824
5284
  console.log(JSON.stringify(policy, null, 2));
4825
5285
  return;
4826
5286
  }
4827
- consola36.info(`YOLO-policy for ${email}`);
4828
- consola36.info(` mode: ${policy.mode}`);
4829
- consola36.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
4830
- consola36.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
4831
- consola36.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
4832
- consola36.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
5287
+ consola37.info(`YOLO-policy for ${email}`);
5288
+ consola37.info(` mode: ${policy.mode}`);
5289
+ consola37.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
5290
+ consola37.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
5291
+ consola37.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
5292
+ consola37.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
4833
5293
  }
4834
5294
  });
4835
5295
 
4836
5296
  // src/commands/yolo/index.ts
4837
- var yoloCommand = defineCommand43({
5297
+ var yoloCommand = defineCommand44({
4838
5298
  meta: {
4839
5299
  name: "yolo",
4840
5300
  description: "Manage YOLO-policies on DDISA agents you own \u2014 auto-approve grant patterns at the IdP layer (allow-list) or block dangerous ones outright (deny-list)."
@@ -4847,15 +5307,15 @@ var yoloCommand = defineCommand43({
4847
5307
  });
4848
5308
 
4849
5309
  // src/commands/adapter/index.ts
4850
- import { defineCommand as defineCommand44 } from "citty";
4851
- import consola37 from "consola";
4852
- var adapterCommand = defineCommand44({
5310
+ import { defineCommand as defineCommand45 } from "citty";
5311
+ import consola38 from "consola";
5312
+ var adapterCommand = defineCommand45({
4853
5313
  meta: {
4854
5314
  name: "adapter",
4855
5315
  description: "Manage CLI adapters"
4856
5316
  },
4857
5317
  subCommands: {
4858
- list: defineCommand44({
5318
+ list: defineCommand45({
4859
5319
  meta: {
4860
5320
  name: "list",
4861
5321
  description: "List available adapters"
@@ -4886,7 +5346,7 @@ var adapterCommand = defineCommand44({
4886
5346
  `);
4887
5347
  return;
4888
5348
  }
4889
- consola37.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
5349
+ consola38.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4890
5350
  for (const a of index2.adapters) {
4891
5351
  const installed = isInstalled(a.id, false) ? " [installed]" : "";
4892
5352
  console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
@@ -4908,7 +5368,7 @@ var adapterCommand = defineCommand44({
4908
5368
  return;
4909
5369
  }
4910
5370
  if (local.length === 0) {
4911
- consola37.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
5371
+ consola38.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
4912
5372
  return;
4913
5373
  }
4914
5374
  for (const a of local) {
@@ -4916,7 +5376,7 @@ var adapterCommand = defineCommand44({
4916
5376
  }
4917
5377
  }
4918
5378
  }),
4919
- install: defineCommand44({
5379
+ install: defineCommand45({
4920
5380
  meta: {
4921
5381
  name: "install",
4922
5382
  description: "Install an adapter from the registry"
@@ -4945,24 +5405,24 @@ var adapterCommand = defineCommand44({
4945
5405
  for (const id of ids) {
4946
5406
  const entry = findAdapter(index, id);
4947
5407
  if (!entry) {
4948
- consola37.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
5408
+ consola38.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
4949
5409
  continue;
4950
5410
  }
4951
5411
  const conflicts = findConflictingAdapters(entry.executable, id);
4952
5412
  if (conflicts.length > 0) {
4953
5413
  for (const c of conflicts) {
4954
- consola37.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
4955
- consola37.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
5414
+ consola38.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
5415
+ consola38.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
4956
5416
  }
4957
5417
  }
4958
5418
  const result = await installAdapter(entry, { local });
4959
5419
  const verb = result.updated ? "Updated" : "Installed";
4960
- consola37.success(`${verb} ${result.id} \u2192 ${result.path}`);
4961
- consola37.info(`Digest: ${result.digest}`);
5420
+ consola38.success(`${verb} ${result.id} \u2192 ${result.path}`);
5421
+ consola38.info(`Digest: ${result.digest}`);
4962
5422
  }
4963
5423
  }
4964
5424
  }),
4965
- remove: defineCommand44({
5425
+ remove: defineCommand45({
4966
5426
  meta: {
4967
5427
  name: "remove",
4968
5428
  description: "Remove an installed adapter"
@@ -4985,9 +5445,9 @@ var adapterCommand = defineCommand44({
4985
5445
  let failed = false;
4986
5446
  for (const id of ids) {
4987
5447
  if (removeAdapter(id, local)) {
4988
- consola37.success(`Removed adapter: ${id}`);
5448
+ consola38.success(`Removed adapter: ${id}`);
4989
5449
  } else {
4990
- consola37.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
5450
+ consola38.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4991
5451
  failed = true;
4992
5452
  }
4993
5453
  }
@@ -4995,7 +5455,7 @@ var adapterCommand = defineCommand44({
4995
5455
  throw new CliError("Some adapters could not be removed");
4996
5456
  }
4997
5457
  }),
4998
- info: defineCommand44({
5458
+ info: defineCommand45({
4999
5459
  meta: {
5000
5460
  name: "info",
5001
5461
  description: "Show detailed adapter information"
@@ -5037,7 +5497,7 @@ var adapterCommand = defineCommand44({
5037
5497
  }
5038
5498
  }
5039
5499
  }),
5040
- search: defineCommand44({
5500
+ search: defineCommand45({
5041
5501
  meta: {
5042
5502
  name: "search",
5043
5503
  description: "Search adapters in the registry"
@@ -5069,7 +5529,7 @@ var adapterCommand = defineCommand44({
5069
5529
  return;
5070
5530
  }
5071
5531
  if (results.length === 0) {
5072
- consola37.info(`No adapters matching "${query}"`);
5532
+ consola38.info(`No adapters matching "${query}"`);
5073
5533
  return;
5074
5534
  }
5075
5535
  for (const a of results) {
@@ -5078,7 +5538,7 @@ var adapterCommand = defineCommand44({
5078
5538
  }
5079
5539
  }
5080
5540
  }),
5081
- update: defineCommand44({
5541
+ update: defineCommand45({
5082
5542
  meta: {
5083
5543
  name: "update",
5084
5544
  description: "Update installed adapters"
@@ -5104,33 +5564,33 @@ var adapterCommand = defineCommand44({
5104
5564
  const targetId = args.id ? String(args.id) : void 0;
5105
5565
  const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
5106
5566
  if (targets.length === 0) {
5107
- consola37.info("No adapters installed to update.");
5567
+ consola38.info("No adapters installed to update.");
5108
5568
  return;
5109
5569
  }
5110
5570
  for (const id of targets) {
5111
5571
  const entry = findAdapter(index, id);
5112
5572
  if (!entry) {
5113
- consola37.warn(`${id}: not found in registry, skipping`);
5573
+ consola38.warn(`${id}: not found in registry, skipping`);
5114
5574
  continue;
5115
5575
  }
5116
5576
  const localDigest = getInstalledDigest(id, false);
5117
5577
  if (localDigest === entry.digest) {
5118
- consola37.info(`${id}: already up to date`);
5578
+ consola38.info(`${id}: already up to date`);
5119
5579
  continue;
5120
5580
  }
5121
5581
  if (localDigest && !args.yes) {
5122
- consola37.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
5123
- consola37.info(` Old: ${localDigest}`);
5124
- consola37.info(` New: ${entry.digest}`);
5125
- consola37.info(" Use --yes to confirm");
5582
+ consola38.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
5583
+ consola38.info(` Old: ${localDigest}`);
5584
+ consola38.info(` New: ${entry.digest}`);
5585
+ consola38.info(" Use --yes to confirm");
5126
5586
  continue;
5127
5587
  }
5128
5588
  const result = await installAdapter(entry);
5129
- consola37.success(`Updated ${result.id} \u2192 ${result.path}`);
5589
+ consola38.success(`Updated ${result.id} \u2192 ${result.path}`);
5130
5590
  }
5131
5591
  }
5132
5592
  }),
5133
- verify: defineCommand44({
5593
+ verify: defineCommand45({
5134
5594
  meta: {
5135
5595
  name: "verify",
5136
5596
  description: "Verify installed adapter against registry digest"
@@ -5163,7 +5623,7 @@ var adapterCommand = defineCommand44({
5163
5623
  if (!localDigest)
5164
5624
  throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
5165
5625
  if (localDigest === entry.digest) {
5166
- consola37.success(`${id}: digest matches registry`);
5626
+ consola38.success(`${id}: digest matches registry`);
5167
5627
  } else {
5168
5628
  console.log(` Local: ${localDigest}`);
5169
5629
  console.log(` Registry: ${entry.digest}`);
@@ -5178,8 +5638,8 @@ var adapterCommand = defineCommand44({
5178
5638
  import { execFileSync as execFileSync15 } from "child_process";
5179
5639
  import { hostname as hostname5 } from "os";
5180
5640
  import { basename } from "path";
5181
- import { defineCommand as defineCommand45 } from "citty";
5182
- import consola38 from "consola";
5641
+ import { defineCommand as defineCommand46 } from "citty";
5642
+ import consola39 from "consola";
5183
5643
  function resolveRunAsTarget(runAs) {
5184
5644
  if (!runAs) return runAs;
5185
5645
  if (!isDarwin()) return runAs;
@@ -5225,7 +5685,7 @@ function printPendingGrantInfo(grant, idp) {
5225
5685
  const statusCmd = `apes grants status ${grant.id}`;
5226
5686
  const executeCmd = `apes grants run ${grant.id}`;
5227
5687
  if (mode === "human") {
5228
- consola38.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
5688
+ consola39.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
5229
5689
  console.log(` Approve in browser: ${approveUrl}`);
5230
5690
  console.log(` Check status: ${statusCmd}`);
5231
5691
  console.log(` Run after approval: ${executeCmd}`);
@@ -5235,7 +5695,7 @@ function printPendingGrantInfo(grant, idp) {
5235
5695
  return;
5236
5696
  }
5237
5697
  const maxMin = getPollMaxMinutes();
5238
- consola38.success(`Grant ${grant.id} created (pending approval)`);
5698
+ consola39.success(`Grant ${grant.id} created (pending approval)`);
5239
5699
  console.log(` Approve: ${approveUrl}`);
5240
5700
  console.log(` Status: ${statusCmd} [--json]`);
5241
5701
  console.log(` Execute: ${executeCmd} --wait`);
@@ -5257,7 +5717,7 @@ function printPendingGrantInfo(grant, idp) {
5257
5717
  console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
5258
5718
  console.log(" grant be reused on subsequent invocations without re-approval.");
5259
5719
  }
5260
- var runCommand = defineCommand45({
5720
+ var runCommand = defineCommand46({
5261
5721
  meta: {
5262
5722
  name: "run",
5263
5723
  description: "Execute a grant-secured command"
@@ -5360,7 +5820,7 @@ async function runShellMode(command, args) {
5360
5820
  }
5361
5821
  } catch {
5362
5822
  }
5363
- consola38.info(`Requesting ape-shell session grant on ${targetHost}`);
5823
+ consola39.info(`Requesting ape-shell session grant on ${targetHost}`);
5364
5824
  const grant = await apiFetch(grantsUrl, {
5365
5825
  method: "POST",
5366
5826
  body: {
@@ -5380,8 +5840,8 @@ async function runShellMode(command, args) {
5380
5840
  host: targetHost
5381
5841
  });
5382
5842
  if (shouldWaitForGrant(args)) {
5383
- consola38.info(`Grant requested: ${grant.id}`);
5384
- consola38.info("Waiting for approval...");
5843
+ consola39.info(`Grant requested: ${grant.id}`);
5844
+ consola39.info("Waiting for approval...");
5385
5845
  const maxWait = 3e5;
5386
5846
  const interval = 3e3;
5387
5847
  const start = Date.now();
@@ -5412,13 +5872,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
5412
5872
  try {
5413
5873
  resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
5414
5874
  } catch (err) {
5415
- consola38.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
5875
+ consola39.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
5416
5876
  return false;
5417
5877
  }
5418
5878
  try {
5419
5879
  const existingGrantId = await findExistingGrant(resolved, idp);
5420
5880
  if (existingGrantId) {
5421
- consola38.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
5881
+ consola39.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
5422
5882
  const token = await fetchGrantToken(idp, existingGrantId);
5423
5883
  await verifyAndExecute(token, resolved, existingGrantId);
5424
5884
  return true;
@@ -5426,7 +5886,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
5426
5886
  } catch {
5427
5887
  }
5428
5888
  const approval = args.approval ?? "once";
5429
- consola38.info(`Requesting grant for: ${resolved.detail.display}`);
5889
+ consola39.info(`Requesting grant for: ${resolved.detail.display}`);
5430
5890
  const grant = await createShapesGrant(resolved, {
5431
5891
  idp,
5432
5892
  approval,
@@ -5434,8 +5894,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
5434
5894
  });
5435
5895
  if (grant.similar_grants?.similar_grants?.length) {
5436
5896
  const n = grant.similar_grants.similar_grants.length;
5437
- consola38.info("");
5438
- consola38.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
5897
+ consola39.info("");
5898
+ consola39.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
5439
5899
  }
5440
5900
  notifyGrantPending({
5441
5901
  grantId: grant.id,
@@ -5445,8 +5905,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
5445
5905
  host: args.host || hostname5()
5446
5906
  });
5447
5907
  if (shouldWaitForGrant(args)) {
5448
- consola38.info(`Grant requested: ${grant.id}`);
5449
- consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5908
+ consola39.info(`Grant requested: ${grant.id}`);
5909
+ consola39.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5450
5910
  const status = await waitForGrantStatus(idp, grant.id);
5451
5911
  if (status !== "approved")
5452
5912
  throw new CliError(`Grant ${status}`);
@@ -5520,7 +5980,7 @@ async function runAdapterMode(command, rawArgs, args) {
5520
5980
  try {
5521
5981
  const existingGrantId = await findExistingGrant(resolved, idp);
5522
5982
  if (existingGrantId) {
5523
- consola38.info(`Reusing existing grant: ${existingGrantId}`);
5983
+ consola39.info(`Reusing existing grant: ${existingGrantId}`);
5524
5984
  const token = await fetchGrantToken(idp, existingGrantId);
5525
5985
  await verifyAndExecute(token, resolved, existingGrantId);
5526
5986
  return;
@@ -5534,17 +5994,17 @@ async function runAdapterMode(command, rawArgs, args) {
5534
5994
  });
5535
5995
  if (grant.similar_grants?.similar_grants?.length) {
5536
5996
  const n = grant.similar_grants.similar_grants.length;
5537
- consola38.info("");
5538
- consola38.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
5997
+ consola39.info("");
5998
+ consola39.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
5539
5999
  if (grant.similar_grants.widened_details?.length) {
5540
6000
  const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
5541
- consola38.info(` Broader scope: ${wider}`);
6001
+ consola39.info(` Broader scope: ${wider}`);
5542
6002
  }
5543
- consola38.info("");
6003
+ consola39.info("");
5544
6004
  }
5545
6005
  if (shouldWaitForGrant(args)) {
5546
- consola38.info(`Grant requested: ${grant.id}`);
5547
- consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
6006
+ consola39.info(`Grant requested: ${grant.id}`);
6007
+ consola39.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5548
6008
  const status = await waitForGrantStatus(idp, grant.id);
5549
6009
  if (status !== "approved")
5550
6010
  throw new Error(`Grant ${status}`);
@@ -5577,7 +6037,7 @@ async function runAudienceMode(audience, action, args) {
5577
6037
  const { authz_jwt: authz_jwt2 } = await apiFetch(`${grantsUrl}/${reusableId}/token`, { method: "POST" });
5578
6038
  return executeWithGrantToken({ audience, command, args, token: authz_jwt2 });
5579
6039
  }
5580
- consola38.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
6040
+ consola39.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
5581
6041
  const grant = await apiFetch(grantsUrl, {
5582
6042
  method: "POST",
5583
6043
  body: {
@@ -5594,9 +6054,9 @@ async function runAudienceMode(audience, action, args) {
5594
6054
  printPendingGrantInfo(grant, idp);
5595
6055
  throw new CliExit(getAsyncExitCode());
5596
6056
  }
5597
- consola38.success(`Grant requested: ${grant.id}`);
5598
- consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5599
- consola38.info("Waiting for approval...");
6057
+ consola39.success(`Grant requested: ${grant.id}`);
6058
+ consola39.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
6059
+ consola39.info("Waiting for approval...");
5600
6060
  const maxWait = 15 * 60 * 1e3;
5601
6061
  const interval = 3e3;
5602
6062
  const start = Date.now();
@@ -5604,7 +6064,7 @@ async function runAudienceMode(audience, action, args) {
5604
6064
  while (Date.now() - start < maxWait) {
5605
6065
  const status = await apiFetch(`${grantsUrl}/${grant.id}`);
5606
6066
  if (status.status === "approved") {
5607
- consola38.success("Grant approved!");
6067
+ consola39.success("Grant approved!");
5608
6068
  approved = true;
5609
6069
  break;
5610
6070
  }
@@ -5619,7 +6079,7 @@ async function runAudienceMode(audience, action, args) {
5619
6079
  `Grant approval timed out after ${minutes} min (still pending). Check your DDISA inbox at ${idp}/grant-approval?grant_id=${grant.id} \u2014 if approved later, re-run the same \`apes run\` command and it will reuse the grant.`
5620
6080
  );
5621
6081
  }
5622
- consola38.info("Fetching grant token...");
6082
+ consola39.info("Fetching grant token...");
5623
6083
  const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
5624
6084
  method: "POST"
5625
6085
  });
@@ -5628,7 +6088,7 @@ async function runAudienceMode(audience, action, args) {
5628
6088
  function executeWithGrantToken(opts) {
5629
6089
  const { audience, command, args, token } = opts;
5630
6090
  if (audience === "escapes") {
5631
- consola38.info(`Executing: ${command.join(" ")}`);
6091
+ consola39.info(`Executing: ${command.join(" ")}`);
5632
6092
  try {
5633
6093
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5634
6094
  execFileSync15(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
@@ -5667,11 +6127,11 @@ async function findReusableAudienceGrant(opts) {
5667
6127
 
5668
6128
  // src/commands/proxy.ts
5669
6129
  import { spawn as spawn2 } from "child_process";
5670
- import { existsSync as existsSync17 } from "fs";
5671
- import { homedir as homedir14 } from "os";
5672
- import { join as join16 } from "path";
5673
- import { defineCommand as defineCommand46 } from "citty";
5674
- import consola39 from "consola";
6130
+ import { existsSync as existsSync18 } from "fs";
6131
+ import { homedir as homedir15 } from "os";
6132
+ import { join as join17 } from "path";
6133
+ import { defineCommand as defineCommand47 } from "citty";
6134
+ import consola40 from "consola";
5675
6135
 
5676
6136
  // src/proxy/config.ts
5677
6137
  function buildDefaultProxyConfigToml(opts) {
@@ -5708,7 +6168,7 @@ import { spawn } from "child_process";
5708
6168
  import { mkdtempSync as mkdtempSync3, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
5709
6169
  import { createRequire } from "module";
5710
6170
  import { tmpdir as tmpdir3 } from "os";
5711
- import { dirname as dirname4, join as join14, resolve as resolve3 } from "path";
6171
+ import { dirname as dirname4, join as join15, resolve as resolve3 } from "path";
5712
6172
  var require2 = createRequire(import.meta.url);
5713
6173
  function findProxyBin() {
5714
6174
  const pkgPath = require2.resolve("@openape/proxy/package.json");
@@ -5720,8 +6180,8 @@ function findProxyBin() {
5720
6180
  return resolve3(dirname4(pkgPath), binRel);
5721
6181
  }
5722
6182
  async function startEphemeralProxy(configToml) {
5723
- const tmpDir = mkdtempSync3(join14(tmpdir3(), "openape-proxy-"));
5724
- const configPath = join14(tmpDir, "config.toml");
6183
+ const tmpDir = mkdtempSync3(join15(tmpdir3(), "openape-proxy-"));
6184
+ const configPath = join15(tmpDir, "config.toml");
5725
6185
  writeFileSync8(configPath, configToml, { mode: 384 });
5726
6186
  const binPath = findProxyBin();
5727
6187
  const child = spawn(process.execPath, [binPath, "-c", configPath], {
@@ -5804,9 +6264,9 @@ function waitForListenLine(child) {
5804
6264
  }
5805
6265
 
5806
6266
  // src/proxy/trust-bundle.ts
5807
- import { existsSync as existsSync16, mkdtempSync as mkdtempSync4, readFileSync as readFileSync13, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
6267
+ import { existsSync as existsSync17, mkdtempSync as mkdtempSync4, readFileSync as readFileSync14, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
5808
6268
  import { tmpdir as tmpdir4 } from "os";
5809
- import { join as join15 } from "path";
6269
+ import { join as join16 } from "path";
5810
6270
  var CANDIDATES = [
5811
6271
  "/etc/ssl/cert.pem",
5812
6272
  // macOS
@@ -5819,17 +6279,17 @@ var CANDIDATES = [
5819
6279
  ];
5820
6280
  function detectSystemCaPath() {
5821
6281
  for (const p of CANDIDATES) {
5822
- if (existsSync16(p)) return p;
6282
+ if (existsSync17(p)) return p;
5823
6283
  }
5824
6284
  throw new Error(
5825
6285
  `Could not locate a system CA bundle. Tried: ${CANDIDATES.join(", ")}. Set NODE_EXTRA_CA_CERTS yourself or pass --allow-no-system-ca.`
5826
6286
  );
5827
6287
  }
5828
6288
  function buildTrustBundle(opts) {
5829
- const dir = mkdtempSync4(join15(tmpdir4(), "openape-trust-"));
5830
- const path2 = join15(dir, "bundle.pem");
5831
- const sys = readFileSync13(opts.systemCaPath, "utf-8");
5832
- const local = readFileSync13(opts.localCaPath, "utf-8");
6289
+ const dir = mkdtempSync4(join16(tmpdir4(), "openape-trust-"));
6290
+ const path2 = join16(dir, "bundle.pem");
6291
+ const sys = readFileSync14(opts.systemCaPath, "utf-8");
6292
+ const local = readFileSync14(opts.localCaPath, "utf-8");
5833
6293
  writeFileSync9(path2, `${sys.trimEnd()}
5834
6294
  ${local.trimEnd()}
5835
6295
  `, { mode: 384 });
@@ -5859,10 +6319,10 @@ function resolveProxyConfigOptions() {
5859
6319
  77
5860
6320
  );
5861
6321
  }
5862
- consola39.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
6322
+ consola40.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
5863
6323
  return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
5864
6324
  }
5865
- var proxyCommand = defineCommand46({
6325
+ var proxyCommand = defineCommand47({
5866
6326
  meta: {
5867
6327
  name: "proxy",
5868
6328
  description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
@@ -5886,9 +6346,9 @@ var proxyCommand = defineCommand46({
5886
6346
  let close = null;
5887
6347
  if (reuseHostPort) {
5888
6348
  proxyUrl = `http://${reuseHostPort}`;
5889
- consola39.info(`[apes proxy] using long-running daemon at ${proxyUrl}`);
5890
- const localCaPath = join16(homedir14(), ".openape", "proxy", "ca.crt");
5891
- if (!existsSync17(localCaPath)) {
6349
+ consola40.info(`[apes proxy] using long-running daemon at ${proxyUrl}`);
6350
+ const localCaPath = join17(homedir15(), ".openape", "proxy", "ca.crt");
6351
+ if (!existsSync18(localCaPath)) {
5892
6352
  throw new CliError(
5893
6353
  `OPENAPE_PROXY is set but no local CA found at ${localCaPath}. Start the daemon (sudo -u <agent> apes proxy --global < secrets.toml) first.`
5894
6354
  );
@@ -5897,15 +6357,15 @@ var proxyCommand = defineCommand46({
5897
6357
  systemCaPath: detectSystemCaPath(),
5898
6358
  localCaPath
5899
6359
  });
5900
- consola39.debug(`[apes proxy] trust bundle: ${bundle.path}`);
6360
+ consola40.debug(`[apes proxy] trust bundle: ${bundle.path}`);
5901
6361
  } else if (reuseUrl) {
5902
6362
  proxyUrl = reuseUrl;
5903
- consola39.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
6363
+ consola40.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5904
6364
  } else {
5905
6365
  const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
5906
6366
  proxyUrl = ephemeral.url;
5907
6367
  close = ephemeral.close;
5908
- consola39.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
6368
+ consola40.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5909
6369
  }
5910
6370
  const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
5911
6371
  const childEnv = {
@@ -5946,7 +6406,7 @@ var proxyCommand = defineCommand46({
5946
6406
  else resolveExit(code ?? 0);
5947
6407
  });
5948
6408
  child.once("error", (err) => {
5949
- consola39.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
6409
+ consola40.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5950
6410
  resolveExit(127);
5951
6411
  });
5952
6412
  });
@@ -5963,8 +6423,8 @@ function signalNumber(signal) {
5963
6423
  }
5964
6424
 
5965
6425
  // src/commands/explain.ts
5966
- import { defineCommand as defineCommand47 } from "citty";
5967
- var explainCommand = defineCommand47({
6426
+ import { defineCommand as defineCommand48 } from "citty";
6427
+ var explainCommand = defineCommand48({
5968
6428
  meta: {
5969
6429
  name: "explain",
5970
6430
  description: "Show what permission a command would need"
@@ -6002,9 +6462,9 @@ var explainCommand = defineCommand47({
6002
6462
  });
6003
6463
 
6004
6464
  // src/commands/config/get.ts
6005
- import { defineCommand as defineCommand48 } from "citty";
6006
- import consola40 from "consola";
6007
- var configGetCommand = defineCommand48({
6465
+ import { defineCommand as defineCommand49 } from "citty";
6466
+ import consola41 from "consola";
6467
+ var configGetCommand = defineCommand49({
6008
6468
  meta: {
6009
6469
  name: "get",
6010
6470
  description: "Get a configuration value"
@@ -6024,7 +6484,7 @@ var configGetCommand = defineCommand48({
6024
6484
  if (idp)
6025
6485
  console.log(idp);
6026
6486
  else
6027
- consola40.info("No IdP configured.");
6487
+ consola41.info("No IdP configured.");
6028
6488
  break;
6029
6489
  }
6030
6490
  case "email": {
@@ -6032,7 +6492,7 @@ var configGetCommand = defineCommand48({
6032
6492
  if (auth?.email)
6033
6493
  console.log(auth.email);
6034
6494
  else
6035
- consola40.info("Not logged in.");
6495
+ consola41.info("Not logged in.");
6036
6496
  break;
6037
6497
  }
6038
6498
  default: {
@@ -6045,7 +6505,7 @@ var configGetCommand = defineCommand48({
6045
6505
  if (sectionObj && field in sectionObj) {
6046
6506
  console.log(sectionObj[field]);
6047
6507
  } else {
6048
- consola40.info(`Key "${key}" not set.`);
6508
+ consola41.info(`Key "${key}" not set.`);
6049
6509
  }
6050
6510
  } else {
6051
6511
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -6056,9 +6516,9 @@ var configGetCommand = defineCommand48({
6056
6516
  });
6057
6517
 
6058
6518
  // src/commands/config/set.ts
6059
- import { defineCommand as defineCommand49 } from "citty";
6060
- import consola41 from "consola";
6061
- var configSetCommand = defineCommand49({
6519
+ import { defineCommand as defineCommand50 } from "citty";
6520
+ import consola42 from "consola";
6521
+ var configSetCommand = defineCommand50({
6062
6522
  meta: {
6063
6523
  name: "set",
6064
6524
  description: "Set a configuration value"
@@ -6094,12 +6554,12 @@ var configSetCommand = defineCommand49({
6094
6554
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
6095
6555
  }
6096
6556
  saveConfig(config);
6097
- consola41.success(`Set ${key} = ${value}`);
6557
+ consola42.success(`Set ${key} = ${value}`);
6098
6558
  }
6099
6559
  });
6100
6560
 
6101
6561
  // src/commands/fetch/index.ts
6102
- import { defineCommand as defineCommand50 } from "citty";
6562
+ import { defineCommand as defineCommand51 } from "citty";
6103
6563
  async function doRequest(method, url, body, contentType, raw, showHeaders) {
6104
6564
  const token = getAuthToken();
6105
6565
  if (!token) {
@@ -6135,13 +6595,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
6135
6595
  throw new CliError(`HTTP ${response.status} ${response.statusText}`);
6136
6596
  }
6137
6597
  }
6138
- var fetchCommand = defineCommand50({
6598
+ var fetchCommand = defineCommand51({
6139
6599
  meta: {
6140
6600
  name: "fetch",
6141
6601
  description: "Make authenticated HTTP requests"
6142
6602
  },
6143
6603
  subCommands: {
6144
- get: defineCommand50({
6604
+ get: defineCommand51({
6145
6605
  meta: {
6146
6606
  name: "get",
6147
6607
  description: "GET request with auth token"
@@ -6167,7 +6627,7 @@ var fetchCommand = defineCommand50({
6167
6627
  await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
6168
6628
  }
6169
6629
  }),
6170
- post: defineCommand50({
6630
+ post: defineCommand51({
6171
6631
  meta: {
6172
6632
  name: "post",
6173
6633
  description: "POST request with auth token"
@@ -6206,8 +6666,8 @@ var fetchCommand = defineCommand50({
6206
6666
  });
6207
6667
 
6208
6668
  // src/commands/mcp/index.ts
6209
- import { defineCommand as defineCommand51 } from "citty";
6210
- var mcpCommand = defineCommand51({
6669
+ import { defineCommand as defineCommand52 } from "citty";
6670
+ var mcpCommand = defineCommand52({
6211
6671
  meta: {
6212
6672
  name: "mcp",
6213
6673
  description: "Start MCP server for AI agents"
@@ -6230,25 +6690,25 @@ var mcpCommand = defineCommand51({
6230
6690
  if (transport !== "stdio" && transport !== "sse") {
6231
6691
  throw new Error('Transport must be "stdio" or "sse"');
6232
6692
  }
6233
- const { startMcpServer } = await import("./server-LT5BFMTP.js");
6693
+ const { startMcpServer } = await import("./server-ZBKJAUTT.js");
6234
6694
  await startMcpServer(transport, port);
6235
6695
  }
6236
6696
  });
6237
6697
 
6238
6698
  // src/commands/init/index.ts
6239
- import { existsSync as existsSync18, copyFileSync, writeFileSync as writeFileSync10 } from "fs";
6699
+ import { existsSync as existsSync19, copyFileSync, writeFileSync as writeFileSync10 } from "fs";
6240
6700
  import { randomBytes } from "crypto";
6241
6701
  import { execFileSync as execFileSync16 } from "child_process";
6242
- import { join as join17 } from "path";
6243
- import { defineCommand as defineCommand52 } from "citty";
6244
- import consola42 from "consola";
6702
+ import { join as join18 } from "path";
6703
+ import { defineCommand as defineCommand53 } from "citty";
6704
+ import consola43 from "consola";
6245
6705
  var DEFAULT_IDP_URL = "https://id.openape.at";
6246
6706
  async function downloadTemplate(repo, targetDir) {
6247
6707
  const { downloadTemplate: gigetDownload } = await import("giget");
6248
6708
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
6249
6709
  }
6250
6710
  function installDeps(dir) {
6251
- const hasLockFile = (name) => existsSync18(join17(dir, name));
6711
+ const hasLockFile = (name) => existsSync19(join18(dir, name));
6252
6712
  if (hasLockFile("pnpm-lock.yaml")) {
6253
6713
  execFileSync16("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6254
6714
  } else if (hasLockFile("bun.lockb")) {
@@ -6258,20 +6718,20 @@ function installDeps(dir) {
6258
6718
  }
6259
6719
  }
6260
6720
  async function promptChoice(message, choices) {
6261
- const result = await consola42.prompt(message, { type: "select", options: choices });
6721
+ const result = await consola43.prompt(message, { type: "select", options: choices });
6262
6722
  if (typeof result === "symbol") {
6263
6723
  throw new CliExit(0);
6264
6724
  }
6265
6725
  return result;
6266
6726
  }
6267
6727
  async function promptText(message, defaultValue) {
6268
- const result = await consola42.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
6728
+ const result = await consola43.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
6269
6729
  if (typeof result === "symbol") {
6270
6730
  throw new CliExit(0);
6271
6731
  }
6272
6732
  return result || defaultValue || "";
6273
6733
  }
6274
- var initCommand = defineCommand52({
6734
+ var initCommand = defineCommand53({
6275
6735
  meta: {
6276
6736
  name: "init",
6277
6737
  description: "Scaffold a new OpenApe project"
@@ -6313,23 +6773,23 @@ var initCommand = defineCommand52({
6313
6773
  });
6314
6774
  async function initSP(targetDir) {
6315
6775
  const dir = targetDir || "my-app";
6316
- if (existsSync18(join17(dir, "package.json"))) {
6776
+ if (existsSync19(join18(dir, "package.json"))) {
6317
6777
  throw new CliError(`Directory "${dir}" already contains a project.`);
6318
6778
  }
6319
- consola42.start("Scaffolding SP starter...");
6779
+ consola43.start("Scaffolding SP starter...");
6320
6780
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
6321
- consola42.success("Scaffolded from openape-sp-starter");
6322
- consola42.start("Installing dependencies...");
6781
+ consola43.success("Scaffolded from openape-sp-starter");
6782
+ consola43.start("Installing dependencies...");
6323
6783
  installDeps(dir);
6324
- consola42.success("Dependencies installed");
6325
- const envExample = join17(dir, ".env.example");
6326
- const envFile = join17(dir, ".env");
6327
- if (existsSync18(envExample) && !existsSync18(envFile)) {
6784
+ consola43.success("Dependencies installed");
6785
+ const envExample = join18(dir, ".env.example");
6786
+ const envFile = join18(dir, ".env");
6787
+ if (existsSync19(envExample) && !existsSync19(envFile)) {
6328
6788
  copyFileSync(envExample, envFile);
6329
- consola42.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
6789
+ consola43.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
6330
6790
  }
6331
6791
  console.log("");
6332
- consola42.box([
6792
+ consola43.box([
6333
6793
  `cd ${dir}`,
6334
6794
  "npm run dev",
6335
6795
  "",
@@ -6338,7 +6798,7 @@ async function initSP(targetDir) {
6338
6798
  }
6339
6799
  async function initIdP(targetDir) {
6340
6800
  const dir = targetDir || "my-idp";
6341
- if (existsSync18(join17(dir, "package.json"))) {
6801
+ if (existsSync19(join18(dir, "package.json"))) {
6342
6802
  throw new CliError(`Directory "${dir}" already contains a project.`);
6343
6803
  }
6344
6804
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -6348,15 +6808,15 @@ async function initIdP(targetDir) {
6348
6808
  "s3 (S3-compatible)"
6349
6809
  ]);
6350
6810
  const adminEmail = await promptText("Admin email");
6351
- consola42.start("Scaffolding IdP starter...");
6811
+ consola43.start("Scaffolding IdP starter...");
6352
6812
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
6353
- consola42.success("Scaffolded from openape-idp-starter");
6354
- consola42.start("Installing dependencies...");
6813
+ consola43.success("Scaffolded from openape-idp-starter");
6814
+ consola43.start("Installing dependencies...");
6355
6815
  installDeps(dir);
6356
- consola42.success("Dependencies installed");
6816
+ consola43.success("Dependencies installed");
6357
6817
  const sessionSecret = randomBytes(32).toString("hex");
6358
6818
  const managementToken = randomBytes(32).toString("hex");
6359
- consola42.success("Secrets generated");
6819
+ consola43.success("Secrets generated");
6360
6820
  const isLocalhost = domain === "localhost";
6361
6821
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
6362
6822
  const envContent = [
@@ -6370,11 +6830,11 @@ async function initIdP(targetDir) {
6370
6830
  `NUXT_OPENAPE_RP_ID=${domain}`,
6371
6831
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
6372
6832
  ].join("\n");
6373
- writeFileSync10(join17(dir, ".env"), `${envContent}
6833
+ writeFileSync10(join18(dir, ".env"), `${envContent}
6374
6834
  `, { mode: 384 });
6375
- consola42.success(".env created");
6835
+ consola43.success(".env created");
6376
6836
  console.log("");
6377
- consola42.box([
6837
+ consola43.box([
6378
6838
  `cd ${dir}`,
6379
6839
  "npm run dev",
6380
6840
  "",
@@ -6391,11 +6851,11 @@ async function initIdP(targetDir) {
6391
6851
 
6392
6852
  // src/commands/enroll.ts
6393
6853
  import { Buffer as Buffer5 } from "buffer";
6394
- import { existsSync as existsSync19, readFileSync as readFileSync14 } from "fs";
6854
+ import { existsSync as existsSync20, readFileSync as readFileSync15 } from "fs";
6395
6855
  import { execFile as execFile2 } from "child_process";
6396
6856
  import { sign as sign2 } from "crypto";
6397
- import { defineCommand as defineCommand53 } from "citty";
6398
- import consola43 from "consola";
6857
+ import { defineCommand as defineCommand54 } from "citty";
6858
+ import consola44 from "consola";
6399
6859
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
6400
6860
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
6401
6861
  var POLL_INTERVAL = 3e3;
@@ -6407,7 +6867,7 @@ function openBrowser2(url) {
6407
6867
  }
6408
6868
  async function pollForEnrollment(idp, agentEmail, keyPath) {
6409
6869
  const resolvedKey = resolveKeyPath(keyPath);
6410
- const keyContent = readFileSync14(resolvedKey, "utf-8");
6870
+ const keyContent = readFileSync15(resolvedKey, "utf-8");
6411
6871
  const privateKey = loadEd25519PrivateKey(keyContent);
6412
6872
  const challengeUrl = await getAgentChallengeEndpoint(idp);
6413
6873
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -6438,7 +6898,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
6438
6898
  }
6439
6899
  throw new Error("Enrollment timed out. Please check the browser and try again.");
6440
6900
  }
6441
- var enrollCommand = defineCommand53({
6901
+ var enrollCommand = defineCommand54({
6442
6902
  meta: {
6443
6903
  name: "enroll",
6444
6904
  description: "Enroll an agent with an Identity Provider"
@@ -6458,38 +6918,38 @@ var enrollCommand = defineCommand53({
6458
6918
  }
6459
6919
  },
6460
6920
  async run({ args }) {
6461
- const idp = args.idp || await consola43.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
6921
+ const idp = args.idp || await consola44.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
6462
6922
  if (typeof r === "symbol") throw new CliExit(0);
6463
6923
  return r;
6464
6924
  }) || DEFAULT_IDP_URL2;
6465
- const agentName = args.name || await consola43.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
6925
+ const agentName = args.name || await consola44.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
6466
6926
  if (typeof r === "symbol") throw new CliExit(0);
6467
6927
  return r;
6468
6928
  });
6469
6929
  if (!agentName) {
6470
6930
  throw new CliError("Agent name is required.");
6471
6931
  }
6472
- const keyPath = args.key || await consola43.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
6932
+ const keyPath = args.key || await consola44.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
6473
6933
  if (typeof r === "symbol") throw new CliExit(0);
6474
6934
  return r;
6475
6935
  }) || DEFAULT_KEY_PATH;
6476
6936
  const resolvedKey = resolveKeyPath(keyPath);
6477
6937
  let publicKey;
6478
- if (existsSync19(resolvedKey)) {
6938
+ if (existsSync20(resolvedKey)) {
6479
6939
  publicKey = readPublicKey(resolvedKey);
6480
- consola43.success(`Using existing key ${keyPath}`);
6940
+ consola44.success(`Using existing key ${keyPath}`);
6481
6941
  } else {
6482
- consola43.start(`Generating Ed25519 key pair at ${keyPath}...`);
6942
+ consola44.start(`Generating Ed25519 key pair at ${keyPath}...`);
6483
6943
  publicKey = generateAndSaveKey(keyPath);
6484
- consola43.success(`Key pair generated at ${keyPath}`);
6944
+ consola44.success(`Key pair generated at ${keyPath}`);
6485
6945
  }
6486
6946
  const encodedKey = encodeURIComponent(publicKey);
6487
6947
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
6488
- consola43.info("Opening browser for enrollment...");
6489
- consola43.info(`\u2192 ${idp}/enroll`);
6948
+ consola44.info("Opening browser for enrollment...");
6949
+ consola44.info(`\u2192 ${idp}/enroll`);
6490
6950
  openBrowser2(enrollUrl);
6491
6951
  console.log("");
6492
- const agentEmail = await consola43.prompt(
6952
+ const agentEmail = await consola44.prompt(
6493
6953
  "Agent email (shown in browser after enrollment)",
6494
6954
  { type: "text", placeholder: `agent+${agentName}@...` }
6495
6955
  ).then((r) => {
@@ -6499,7 +6959,7 @@ var enrollCommand = defineCommand53({
6499
6959
  if (!agentEmail) {
6500
6960
  throw new CliError("Agent email is required to verify enrollment.");
6501
6961
  }
6502
- consola43.start("Verifying enrollment...");
6962
+ consola44.start("Verifying enrollment...");
6503
6963
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
6504
6964
  saveAuth({
6505
6965
  idp,
@@ -6511,18 +6971,18 @@ var enrollCommand = defineCommand53({
6511
6971
  config.defaults = { ...config.defaults, idp };
6512
6972
  config.agent = { key: keyPath, email: agentEmail };
6513
6973
  saveConfig(config);
6514
- consola43.success(`Agent enrolled as ${agentEmail}`);
6515
- consola43.success("Config saved to ~/.config/apes/");
6974
+ consola44.success(`Agent enrolled as ${agentEmail}`);
6975
+ consola44.success("Config saved to ~/.config/apes/");
6516
6976
  console.log("");
6517
- consola43.info("Verify with: apes whoami");
6977
+ consola44.info("Verify with: apes whoami");
6518
6978
  }
6519
6979
  });
6520
6980
 
6521
6981
  // src/commands/register-user.ts
6522
- import { existsSync as existsSync20, readFileSync as readFileSync15 } from "fs";
6523
- import { defineCommand as defineCommand54 } from "citty";
6524
- import consola44 from "consola";
6525
- var registerUserCommand = defineCommand54({
6982
+ import { existsSync as existsSync21, readFileSync as readFileSync16 } from "fs";
6983
+ import { defineCommand as defineCommand55 } from "citty";
6984
+ import consola45 from "consola";
6985
+ var registerUserCommand = defineCommand55({
6526
6986
  meta: {
6527
6987
  name: "register-user",
6528
6988
  description: "Register a sub-user with SSH key"
@@ -6558,8 +7018,8 @@ var registerUserCommand = defineCommand54({
6558
7018
  throw new CliError("No IdP URL configured. Run `apes login` first.");
6559
7019
  }
6560
7020
  let publicKey = args.key;
6561
- if (existsSync20(args.key)) {
6562
- publicKey = readFileSync15(args.key, "utf-8").trim();
7021
+ if (existsSync21(args.key)) {
7022
+ publicKey = readFileSync16(args.key, "utf-8").trim();
6563
7023
  }
6564
7024
  if (!publicKey.startsWith("ssh-ed25519 ")) {
6565
7025
  throw new CliError("Public key must be in ssh-ed25519 format.");
@@ -6577,18 +7037,18 @@ var registerUserCommand = defineCommand54({
6577
7037
  ...userType ? { type: userType } : {}
6578
7038
  }
6579
7039
  });
6580
- consola44.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
7040
+ consola45.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
6581
7041
  }
6582
7042
  });
6583
7043
 
6584
7044
  // src/commands/utils/index.ts
6585
- import { defineCommand as defineCommand56 } from "citty";
7045
+ import { defineCommand as defineCommand57 } from "citty";
6586
7046
 
6587
7047
  // src/commands/utils/dig.ts
6588
- import { defineCommand as defineCommand55 } from "citty";
6589
- import consola45 from "consola";
7048
+ import { defineCommand as defineCommand56 } from "citty";
7049
+ import consola46 from "consola";
6590
7050
  import { resolveDDISA as resolveDDISA2 } from "@openape/core";
6591
- var digCommand = defineCommand55({
7051
+ var digCommand = defineCommand56({
6592
7052
  meta: {
6593
7053
  name: "dig",
6594
7054
  description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
@@ -6661,12 +7121,12 @@ var digCommand = defineCommand55({
6661
7121
  console.log(` domain: ${domain}`);
6662
7122
  console.log("");
6663
7123
  if (!result.ddisa.found) {
6664
- consola45.warn(`No DDISA record at _ddisa.${domain}`);
7124
+ consola46.warn(`No DDISA record at _ddisa.${domain}`);
6665
7125
  if (result.hint) console.log(`
6666
7126
  ${result.hint}`);
6667
7127
  throw new CliError(`No DDISA record found for ${domain}`);
6668
7128
  }
6669
- consola45.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
7129
+ consola46.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
6670
7130
  console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
6671
7131
  console.log(` IdP URL: ${result.ddisa.idp}`);
6672
7132
  if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
@@ -6676,13 +7136,13 @@ ${result.hint}`);
6676
7136
  return;
6677
7137
  }
6678
7138
  if (result.idpDiscovery.ok) {
6679
- consola45.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
7139
+ consola46.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
6680
7140
  if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
6681
7141
  if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
6682
7142
  if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
6683
7143
  if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
6684
7144
  } else {
6685
- consola45.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
7145
+ consola46.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
6686
7146
  if (result.hint) console.log(`
6687
7147
  ${result.hint}`);
6688
7148
  throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
@@ -6691,7 +7151,7 @@ ${result.hint}`);
6691
7151
  });
6692
7152
 
6693
7153
  // src/commands/utils/index.ts
6694
- var utilsCommand = defineCommand56({
7154
+ var utilsCommand = defineCommand57({
6695
7155
  meta: {
6696
7156
  name: "utils",
6697
7157
  description: "Admin/diagnostic utilities (dig, \u2026)"
@@ -6702,12 +7162,12 @@ var utilsCommand = defineCommand56({
6702
7162
  });
6703
7163
 
6704
7164
  // src/commands/sessions/index.ts
6705
- import { defineCommand as defineCommand59 } from "citty";
7165
+ import { defineCommand as defineCommand60 } from "citty";
6706
7166
 
6707
7167
  // src/commands/sessions/list.ts
6708
- import { defineCommand as defineCommand57 } from "citty";
6709
- import consola46 from "consola";
6710
- var sessionsListCommand = defineCommand57({
7168
+ import { defineCommand as defineCommand58 } from "citty";
7169
+ import consola47 from "consola";
7170
+ var sessionsListCommand = defineCommand58({
6711
7171
  meta: {
6712
7172
  name: "list",
6713
7173
  description: "List your active refresh-token families (one per logged-in device)."
@@ -6725,7 +7185,7 @@ var sessionsListCommand = defineCommand57({
6725
7185
  return;
6726
7186
  }
6727
7187
  if (result.data.length === 0) {
6728
- consola46.info("No active sessions.");
7188
+ consola47.info("No active sessions.");
6729
7189
  return;
6730
7190
  }
6731
7191
  for (const f of result.data) {
@@ -6737,9 +7197,9 @@ var sessionsListCommand = defineCommand57({
6737
7197
  });
6738
7198
 
6739
7199
  // src/commands/sessions/remove.ts
6740
- import { defineCommand as defineCommand58 } from "citty";
6741
- import consola47 from "consola";
6742
- var sessionsRemoveCommand = defineCommand58({
7200
+ import { defineCommand as defineCommand59 } from "citty";
7201
+ import consola48 from "consola";
7202
+ var sessionsRemoveCommand = defineCommand59({
6743
7203
  meta: {
6744
7204
  name: "remove",
6745
7205
  description: "Revoke one of your active refresh-token families by id."
@@ -6755,12 +7215,12 @@ var sessionsRemoveCommand = defineCommand58({
6755
7215
  const id = String(args.familyId).trim();
6756
7216
  if (!id) throw new CliError("familyId required");
6757
7217
  await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
6758
- consola47.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
7218
+ consola48.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
6759
7219
  }
6760
7220
  });
6761
7221
 
6762
7222
  // src/commands/sessions/index.ts
6763
- var sessionsCommand = defineCommand59({
7223
+ var sessionsCommand = defineCommand60({
6764
7224
  meta: {
6765
7225
  name: "sessions",
6766
7226
  description: "Manage your active refresh-token sessions across devices"
@@ -6772,10 +7232,10 @@ var sessionsCommand = defineCommand59({
6772
7232
  });
6773
7233
 
6774
7234
  // src/commands/dns-check.ts
6775
- import { defineCommand as defineCommand60 } from "citty";
6776
- import consola48 from "consola";
7235
+ import { defineCommand as defineCommand61 } from "citty";
7236
+ import consola49 from "consola";
6777
7237
  import { resolveDDISA as resolveDDISA3 } from "@openape/core";
6778
- var dnsCheckCommand = defineCommand60({
7238
+ var dnsCheckCommand = defineCommand61({
6779
7239
  meta: {
6780
7240
  name: "dns-check",
6781
7241
  description: "Validate DDISA DNS TXT records for a domain"
@@ -6789,7 +7249,7 @@ var dnsCheckCommand = defineCommand60({
6789
7249
  },
6790
7250
  async run({ args }) {
6791
7251
  const domain = args.domain;
6792
- consola48.start(`Checking _ddisa.${domain}...`);
7252
+ consola49.start(`Checking _ddisa.${domain}...`);
6793
7253
  try {
6794
7254
  const result = await resolveDDISA3(domain);
6795
7255
  if (!result) {
@@ -6798,7 +7258,7 @@ var dnsCheckCommand = defineCommand60({
6798
7258
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
6799
7259
  throw new CliError(`No DDISA record found for ${domain}`);
6800
7260
  }
6801
- consola48.success(`_ddisa.${domain} \u2192 ${result.idp}`);
7261
+ consola49.success(`_ddisa.${domain} \u2192 ${result.idp}`);
6802
7262
  console.log("");
6803
7263
  console.log(` Version: ${result.version || "ddisa1"}`);
6804
7264
  console.log(` IdP URL: ${result.idp}`);
@@ -6807,14 +7267,14 @@ var dnsCheckCommand = defineCommand60({
6807
7267
  if (result.priority !== void 0)
6808
7268
  console.log(` Priority: ${result.priority}`);
6809
7269
  console.log("");
6810
- consola48.start(`Verifying IdP at ${result.idp}...`);
7270
+ consola49.start(`Verifying IdP at ${result.idp}...`);
6811
7271
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
6812
7272
  if (!discoResp.ok) {
6813
- consola48.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
7273
+ consola49.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
6814
7274
  return;
6815
7275
  }
6816
7276
  const disco = await discoResp.json();
6817
- consola48.success(`IdP is reachable`);
7277
+ consola49.success(`IdP is reachable`);
6818
7278
  console.log(` Issuer: ${disco.issuer}`);
6819
7279
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
6820
7280
  if (disco.ddisa_auth_methods_supported) {
@@ -6832,7 +7292,7 @@ var dnsCheckCommand = defineCommand60({
6832
7292
  // src/commands/health.ts
6833
7293
  import { exec } from "child_process";
6834
7294
  import { promisify } from "util";
6835
- import { defineCommand as defineCommand61 } from "citty";
7295
+ import { defineCommand as defineCommand62 } from "citty";
6836
7296
  var execAsync = promisify(exec);
6837
7297
  async function resolveApeShellPath() {
6838
7298
  try {
@@ -6868,7 +7328,7 @@ async function bestEffortGrantCount(idp) {
6868
7328
  }
6869
7329
  }
6870
7330
  async function runHealth(args) {
6871
- const version = true ? "1.25.1" : "0.0.0";
7331
+ const version = true ? "1.27.0" : "0.0.0";
6872
7332
  const auth = loadAuth();
6873
7333
  if (!auth) {
6874
7334
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -6931,7 +7391,7 @@ async function runHealth(args) {
6931
7391
  throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
6932
7392
  }
6933
7393
  }
6934
- var healthCommand = defineCommand61({
7394
+ var healthCommand = defineCommand62({
6935
7395
  meta: {
6936
7396
  name: "health",
6937
7397
  description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
@@ -6949,8 +7409,8 @@ var healthCommand = defineCommand61({
6949
7409
  });
6950
7410
 
6951
7411
  // src/commands/workflows.ts
6952
- import { defineCommand as defineCommand62 } from "citty";
6953
- import consola49 from "consola";
7412
+ import { defineCommand as defineCommand63 } from "citty";
7413
+ import consola50 from "consola";
6954
7414
 
6955
7415
  // src/guides/index.ts
6956
7416
  var guides = [
@@ -7000,7 +7460,7 @@ var guides = [
7000
7460
  ];
7001
7461
 
7002
7462
  // src/commands/workflows.ts
7003
- var workflowsCommand = defineCommand62({
7463
+ var workflowsCommand = defineCommand63({
7004
7464
  meta: {
7005
7465
  name: "workflows",
7006
7466
  description: "Discover workflow guides"
@@ -7021,7 +7481,7 @@ var workflowsCommand = defineCommand62({
7021
7481
  if (args.id) {
7022
7482
  const guide = guides.find((g) => g.id === String(args.id));
7023
7483
  if (!guide) {
7024
- consola49.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
7484
+ consola50.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
7025
7485
  throw new CliError(`Guide not found: ${args.id}`);
7026
7486
  }
7027
7487
  if (args.json) {
@@ -7061,25 +7521,25 @@ var workflowsCommand = defineCommand62({
7061
7521
  });
7062
7522
 
7063
7523
  // src/version-check.ts
7064
- import { existsSync as existsSync21, mkdirSync as mkdirSync6, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "fs";
7065
- import { homedir as homedir15 } from "os";
7066
- import { join as join18 } from "path";
7067
- import consola50 from "consola";
7524
+ import { existsSync as existsSync22, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
7525
+ import { homedir as homedir16 } from "os";
7526
+ import { join as join19 } from "path";
7527
+ import consola51 from "consola";
7068
7528
  var PACKAGE_NAME = "@openape/apes";
7069
7529
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
7070
- var CACHE_FILE = join18(homedir15(), ".config", "apes", ".version-check.json");
7530
+ var CACHE_FILE = join19(homedir16(), ".config", "apes", ".version-check.json");
7071
7531
  function readCache() {
7072
- if (!existsSync21(CACHE_FILE)) return null;
7532
+ if (!existsSync22(CACHE_FILE)) return null;
7073
7533
  try {
7074
- return JSON.parse(readFileSync16(CACHE_FILE, "utf-8"));
7534
+ return JSON.parse(readFileSync17(CACHE_FILE, "utf-8"));
7075
7535
  } catch {
7076
7536
  return null;
7077
7537
  }
7078
7538
  }
7079
7539
  function writeCache(entry) {
7080
7540
  try {
7081
- const dir = join18(homedir15(), ".config", "apes");
7082
- if (!existsSync21(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
7541
+ const dir = join19(homedir16(), ".config", "apes");
7542
+ if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
7083
7543
  writeFileSync11(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
7084
7544
  } catch {
7085
7545
  }
@@ -7109,7 +7569,7 @@ async function fetchLatestVersion() {
7109
7569
  }
7110
7570
  function warnIfBehind(currentVersion, latest) {
7111
7571
  if (compareSemver(currentVersion, latest) < 0) {
7112
- consola50.warn(
7572
+ consola51.warn(
7113
7573
  `apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
7114
7574
  );
7115
7575
  }
@@ -7141,10 +7601,10 @@ if (shellRewrite) {
7141
7601
  if (shellRewrite.action === "rewrite") {
7142
7602
  process.argv = shellRewrite.argv;
7143
7603
  } else if (shellRewrite.action === "version") {
7144
- console.log(`ape-shell ${"1.25.1"} (OpenApe DDISA shell wrapper)`);
7604
+ console.log(`ape-shell ${"1.27.0"} (OpenApe DDISA shell wrapper)`);
7145
7605
  process.exit(0);
7146
7606
  } else if (shellRewrite.action === "help") {
7147
- console.log(`ape-shell ${"1.25.1"} \u2014 OpenApe DDISA shell wrapper`);
7607
+ console.log(`ape-shell ${"1.27.0"} \u2014 OpenApe DDISA shell wrapper`);
7148
7608
  console.log("");
7149
7609
  console.log("Usage:");
7150
7610
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -7168,7 +7628,7 @@ if (shellRewrite) {
7168
7628
  }
7169
7629
  }
7170
7630
  var debug = process.argv.includes("--debug");
7171
- var grantsCommand = defineCommand63({
7631
+ var grantsCommand = defineCommand64({
7172
7632
  meta: {
7173
7633
  name: "grants",
7174
7634
  description: "Grant management"
@@ -7189,7 +7649,7 @@ var grantsCommand = defineCommand63({
7189
7649
  "delegation-revoke": delegationRevokeCommand
7190
7650
  }
7191
7651
  });
7192
- var configCommand = defineCommand63({
7652
+ var configCommand = defineCommand64({
7193
7653
  meta: {
7194
7654
  name: "config",
7195
7655
  description: "Configuration management"
@@ -7199,10 +7659,10 @@ var configCommand = defineCommand63({
7199
7659
  set: configSetCommand
7200
7660
  }
7201
7661
  });
7202
- var main = defineCommand63({
7662
+ var main = defineCommand64({
7203
7663
  meta: {
7204
7664
  name: "apes",
7205
- version: "1.25.1",
7665
+ version: "1.27.0",
7206
7666
  description: "Unified CLI for OpenApe"
7207
7667
  },
7208
7668
  subCommands: {
@@ -7260,20 +7720,20 @@ async function maybeRefreshAuth() {
7260
7720
  }
7261
7721
  }
7262
7722
  await maybeRefreshAuth();
7263
- await maybeWarnStaleVersion("1.25.1").catch(() => {
7723
+ await maybeWarnStaleVersion("1.27.0").catch(() => {
7264
7724
  });
7265
7725
  runMain(main).catch((err) => {
7266
7726
  if (err instanceof CliExit) {
7267
7727
  process.exit(err.exitCode);
7268
7728
  }
7269
7729
  if (err instanceof CliError) {
7270
- consola51.error(err.message);
7730
+ consola52.error(err.message);
7271
7731
  process.exit(err.exitCode);
7272
7732
  }
7273
7733
  if (debug) {
7274
- consola51.error(err);
7734
+ consola52.error(err);
7275
7735
  } else {
7276
- consola51.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
7736
+ consola52.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
7277
7737
  }
7278
7738
  process.exit(1);
7279
7739
  });