@openape/apes 1.5.0 → 1.6.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
@@ -63,7 +63,7 @@ import {
63
63
  } from "./chunk-OBF7IMQ2.js";
64
64
 
65
65
  // src/cli.ts
66
- import consola43 from "consola";
66
+ import consola44 from "consola";
67
67
 
68
68
  // src/ape-shell.ts
69
69
  import path from "path";
@@ -93,7 +93,7 @@ function rewriteApeShellArgs(argv, argv0) {
93
93
  }
94
94
 
95
95
  // src/cli.ts
96
- import { defineCommand as defineCommand53, runMain } from "citty";
96
+ import { defineCommand as defineCommand54, runMain } from "citty";
97
97
 
98
98
  // src/commands/auth/login.ts
99
99
  import { Buffer as Buffer2 } from "buffer";
@@ -379,7 +379,7 @@ async function loginWithPKCE(idp) {
379
379
  consola2.success(`Logged in as ${payload.email || payload.sub}`);
380
380
  }
381
381
  async function loginWithKey(idp, keyPath, agentEmail) {
382
- const { readFileSync: readFileSync14 } = await import("fs");
382
+ const { readFileSync: readFileSync15 } = await import("fs");
383
383
  const { sign: sign3 } = await import("crypto");
384
384
  const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
385
385
  const challengeUrl = await getAgentChallengeEndpoint(idp);
@@ -392,7 +392,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
392
392
  throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
393
393
  }
394
394
  const { challenge } = await challengeResp.json();
395
- const keyContent = readFileSync14(keyPath, "utf-8");
395
+ const keyContent = readFileSync15(keyPath, "utf-8");
396
396
  const privateKey = loadEd25519PrivateKey2(keyContent);
397
397
  const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
398
398
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -3918,71 +3918,186 @@ var agentsCommand = defineCommand28({
3918
3918
  });
3919
3919
 
3920
3920
  // src/commands/nest/index.ts
3921
- import { defineCommand as defineCommand33 } from "citty";
3921
+ import { defineCommand as defineCommand34 } from "citty";
3922
3922
 
3923
3923
  // src/commands/nest/authorize.ts
3924
- import { execFileSync as execFileSync9 } from "child_process";
3924
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
3925
+ import { join as join9 } from "path";
3926
+ import { defineCommand as defineCommand30 } from "citty";
3927
+ import consola26 from "consola";
3928
+
3929
+ // src/commands/nest/enroll.ts
3930
+ import { hostname as hostname4, homedir as homedir10 } from "os";
3931
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, chmodSync } from "fs";
3932
+ import { join as join8 } from "path";
3925
3933
  import { defineCommand as defineCommand29 } from "citty";
3926
3934
  import consola25 from "consola";
3927
- var authorizeNestCommand = defineCommand29({
3935
+ var NEST_DATA_DIR = join8(homedir10(), ".openape", "nest");
3936
+ function nestAgentName() {
3937
+ const raw = hostname4().toLowerCase();
3938
+ const head = raw.split(".")[0] ?? raw;
3939
+ const safe = head.replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
3940
+ const trimmed = safe.slice(0, 16);
3941
+ return `nest-${trimmed || "host"}`;
3942
+ }
3943
+ var enrollNestCommand = defineCommand29({
3928
3944
  meta: {
3929
- name: "authorize",
3930
- description: "Request the always-capability-grant the nest needs for zero-prompt spawn/destroy"
3945
+ name: "enroll",
3946
+ 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."
3931
3947
  },
3932
3948
  args: {
3933
- "reason": {
3949
+ name: {
3934
3950
  type: "string",
3935
- description: "Reason shown in the DDISA approval UI"
3951
+ description: "Override the nest agent name (default: nest-<short-hostname>)"
3936
3952
  },
3937
- "wait": {
3953
+ force: {
3938
3954
  type: "boolean",
3939
- description: "Block until the grant is approved (default: print URL + exit 0)"
3955
+ description: "Re-enroll even if ~/.openape/nest/.config/apes/auth.json already exists"
3940
3956
  }
3941
3957
  },
3942
3958
  async run({ args }) {
3943
- const reason = args.reason ?? "nest-managed agent lifecycle (spawn / destroy / sync) \u2014 approve as Always";
3944
- consola25.info("Requesting capability-grant for `apes-agents` (selector name=* covers all agent names)...");
3959
+ const idp = getIdpUrl();
3960
+ if (!idp) {
3961
+ throw new CliError("No IdP configured. Run `apes login <email>` first.");
3962
+ }
3963
+ const ownerAuth = loadAuth();
3964
+ if (!ownerAuth?.email) {
3965
+ throw new CliError("Run `apes login <email>` first \u2014 nest enroll attaches the new identity to your owner account.");
3966
+ }
3967
+ const name = args.name || nestAgentName();
3968
+ const authPath = join8(NEST_DATA_DIR, ".config", "apes", "auth.json");
3969
+ if (existsSync10(authPath) && !args.force) {
3970
+ throw new CliError(`Nest already enrolled at ${authPath}. Pass --force to re-enroll.`);
3971
+ }
3972
+ const sshDir = join8(NEST_DATA_DIR, ".ssh");
3973
+ const configDir = join8(NEST_DATA_DIR, ".config", "apes");
3974
+ mkdirSync4(sshDir, { recursive: true });
3975
+ mkdirSync4(configDir, { recursive: true });
3976
+ consola25.start(`Generating keypair for ${name}\u2026`);
3977
+ const { privatePem, publicSshLine } = generateKeyPairInMemory();
3978
+ writeFileSync6(join8(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
3979
+ `, { mode: 384 });
3980
+ writeFileSync6(join8(sshDir, "id_ed25519.pub"), `${publicSshLine}
3981
+ `, { mode: 420 });
3982
+ chmodSync(sshDir, 448);
3983
+ consola25.start(`Registering nest at ${idp}\u2026`);
3984
+ const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
3985
+ consola25.success(`Registered as ${registration.email}`);
3986
+ consola25.start("Issuing nest access token\u2026");
3987
+ const { token, expiresIn } = await issueAgentToken({
3988
+ idp,
3989
+ agentEmail: registration.email,
3990
+ privateKeyPem: privatePem
3991
+ });
3992
+ const authJson = buildAgentAuthJson({
3993
+ idp,
3994
+ accessToken: token,
3995
+ email: registration.email,
3996
+ expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
3997
+ keyPath: join8(sshDir, "id_ed25519"),
3998
+ ownerEmail: ownerAuth.email
3999
+ });
4000
+ writeFileSync6(authPath, authJson, { mode: 384 });
4001
+ chmodSync(configDir, 448);
4002
+ consola25.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
3945
4003
  consola25.info("");
3946
- consola25.info("When the IdP approval page opens, choose **Always** so the nest can re-use the grant on every spawn.");
4004
+ consola25.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
3947
4005
  consola25.info("");
3948
- const cmdArgs = [
3949
- "grants",
3950
- "request-capability",
3951
- "apes-agents",
3952
- "--resource",
3953
- "agents:*",
3954
- "--selector",
3955
- "name=*",
3956
- "--action",
3957
- "create",
3958
- "--action",
3959
- "delete",
3960
- "--action",
3961
- "edit",
3962
- "--action",
3963
- "list",
3964
- "--approval=always",
3965
- "--reason",
3966
- reason,
3967
- "--run-as",
3968
- "root"
3969
- ];
3970
- if (args.wait) cmdArgs.push("--wait");
3971
- try {
3972
- execFileSync9("apes", cmdArgs, { stdio: "inherit" });
3973
- } catch (err) {
3974
- throw new Error(err instanceof Error ? err.message : String(err));
4006
+ consola25.info(" apes nest authorize");
4007
+ }
4008
+ });
4009
+
4010
+ // src/commands/nest/authorize.ts
4011
+ var DEFAULT_ALLOW_PATTERNS = [
4012
+ // Agent lifecycle ops the nest issues against `apes run --as root`
4013
+ "apes agents spawn *",
4014
+ "apes agents destroy *",
4015
+ "apes agents sync",
4016
+ // Bridge invocation the supervisor uses to keep agent processes
4017
+ // running. Pattern is intentionally precise — not a generic
4018
+ // `apes run --as *` wildcard — so a compromised nest can't pivot
4019
+ // to running arbitrary commands as arbitrary users.
4020
+ "apes run --as * -- openape-chat-bridge"
4021
+ ];
4022
+ var authorizeNestCommand = defineCommand30({
4023
+ meta: {
4024
+ name: "authorize",
4025
+ description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts"
4026
+ },
4027
+ args: {
4028
+ "allow": {
4029
+ type: "string",
4030
+ description: "Override allow_patterns (comma-separated globs). Default: nest-managed agent lifecycle."
4031
+ },
4032
+ "mode": {
4033
+ type: "string",
4034
+ description: "Policy mode (allow-list | deny-list). Default: allow-list \u2014 auto-approve only matched patterns."
4035
+ },
4036
+ "expires-in": {
4037
+ type: "string",
4038
+ description: "Optional duration like 30d, 6h. Omit for no expiry."
4039
+ }
4040
+ },
4041
+ async run({ args }) {
4042
+ const ownerAuth = loadAuth();
4043
+ if (!ownerAuth?.email || !ownerAuth.access_token) {
4044
+ throw new CliError("Run `apes login <email>` first \u2014 only the nest's owner can set its YOLO-policy.");
4045
+ }
4046
+ const nestAuthPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
4047
+ if (!existsSync11(nestAuthPath)) {
4048
+ throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
4049
+ }
4050
+ const nestAuth = JSON.parse(readFileSync10(nestAuthPath, "utf8"));
4051
+ if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
4052
+ const idp = getIdpUrl();
4053
+ if (!idp) throw new CliError("No IdP configured.");
4054
+ const allowPatterns = typeof args.allow === "string" && args.allow ? args.allow.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_ALLOW_PATTERNS;
4055
+ const mode = args.mode ?? "allow-list";
4056
+ const expiresAt = parseExpiresIn(args["expires-in"]);
4057
+ consola26.info(`Setting YOLO-policy on ${nestAuth.email}`);
4058
+ consola26.info(` mode: ${mode}`);
4059
+ consola26.info(` allow_patterns:`);
4060
+ for (const p of allowPatterns) consola26.info(` - ${p}`);
4061
+ if (expiresAt) consola26.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
4062
+ const url = `${idp}/api/users/${encodeURIComponent(nestAuth.email)}/yolo-policy`;
4063
+ const res = await fetch(url, {
4064
+ method: "PUT",
4065
+ headers: {
4066
+ "Authorization": `Bearer ${ownerAuth.access_token}`,
4067
+ "Content-Type": "application/json"
4068
+ },
4069
+ body: JSON.stringify({
4070
+ mode,
4071
+ allowPatterns,
4072
+ denyPatterns: [],
4073
+ expiresAt: expiresAt ?? null
4074
+ })
4075
+ });
4076
+ if (!res.ok) {
4077
+ const text = await res.text().catch(() => "");
4078
+ throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
3975
4079
  }
4080
+ consola26.success("YOLO-policy applied. Nest-driven agent lifecycle is now zero-prompt.");
4081
+ consola26.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
3976
4082
  }
3977
4083
  });
4084
+ function parseExpiresIn(s) {
4085
+ if (!s) return null;
4086
+ const m = s.match(/^(\d+)([hdw])$/);
4087
+ if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
4088
+ const n = Number(m[1]);
4089
+ const unit = m[2];
4090
+ const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
4091
+ return Math.floor(Date.now() / 1e3) + n * seconds;
4092
+ }
3978
4093
 
3979
4094
  // src/commands/nest/install.ts
3980
- import { execFileSync as execFileSync10 } from "child_process";
3981
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
3982
- import { homedir as homedir10, userInfo as userInfo2 } from "os";
3983
- import { dirname as dirname3, join as join8 } from "path";
3984
- import { defineCommand as defineCommand30 } from "citty";
3985
- import consola26 from "consola";
4095
+ import { execFileSync as execFileSync9 } from "child_process";
4096
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
4097
+ import { homedir as homedir11, userInfo as userInfo2 } from "os";
4098
+ import { dirname as dirname3, join as join10 } from "path";
4099
+ import { defineCommand as defineCommand31 } from "citty";
4100
+ import consola27 from "consola";
3986
4101
 
3987
4102
  // src/commands/nest/apes-agents-adapter.ts
3988
4103
  var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
@@ -4049,13 +4164,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
4049
4164
  // src/commands/nest/install.ts
4050
4165
  var PLIST_LABEL = "ai.openape.nest";
4051
4166
  function plistPath() {
4052
- return join8(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4167
+ return join10(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4053
4168
  }
4054
4169
  function escape2(s) {
4055
4170
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4056
4171
  }
4057
4172
  function buildPlist(args) {
4058
- const logsDir = join8(args.homeDir, "Library", "Logs");
4173
+ const logsDir = join10(args.userHome, "Library", "Logs");
4059
4174
  return `<?xml version="1.0" encoding="UTF-8"?>
4060
4175
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4061
4176
  <plist version="1.0">
@@ -4067,7 +4182,7 @@ function buildPlist(args) {
4067
4182
  <string>${escape2(args.nestBin)}</string>
4068
4183
  </array>
4069
4184
  <key>WorkingDirectory</key>
4070
- <string>${escape2(args.homeDir)}</string>
4185
+ <string>${escape2(args.nestHome)}</string>
4071
4186
  <key>RunAtLoad</key>
4072
4187
  <true/>
4073
4188
  <key>KeepAlive</key>
@@ -4076,8 +4191,8 @@ function buildPlist(args) {
4076
4191
  <integer>10</integer>
4077
4192
  <key>EnvironmentVariables</key>
4078
4193
  <dict>
4079
- <key>HOME</key><string>${escape2(args.homeDir)}</string>
4080
- <key>PATH</key><string>${escape2(args.homeDir)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
4194
+ <key>HOME</key><string>${escape2(args.nestHome)}</string>
4195
+ <key>PATH</key><string>${escape2(args.userHome)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
4081
4196
  <key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
4082
4197
  <key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
4083
4198
  </dict>
@@ -4090,31 +4205,31 @@ function buildPlist(args) {
4090
4205
  `;
4091
4206
  }
4092
4207
  function installAdapter2() {
4093
- const target = join8(homedir10(), ".openape", "shapes", "adapters", "apes-agents.toml");
4094
- mkdirSync4(dirname3(target), { recursive: true });
4208
+ const target = join10(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
4209
+ mkdirSync5(dirname3(target), { recursive: true });
4095
4210
  let existing = "";
4096
4211
  try {
4097
- existing = readFileSync10(target, "utf8");
4212
+ existing = readFileSync11(target, "utf8");
4098
4213
  } catch {
4099
4214
  }
4100
4215
  if (existing === APES_AGENTS_ADAPTER_TOML) return false;
4101
- writeFileSync6(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4102
- consola26.success(`Wrote shapes adapter ${target}`);
4216
+ writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4217
+ consola27.success(`Wrote shapes adapter ${target}`);
4103
4218
  return true;
4104
4219
  }
4105
4220
  function findBinary(name) {
4106
4221
  for (const dir of [
4107
- join8(homedir10(), ".bun", "bin"),
4222
+ join10(homedir11(), ".bun", "bin"),
4108
4223
  "/opt/homebrew/bin",
4109
4224
  "/usr/local/bin",
4110
4225
  "/usr/bin"
4111
4226
  ]) {
4112
- const p = join8(dir, name);
4113
- if (existsSync10(p)) return p;
4227
+ const p = join10(dir, name);
4228
+ if (existsSync12(p)) return p;
4114
4229
  }
4115
4230
  throw new Error(`could not locate ${name} on PATH; install it first`);
4116
4231
  }
4117
- var installNestCommand = defineCommand30({
4232
+ var installNestCommand = defineCommand31({
4118
4233
  meta: {
4119
4234
  name: "install",
4120
4235
  description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
@@ -4126,54 +4241,55 @@ var installNestCommand = defineCommand30({
4126
4241
  }
4127
4242
  },
4128
4243
  async run({ args }) {
4129
- const homeDir = homedir10();
4244
+ const homeDir = homedir11();
4130
4245
  const port = Number(args.port ?? 9091);
4131
4246
  if (!Number.isInteger(port) || port < 1024 || port > 65535) {
4132
4247
  throw new Error(`invalid port ${port}`);
4133
4248
  }
4134
4249
  const nestBin = findBinary("openape-nest");
4135
4250
  const apesBin = findBinary("apes");
4136
- consola26.info(`Installing nest at ${plistPath()}`);
4137
- consola26.info(` nest binary: ${nestBin}`);
4138
- consola26.info(` apes binary: ${apesBin}`);
4139
- consola26.info(` HTTP port: ${port}`);
4251
+ consola27.info(`Installing nest at ${plistPath()}`);
4252
+ consola27.info(` nest binary: ${nestBin}`);
4253
+ consola27.info(` apes binary: ${apesBin}`);
4254
+ consola27.info(` HTTP port: ${port}`);
4140
4255
  installAdapter2();
4141
- mkdirSync4(join8(homeDir, "Library", "LaunchAgents"), { recursive: true });
4142
- const desired = buildPlist({ nestBin, apesBin, homeDir, port });
4256
+ mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
4257
+ mkdirSync5(NEST_DATA_DIR, { recursive: true });
4258
+ const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
4143
4259
  let existing = "";
4144
4260
  try {
4145
- existing = readFileSync10(plistPath(), "utf8");
4261
+ existing = readFileSync11(plistPath(), "utf8");
4146
4262
  } catch {
4147
4263
  }
4148
4264
  if (existing !== desired) {
4149
- writeFileSync6(plistPath(), desired, { mode: 420 });
4150
- consola26.success("Wrote launchd plist");
4265
+ writeFileSync7(plistPath(), desired, { mode: 420 });
4266
+ consola27.success("Wrote launchd plist");
4151
4267
  } else {
4152
- consola26.info("plist already up to date");
4268
+ consola27.info("plist already up to date");
4153
4269
  }
4154
4270
  const uid = userInfo2().uid;
4155
4271
  try {
4156
- execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4272
+ execFileSync9("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4157
4273
  } catch {
4158
4274
  }
4159
- execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4160
- consola26.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4161
- consola26.info("");
4162
- consola26.info("Next: request the capability-grant that lets the nest spawn/destroy any agent without per-call approval:");
4163
- consola26.info("");
4164
- consola26.info(" apes nest authorize");
4165
- consola26.info("");
4166
- consola26.info("That requests an `apes-agents` capability-grant covering all agent names (selector glob `name=*`)");
4167
- consola26.info("from your DDISA inbox; approve it once as `always` and the nest API runs silently from then on.");
4275
+ execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4276
+ consola27.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4277
+ consola27.info("");
4278
+ consola27.info("Next steps for zero-prompt spawn \u2014 both one-time:");
4279
+ consola27.info("");
4280
+ consola27.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
4281
+ consola27.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
4282
+ consola27.info("");
4283
+ consola27.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
4168
4284
  }
4169
4285
  });
4170
4286
 
4171
4287
  // src/commands/nest/status.ts
4172
4288
  import process2 from "process";
4173
- import { defineCommand as defineCommand31 } from "citty";
4174
- import consola27 from "consola";
4289
+ import { defineCommand as defineCommand32 } from "citty";
4290
+ import consola28 from "consola";
4175
4291
  var DEFAULT_PORT = 9091;
4176
- var statusNestCommand = defineCommand31({
4292
+ var statusNestCommand = defineCommand32({
4177
4293
  meta: {
4178
4294
  name: "status",
4179
4295
  description: "Print state of the local nest-daemon (agents registered, processes supervised)"
@@ -4193,8 +4309,8 @@ var statusNestCommand = defineCommand31({
4193
4309
  } catch (err) {
4194
4310
  const msg = err instanceof Error ? err.message : String(err);
4195
4311
  if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
4196
- consola27.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
4197
- consola27.info(" Run: apes nest install");
4312
+ consola28.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
4313
+ consola28.info(" Run: apes nest install");
4198
4314
  process2.exit(2);
4199
4315
  }
4200
4316
  throw err;
@@ -4203,15 +4319,15 @@ var statusNestCommand = defineCommand31({
4203
4319
  console.log(JSON.stringify(status, null, 2));
4204
4320
  return;
4205
4321
  }
4206
- consola27.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
4322
+ consola28.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
4207
4323
  if (status.processes.length === 0) {
4208
- consola27.info(" (no processes running)");
4324
+ consola28.info(" (no processes running)");
4209
4325
  return;
4210
4326
  }
4211
4327
  for (const p of status.processes) {
4212
4328
  const uptime = humanDuration(p.uptimeSec);
4213
4329
  const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
4214
- consola27.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
4330
+ consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
4215
4331
  }
4216
4332
  }
4217
4333
  });
@@ -4223,43 +4339,44 @@ function humanDuration(sec) {
4223
4339
  }
4224
4340
 
4225
4341
  // src/commands/nest/uninstall.ts
4226
- import { execFileSync as execFileSync11 } from "child_process";
4227
- import { existsSync as existsSync11, unlinkSync } from "fs";
4228
- import { homedir as homedir11, userInfo as userInfo3 } from "os";
4229
- import { join as join9 } from "path";
4230
- import { defineCommand as defineCommand32 } from "citty";
4231
- import consola28 from "consola";
4342
+ import { execFileSync as execFileSync10 } from "child_process";
4343
+ import { existsSync as existsSync13, unlinkSync } from "fs";
4344
+ import { homedir as homedir12, userInfo as userInfo3 } from "os";
4345
+ import { join as join11 } from "path";
4346
+ import { defineCommand as defineCommand33 } from "citty";
4347
+ import consola29 from "consola";
4232
4348
  var PLIST_LABEL2 = "ai.openape.nest";
4233
- var uninstallNestCommand = defineCommand32({
4349
+ var uninstallNestCommand = defineCommand33({
4234
4350
  meta: {
4235
4351
  name: "uninstall",
4236
4352
  description: "Stop + remove the local nest-daemon (registry + agents preserved)"
4237
4353
  },
4238
4354
  async run() {
4239
4355
  const uid = userInfo3().uid;
4240
- const path2 = join9(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4356
+ const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4241
4357
  try {
4242
- execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4243
- consola28.success("Nest daemon stopped");
4358
+ execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4359
+ consola29.success("Nest daemon stopped");
4244
4360
  } catch {
4245
- consola28.info("Nest daemon was not loaded");
4361
+ consola29.info("Nest daemon was not loaded");
4246
4362
  }
4247
- if (existsSync11(path2)) {
4363
+ if (existsSync13(path2)) {
4248
4364
  unlinkSync(path2);
4249
- consola28.success(`Removed ${path2}`);
4365
+ consola29.success(`Removed ${path2}`);
4250
4366
  }
4251
- consola28.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
4367
+ consola29.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
4252
4368
  }
4253
4369
  });
4254
4370
 
4255
4371
  // src/commands/nest/index.ts
4256
- var nestCommand = defineCommand33({
4372
+ var nestCommand = defineCommand34({
4257
4373
  meta: {
4258
4374
  name: "nest",
4259
- description: "Manage the local Nest control-plane daemon (install, authorize, status, uninstall). The Nest hosts agents on this computer \u2014 once installed + authorized, `apes agents spawn` is fast (no per-spawn DDISA approvals) and per-agent launchd plists are replaced by a single supervised process tree."
4375
+ description: "Manage the local Nest control-plane daemon (install / enroll / authorize / status / uninstall). One-time setup: `install` (launchd) \u2192 `enroll` (own DDISA identity) \u2192 `authorize` (YOLO-policy). After that, `apes agents spawn` runs without per-call DDISA approvals."
4260
4376
  },
4261
4377
  subCommands: {
4262
4378
  install: installNestCommand,
4379
+ enroll: enrollNestCommand,
4263
4380
  authorize: authorizeNestCommand,
4264
4381
  status: statusNestCommand,
4265
4382
  uninstall: uninstallNestCommand
@@ -4267,15 +4384,15 @@ var nestCommand = defineCommand33({
4267
4384
  });
4268
4385
 
4269
4386
  // src/commands/adapter/index.ts
4270
- import { defineCommand as defineCommand34 } from "citty";
4271
- import consola29 from "consola";
4272
- var adapterCommand = defineCommand34({
4387
+ import { defineCommand as defineCommand35 } from "citty";
4388
+ import consola30 from "consola";
4389
+ var adapterCommand = defineCommand35({
4273
4390
  meta: {
4274
4391
  name: "adapter",
4275
4392
  description: "Manage CLI adapters"
4276
4393
  },
4277
4394
  subCommands: {
4278
- list: defineCommand34({
4395
+ list: defineCommand35({
4279
4396
  meta: {
4280
4397
  name: "list",
4281
4398
  description: "List available adapters"
@@ -4306,7 +4423,7 @@ var adapterCommand = defineCommand34({
4306
4423
  `);
4307
4424
  return;
4308
4425
  }
4309
- consola29.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4426
+ consola30.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4310
4427
  for (const a of index2.adapters) {
4311
4428
  const installed = isInstalled(a.id, false) ? " [installed]" : "";
4312
4429
  console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
@@ -4328,7 +4445,7 @@ var adapterCommand = defineCommand34({
4328
4445
  return;
4329
4446
  }
4330
4447
  if (local.length === 0) {
4331
- consola29.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
4448
+ consola30.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
4332
4449
  return;
4333
4450
  }
4334
4451
  for (const a of local) {
@@ -4336,7 +4453,7 @@ var adapterCommand = defineCommand34({
4336
4453
  }
4337
4454
  }
4338
4455
  }),
4339
- install: defineCommand34({
4456
+ install: defineCommand35({
4340
4457
  meta: {
4341
4458
  name: "install",
4342
4459
  description: "Install an adapter from the registry"
@@ -4365,24 +4482,24 @@ var adapterCommand = defineCommand34({
4365
4482
  for (const id of ids) {
4366
4483
  const entry = findAdapter(index, id);
4367
4484
  if (!entry) {
4368
- consola29.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
4485
+ consola30.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
4369
4486
  continue;
4370
4487
  }
4371
4488
  const conflicts = findConflictingAdapters(entry.executable, id);
4372
4489
  if (conflicts.length > 0) {
4373
4490
  for (const c of conflicts) {
4374
- consola29.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
4375
- consola29.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
4491
+ consola30.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
4492
+ consola30.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
4376
4493
  }
4377
4494
  }
4378
4495
  const result = await installAdapter(entry, { local });
4379
4496
  const verb = result.updated ? "Updated" : "Installed";
4380
- consola29.success(`${verb} ${result.id} \u2192 ${result.path}`);
4381
- consola29.info(`Digest: ${result.digest}`);
4497
+ consola30.success(`${verb} ${result.id} \u2192 ${result.path}`);
4498
+ consola30.info(`Digest: ${result.digest}`);
4382
4499
  }
4383
4500
  }
4384
4501
  }),
4385
- remove: defineCommand34({
4502
+ remove: defineCommand35({
4386
4503
  meta: {
4387
4504
  name: "remove",
4388
4505
  description: "Remove an installed adapter"
@@ -4405,9 +4522,9 @@ var adapterCommand = defineCommand34({
4405
4522
  let failed = false;
4406
4523
  for (const id of ids) {
4407
4524
  if (removeAdapter(id, local)) {
4408
- consola29.success(`Removed adapter: ${id}`);
4525
+ consola30.success(`Removed adapter: ${id}`);
4409
4526
  } else {
4410
- consola29.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4527
+ consola30.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4411
4528
  failed = true;
4412
4529
  }
4413
4530
  }
@@ -4415,7 +4532,7 @@ var adapterCommand = defineCommand34({
4415
4532
  throw new CliError("Some adapters could not be removed");
4416
4533
  }
4417
4534
  }),
4418
- info: defineCommand34({
4535
+ info: defineCommand35({
4419
4536
  meta: {
4420
4537
  name: "info",
4421
4538
  description: "Show detailed adapter information"
@@ -4457,7 +4574,7 @@ var adapterCommand = defineCommand34({
4457
4574
  }
4458
4575
  }
4459
4576
  }),
4460
- search: defineCommand34({
4577
+ search: defineCommand35({
4461
4578
  meta: {
4462
4579
  name: "search",
4463
4580
  description: "Search adapters in the registry"
@@ -4489,7 +4606,7 @@ var adapterCommand = defineCommand34({
4489
4606
  return;
4490
4607
  }
4491
4608
  if (results.length === 0) {
4492
- consola29.info(`No adapters matching "${query}"`);
4609
+ consola30.info(`No adapters matching "${query}"`);
4493
4610
  return;
4494
4611
  }
4495
4612
  for (const a of results) {
@@ -4498,7 +4615,7 @@ var adapterCommand = defineCommand34({
4498
4615
  }
4499
4616
  }
4500
4617
  }),
4501
- update: defineCommand34({
4618
+ update: defineCommand35({
4502
4619
  meta: {
4503
4620
  name: "update",
4504
4621
  description: "Update installed adapters"
@@ -4524,33 +4641,33 @@ var adapterCommand = defineCommand34({
4524
4641
  const targetId = args.id ? String(args.id) : void 0;
4525
4642
  const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
4526
4643
  if (targets.length === 0) {
4527
- consola29.info("No adapters installed to update.");
4644
+ consola30.info("No adapters installed to update.");
4528
4645
  return;
4529
4646
  }
4530
4647
  for (const id of targets) {
4531
4648
  const entry = findAdapter(index, id);
4532
4649
  if (!entry) {
4533
- consola29.warn(`${id}: not found in registry, skipping`);
4650
+ consola30.warn(`${id}: not found in registry, skipping`);
4534
4651
  continue;
4535
4652
  }
4536
4653
  const localDigest = getInstalledDigest(id, false);
4537
4654
  if (localDigest === entry.digest) {
4538
- consola29.info(`${id}: already up to date`);
4655
+ consola30.info(`${id}: already up to date`);
4539
4656
  continue;
4540
4657
  }
4541
4658
  if (localDigest && !args.yes) {
4542
- consola29.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
4543
- consola29.info(` Old: ${localDigest}`);
4544
- consola29.info(` New: ${entry.digest}`);
4545
- consola29.info(" Use --yes to confirm");
4659
+ consola30.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
4660
+ consola30.info(` Old: ${localDigest}`);
4661
+ consola30.info(` New: ${entry.digest}`);
4662
+ consola30.info(" Use --yes to confirm");
4546
4663
  continue;
4547
4664
  }
4548
4665
  const result = await installAdapter(entry);
4549
- consola29.success(`Updated ${result.id} \u2192 ${result.path}`);
4666
+ consola30.success(`Updated ${result.id} \u2192 ${result.path}`);
4550
4667
  }
4551
4668
  }
4552
4669
  }),
4553
- verify: defineCommand34({
4670
+ verify: defineCommand35({
4554
4671
  meta: {
4555
4672
  name: "verify",
4556
4673
  description: "Verify installed adapter against registry digest"
@@ -4583,7 +4700,7 @@ var adapterCommand = defineCommand34({
4583
4700
  if (!localDigest)
4584
4701
  throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4585
4702
  if (localDigest === entry.digest) {
4586
- consola29.success(`${id}: digest matches registry`);
4703
+ consola30.success(`${id}: digest matches registry`);
4587
4704
  } else {
4588
4705
  console.log(` Local: ${localDigest}`);
4589
4706
  console.log(` Registry: ${entry.digest}`);
@@ -4595,11 +4712,11 @@ var adapterCommand = defineCommand34({
4595
4712
  });
4596
4713
 
4597
4714
  // src/commands/run.ts
4598
- import { execFileSync as execFileSync12 } from "child_process";
4599
- import { hostname as hostname4 } from "os";
4715
+ import { execFileSync as execFileSync11 } from "child_process";
4716
+ import { hostname as hostname5 } from "os";
4600
4717
  import { basename } from "path";
4601
- import { defineCommand as defineCommand35 } from "citty";
4602
- import consola30 from "consola";
4718
+ import { defineCommand as defineCommand36 } from "citty";
4719
+ import consola31 from "consola";
4603
4720
  function shouldWaitForGrant(args) {
4604
4721
  return args.wait === true || process.env.APE_WAIT === "1";
4605
4722
  }
@@ -4636,7 +4753,7 @@ function printPendingGrantInfo(grant, idp) {
4636
4753
  const statusCmd = `apes grants status ${grant.id}`;
4637
4754
  const executeCmd = `apes grants run ${grant.id}`;
4638
4755
  if (mode === "human") {
4639
- consola30.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
4756
+ consola31.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
4640
4757
  console.log(` Approve in browser: ${approveUrl}`);
4641
4758
  console.log(` Check status: ${statusCmd}`);
4642
4759
  console.log(` Run after approval: ${executeCmd}`);
@@ -4646,7 +4763,7 @@ function printPendingGrantInfo(grant, idp) {
4646
4763
  return;
4647
4764
  }
4648
4765
  const maxMin = getPollMaxMinutes();
4649
- consola30.success(`Grant ${grant.id} created (pending approval)`);
4766
+ consola31.success(`Grant ${grant.id} created (pending approval)`);
4650
4767
  console.log(` Approve: ${approveUrl}`);
4651
4768
  console.log(` Status: ${statusCmd} [--json]`);
4652
4769
  console.log(` Execute: ${executeCmd} --wait`);
@@ -4668,7 +4785,7 @@ function printPendingGrantInfo(grant, idp) {
4668
4785
  console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
4669
4786
  console.log(" grant be reused on subsequent invocations without re-approval.");
4670
4787
  }
4671
- var runCommand = defineCommand35({
4788
+ var runCommand = defineCommand36({
4672
4789
  meta: {
4673
4790
  name: "run",
4674
4791
  description: "Execute a grant-secured command"
@@ -4757,7 +4874,7 @@ async function runShellMode(command, args) {
4757
4874
  const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
4758
4875
  if (adapterHandled) return;
4759
4876
  const grantsUrl = await getGrantsEndpoint(idp);
4760
- const targetHost = args.host || hostname4();
4877
+ const targetHost = args.host || hostname5();
4761
4878
  try {
4762
4879
  const grants = await apiFetch(
4763
4880
  `${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
@@ -4771,7 +4888,7 @@ async function runShellMode(command, args) {
4771
4888
  }
4772
4889
  } catch {
4773
4890
  }
4774
- consola30.info(`Requesting ape-shell session grant on ${targetHost}`);
4891
+ consola31.info(`Requesting ape-shell session grant on ${targetHost}`);
4775
4892
  const grant = await apiFetch(grantsUrl, {
4776
4893
  method: "POST",
4777
4894
  body: {
@@ -4791,8 +4908,8 @@ async function runShellMode(command, args) {
4791
4908
  host: targetHost
4792
4909
  });
4793
4910
  if (shouldWaitForGrant(args)) {
4794
- consola30.info(`Grant requested: ${grant.id}`);
4795
- consola30.info("Waiting for approval...");
4911
+ consola31.info(`Grant requested: ${grant.id}`);
4912
+ consola31.info("Waiting for approval...");
4796
4913
  const maxWait = 3e5;
4797
4914
  const interval = 3e3;
4798
4915
  const start = Date.now();
@@ -4823,13 +4940,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
4823
4940
  try {
4824
4941
  resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
4825
4942
  } catch (err) {
4826
- consola30.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
4943
+ consola31.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
4827
4944
  return false;
4828
4945
  }
4829
4946
  try {
4830
4947
  const existingGrantId = await findExistingGrant(resolved, idp);
4831
4948
  if (existingGrantId) {
4832
- consola30.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
4949
+ consola31.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
4833
4950
  const token = await fetchGrantToken(idp, existingGrantId);
4834
4951
  await verifyAndExecute(token, resolved, existingGrantId);
4835
4952
  return true;
@@ -4837,7 +4954,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
4837
4954
  } catch {
4838
4955
  }
4839
4956
  const approval = args.approval ?? "once";
4840
- consola30.info(`Requesting grant for: ${resolved.detail.display}`);
4957
+ consola31.info(`Requesting grant for: ${resolved.detail.display}`);
4841
4958
  const grant = await createShapesGrant(resolved, {
4842
4959
  idp,
4843
4960
  approval,
@@ -4845,19 +4962,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
4845
4962
  });
4846
4963
  if (grant.similar_grants?.similar_grants?.length) {
4847
4964
  const n = grant.similar_grants.similar_grants.length;
4848
- consola30.info("");
4849
- consola30.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
4965
+ consola31.info("");
4966
+ consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
4850
4967
  }
4851
4968
  notifyGrantPending({
4852
4969
  grantId: grant.id,
4853
4970
  approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
4854
4971
  command: resolved.detail?.display || parsed?.raw || "unknown",
4855
4972
  audience: resolved.adapter?.cli?.audience ?? "shapes",
4856
- host: args.host || hostname4()
4973
+ host: args.host || hostname5()
4857
4974
  });
4858
4975
  if (shouldWaitForGrant(args)) {
4859
- consola30.info(`Grant requested: ${grant.id}`);
4860
- consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4976
+ consola31.info(`Grant requested: ${grant.id}`);
4977
+ consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4861
4978
  const status = await waitForGrantStatus(idp, grant.id);
4862
4979
  if (status !== "approved")
4863
4980
  throw new CliError(`Grant ${status}`);
@@ -4873,7 +4990,7 @@ function execShellCommand(command) {
4873
4990
  throw new CliError("No command to execute");
4874
4991
  try {
4875
4992
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
4876
- execFileSync12(command[0], command.slice(1), {
4993
+ execFileSync11(command[0], command.slice(1), {
4877
4994
  stdio: "inherit",
4878
4995
  env: inheritedEnv
4879
4996
  });
@@ -4931,7 +5048,7 @@ async function runAdapterMode(command, rawArgs, args) {
4931
5048
  try {
4932
5049
  const existingGrantId = await findExistingGrant(resolved, idp);
4933
5050
  if (existingGrantId) {
4934
- consola30.info(`Reusing existing grant: ${existingGrantId}`);
5051
+ consola31.info(`Reusing existing grant: ${existingGrantId}`);
4935
5052
  const token = await fetchGrantToken(idp, existingGrantId);
4936
5053
  await verifyAndExecute(token, resolved, existingGrantId);
4937
5054
  return;
@@ -4945,17 +5062,17 @@ async function runAdapterMode(command, rawArgs, args) {
4945
5062
  });
4946
5063
  if (grant.similar_grants?.similar_grants?.length) {
4947
5064
  const n = grant.similar_grants.similar_grants.length;
4948
- consola30.info("");
4949
- consola30.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
5065
+ consola31.info("");
5066
+ consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
4950
5067
  if (grant.similar_grants.widened_details?.length) {
4951
5068
  const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
4952
- consola30.info(` Broader scope: ${wider}`);
5069
+ consola31.info(` Broader scope: ${wider}`);
4953
5070
  }
4954
- consola30.info("");
5071
+ consola31.info("");
4955
5072
  }
4956
5073
  if (shouldWaitForGrant(args)) {
4957
- consola30.info(`Grant requested: ${grant.id}`);
4958
- consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5074
+ consola31.info(`Grant requested: ${grant.id}`);
5075
+ consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4959
5076
  const status = await waitForGrantStatus(idp, grant.id);
4960
5077
  if (status !== "approved")
4961
5078
  throw new Error(`Grant ${status}`);
@@ -4974,8 +5091,8 @@ async function runAudienceMode(audience, action, args) {
4974
5091
  const idp = getIdpUrl(args.idp);
4975
5092
  const grantsUrl = await getGrantsEndpoint(idp);
4976
5093
  const command = action.split(" ");
4977
- const targetHost = args.host || hostname4();
4978
- consola30.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
5094
+ const targetHost = args.host || hostname5();
5095
+ consola31.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
4979
5096
  const grant = await apiFetch(grantsUrl, {
4980
5097
  method: "POST",
4981
5098
  body: {
@@ -4992,9 +5109,9 @@ async function runAudienceMode(audience, action, args) {
4992
5109
  printPendingGrantInfo(grant, idp);
4993
5110
  throw new CliExit(getAsyncExitCode());
4994
5111
  }
4995
- consola30.success(`Grant requested: ${grant.id}`);
4996
- consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4997
- consola30.info("Waiting for approval...");
5112
+ consola31.success(`Grant requested: ${grant.id}`);
5113
+ consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5114
+ consola31.info("Waiting for approval...");
4998
5115
  const maxWait = 15 * 60 * 1e3;
4999
5116
  const interval = 3e3;
5000
5117
  const start = Date.now();
@@ -5002,7 +5119,7 @@ async function runAudienceMode(audience, action, args) {
5002
5119
  while (Date.now() - start < maxWait) {
5003
5120
  const status = await apiFetch(`${grantsUrl}/${grant.id}`);
5004
5121
  if (status.status === "approved") {
5005
- consola30.success("Grant approved!");
5122
+ consola31.success("Grant approved!");
5006
5123
  approved = true;
5007
5124
  break;
5008
5125
  }
@@ -5017,15 +5134,15 @@ async function runAudienceMode(audience, action, args) {
5017
5134
  `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.`
5018
5135
  );
5019
5136
  }
5020
- consola30.info("Fetching grant token...");
5137
+ consola31.info("Fetching grant token...");
5021
5138
  const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
5022
5139
  method: "POST"
5023
5140
  });
5024
5141
  if (audience === "escapes") {
5025
- consola30.info(`Executing: ${command.join(" ")}`);
5142
+ consola31.info(`Executing: ${command.join(" ")}`);
5026
5143
  try {
5027
5144
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5028
- execFileSync12(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
5145
+ execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
5029
5146
  stdio: "inherit",
5030
5147
  env: inheritedEnv
5031
5148
  });
@@ -5040,8 +5157,8 @@ async function runAudienceMode(audience, action, args) {
5040
5157
 
5041
5158
  // src/commands/proxy.ts
5042
5159
  import { spawn as spawn2 } from "child_process";
5043
- import { defineCommand as defineCommand36 } from "citty";
5044
- import consola31 from "consola";
5160
+ import { defineCommand as defineCommand37 } from "citty";
5161
+ import consola32 from "consola";
5045
5162
 
5046
5163
  // src/proxy/config.ts
5047
5164
  function buildDefaultProxyConfigToml(opts) {
@@ -5075,10 +5192,10 @@ note = "VPC-internal hostname suffix"
5075
5192
 
5076
5193
  // src/proxy/local-proxy.ts
5077
5194
  import { spawn } from "child_process";
5078
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "fs";
5195
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
5079
5196
  import { createRequire } from "module";
5080
5197
  import { tmpdir as tmpdir3 } from "os";
5081
- import { dirname as dirname4, join as join10, resolve as resolve4 } from "path";
5198
+ import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
5082
5199
  var require2 = createRequire(import.meta.url);
5083
5200
  function findProxyBin() {
5084
5201
  const pkgPath = require2.resolve("@openape/proxy/package.json");
@@ -5090,9 +5207,9 @@ function findProxyBin() {
5090
5207
  return resolve4(dirname4(pkgPath), binRel);
5091
5208
  }
5092
5209
  async function startEphemeralProxy(configToml) {
5093
- const tmpDir = mkdtempSync3(join10(tmpdir3(), "openape-proxy-"));
5094
- const configPath = join10(tmpDir, "config.toml");
5095
- writeFileSync7(configPath, configToml, { mode: 384 });
5210
+ const tmpDir = mkdtempSync3(join12(tmpdir3(), "openape-proxy-"));
5211
+ const configPath = join12(tmpDir, "config.toml");
5212
+ writeFileSync8(configPath, configToml, { mode: 384 });
5096
5213
  const binPath = findProxyBin();
5097
5214
  const child = spawn(process.execPath, [binPath, "-c", configPath], {
5098
5215
  stdio: ["ignore", "pipe", "pipe"],
@@ -5184,10 +5301,10 @@ function resolveProxyConfigOptions() {
5184
5301
  77
5185
5302
  );
5186
5303
  }
5187
- consola31.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
5304
+ consola32.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
5188
5305
  return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
5189
5306
  }
5190
- var proxyCommand = defineCommand36({
5307
+ var proxyCommand = defineCommand37({
5191
5308
  meta: {
5192
5309
  name: "proxy",
5193
5310
  description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
@@ -5209,12 +5326,12 @@ var proxyCommand = defineCommand36({
5209
5326
  let close = null;
5210
5327
  if (reuseUrl) {
5211
5328
  proxyUrl = reuseUrl;
5212
- consola31.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5329
+ consola32.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5213
5330
  } else {
5214
5331
  const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
5215
5332
  proxyUrl = ephemeral.url;
5216
5333
  close = ephemeral.close;
5217
- consola31.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5334
+ consola32.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5218
5335
  }
5219
5336
  const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
5220
5337
  const childEnv = {
@@ -5246,7 +5363,7 @@ var proxyCommand = defineCommand36({
5246
5363
  else resolveExit(code ?? 0);
5247
5364
  });
5248
5365
  child.once("error", (err) => {
5249
- consola31.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5366
+ consola32.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5250
5367
  resolveExit(127);
5251
5368
  });
5252
5369
  });
@@ -5260,8 +5377,8 @@ function signalNumber(signal) {
5260
5377
  }
5261
5378
 
5262
5379
  // src/commands/explain.ts
5263
- import { defineCommand as defineCommand37 } from "citty";
5264
- var explainCommand = defineCommand37({
5380
+ import { defineCommand as defineCommand38 } from "citty";
5381
+ var explainCommand = defineCommand38({
5265
5382
  meta: {
5266
5383
  name: "explain",
5267
5384
  description: "Show what permission a command would need"
@@ -5299,9 +5416,9 @@ var explainCommand = defineCommand37({
5299
5416
  });
5300
5417
 
5301
5418
  // src/commands/config/get.ts
5302
- import { defineCommand as defineCommand38 } from "citty";
5303
- import consola32 from "consola";
5304
- var configGetCommand = defineCommand38({
5419
+ import { defineCommand as defineCommand39 } from "citty";
5420
+ import consola33 from "consola";
5421
+ var configGetCommand = defineCommand39({
5305
5422
  meta: {
5306
5423
  name: "get",
5307
5424
  description: "Get a configuration value"
@@ -5321,7 +5438,7 @@ var configGetCommand = defineCommand38({
5321
5438
  if (idp)
5322
5439
  console.log(idp);
5323
5440
  else
5324
- consola32.info("No IdP configured.");
5441
+ consola33.info("No IdP configured.");
5325
5442
  break;
5326
5443
  }
5327
5444
  case "email": {
@@ -5329,7 +5446,7 @@ var configGetCommand = defineCommand38({
5329
5446
  if (auth?.email)
5330
5447
  console.log(auth.email);
5331
5448
  else
5332
- consola32.info("Not logged in.");
5449
+ consola33.info("Not logged in.");
5333
5450
  break;
5334
5451
  }
5335
5452
  default: {
@@ -5342,7 +5459,7 @@ var configGetCommand = defineCommand38({
5342
5459
  if (sectionObj && field in sectionObj) {
5343
5460
  console.log(sectionObj[field]);
5344
5461
  } else {
5345
- consola32.info(`Key "${key}" not set.`);
5462
+ consola33.info(`Key "${key}" not set.`);
5346
5463
  }
5347
5464
  } else {
5348
5465
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -5353,9 +5470,9 @@ var configGetCommand = defineCommand38({
5353
5470
  });
5354
5471
 
5355
5472
  // src/commands/config/set.ts
5356
- import { defineCommand as defineCommand39 } from "citty";
5357
- import consola33 from "consola";
5358
- var configSetCommand = defineCommand39({
5473
+ import { defineCommand as defineCommand40 } from "citty";
5474
+ import consola34 from "consola";
5475
+ var configSetCommand = defineCommand40({
5359
5476
  meta: {
5360
5477
  name: "set",
5361
5478
  description: "Set a configuration value"
@@ -5391,12 +5508,12 @@ var configSetCommand = defineCommand39({
5391
5508
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
5392
5509
  }
5393
5510
  saveConfig(config);
5394
- consola33.success(`Set ${key} = ${value}`);
5511
+ consola34.success(`Set ${key} = ${value}`);
5395
5512
  }
5396
5513
  });
5397
5514
 
5398
5515
  // src/commands/fetch/index.ts
5399
- import { defineCommand as defineCommand40 } from "citty";
5516
+ import { defineCommand as defineCommand41 } from "citty";
5400
5517
  async function doRequest(method, url, body, contentType, raw, showHeaders) {
5401
5518
  const token = getAuthToken();
5402
5519
  if (!token) {
@@ -5432,13 +5549,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
5432
5549
  throw new CliError(`HTTP ${response.status} ${response.statusText}`);
5433
5550
  }
5434
5551
  }
5435
- var fetchCommand = defineCommand40({
5552
+ var fetchCommand = defineCommand41({
5436
5553
  meta: {
5437
5554
  name: "fetch",
5438
5555
  description: "Make authenticated HTTP requests"
5439
5556
  },
5440
5557
  subCommands: {
5441
- get: defineCommand40({
5558
+ get: defineCommand41({
5442
5559
  meta: {
5443
5560
  name: "get",
5444
5561
  description: "GET request with auth token"
@@ -5464,7 +5581,7 @@ var fetchCommand = defineCommand40({
5464
5581
  await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
5465
5582
  }
5466
5583
  }),
5467
- post: defineCommand40({
5584
+ post: defineCommand41({
5468
5585
  meta: {
5469
5586
  name: "post",
5470
5587
  description: "POST request with auth token"
@@ -5503,8 +5620,8 @@ var fetchCommand = defineCommand40({
5503
5620
  });
5504
5621
 
5505
5622
  // src/commands/mcp/index.ts
5506
- import { defineCommand as defineCommand41 } from "citty";
5507
- var mcpCommand = defineCommand41({
5623
+ import { defineCommand as defineCommand42 } from "citty";
5624
+ var mcpCommand = defineCommand42({
5508
5625
  meta: {
5509
5626
  name: "mcp",
5510
5627
  description: "Start MCP server for AI agents"
@@ -5527,48 +5644,48 @@ var mcpCommand = defineCommand41({
5527
5644
  if (transport !== "stdio" && transport !== "sse") {
5528
5645
  throw new Error('Transport must be "stdio" or "sse"');
5529
5646
  }
5530
- const { startMcpServer } = await import("./server-AK2JBMJD.js");
5647
+ const { startMcpServer } = await import("./server-PHANS7PS.js");
5531
5648
  await startMcpServer(transport, port);
5532
5649
  }
5533
5650
  });
5534
5651
 
5535
5652
  // src/commands/init/index.ts
5536
- import { existsSync as existsSync12, copyFileSync, writeFileSync as writeFileSync8 } from "fs";
5653
+ import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
5537
5654
  import { randomBytes } from "crypto";
5538
- import { execFileSync as execFileSync13 } from "child_process";
5539
- import { join as join11 } from "path";
5540
- import { defineCommand as defineCommand42 } from "citty";
5541
- import consola34 from "consola";
5655
+ import { execFileSync as execFileSync12 } from "child_process";
5656
+ import { join as join13 } from "path";
5657
+ import { defineCommand as defineCommand43 } from "citty";
5658
+ import consola35 from "consola";
5542
5659
  var DEFAULT_IDP_URL = "https://id.openape.at";
5543
5660
  async function downloadTemplate(repo, targetDir) {
5544
5661
  const { downloadTemplate: gigetDownload } = await import("giget");
5545
5662
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
5546
5663
  }
5547
5664
  function installDeps(dir) {
5548
- const hasLockFile = (name) => existsSync12(join11(dir, name));
5665
+ const hasLockFile = (name) => existsSync14(join13(dir, name));
5549
5666
  if (hasLockFile("pnpm-lock.yaml")) {
5550
- execFileSync13("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
5667
+ execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
5551
5668
  } else if (hasLockFile("bun.lockb")) {
5552
- execFileSync13("bun", ["install"], { cwd: dir, stdio: "inherit" });
5669
+ execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
5553
5670
  } else {
5554
- execFileSync13("npm", ["install"], { cwd: dir, stdio: "inherit" });
5671
+ execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
5555
5672
  }
5556
5673
  }
5557
5674
  async function promptChoice(message, choices) {
5558
- const result = await consola34.prompt(message, { type: "select", options: choices });
5675
+ const result = await consola35.prompt(message, { type: "select", options: choices });
5559
5676
  if (typeof result === "symbol") {
5560
5677
  throw new CliExit(0);
5561
5678
  }
5562
5679
  return result;
5563
5680
  }
5564
5681
  async function promptText(message, defaultValue) {
5565
- const result = await consola34.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
5682
+ const result = await consola35.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
5566
5683
  if (typeof result === "symbol") {
5567
5684
  throw new CliExit(0);
5568
5685
  }
5569
5686
  return result || defaultValue || "";
5570
5687
  }
5571
- var initCommand = defineCommand42({
5688
+ var initCommand = defineCommand43({
5572
5689
  meta: {
5573
5690
  name: "init",
5574
5691
  description: "Scaffold a new OpenApe project"
@@ -5610,23 +5727,23 @@ var initCommand = defineCommand42({
5610
5727
  });
5611
5728
  async function initSP(targetDir) {
5612
5729
  const dir = targetDir || "my-app";
5613
- if (existsSync12(join11(dir, "package.json"))) {
5730
+ if (existsSync14(join13(dir, "package.json"))) {
5614
5731
  throw new CliError(`Directory "${dir}" already contains a project.`);
5615
5732
  }
5616
- consola34.start("Scaffolding SP starter...");
5733
+ consola35.start("Scaffolding SP starter...");
5617
5734
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
5618
- consola34.success("Scaffolded from openape-sp-starter");
5619
- consola34.start("Installing dependencies...");
5735
+ consola35.success("Scaffolded from openape-sp-starter");
5736
+ consola35.start("Installing dependencies...");
5620
5737
  installDeps(dir);
5621
- consola34.success("Dependencies installed");
5622
- const envExample = join11(dir, ".env.example");
5623
- const envFile = join11(dir, ".env");
5624
- if (existsSync12(envExample) && !existsSync12(envFile)) {
5738
+ consola35.success("Dependencies installed");
5739
+ const envExample = join13(dir, ".env.example");
5740
+ const envFile = join13(dir, ".env");
5741
+ if (existsSync14(envExample) && !existsSync14(envFile)) {
5625
5742
  copyFileSync(envExample, envFile);
5626
- consola34.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
5743
+ consola35.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
5627
5744
  }
5628
5745
  console.log("");
5629
- consola34.box([
5746
+ consola35.box([
5630
5747
  `cd ${dir}`,
5631
5748
  "npm run dev",
5632
5749
  "",
@@ -5635,7 +5752,7 @@ async function initSP(targetDir) {
5635
5752
  }
5636
5753
  async function initIdP(targetDir) {
5637
5754
  const dir = targetDir || "my-idp";
5638
- if (existsSync12(join11(dir, "package.json"))) {
5755
+ if (existsSync14(join13(dir, "package.json"))) {
5639
5756
  throw new CliError(`Directory "${dir}" already contains a project.`);
5640
5757
  }
5641
5758
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -5645,15 +5762,15 @@ async function initIdP(targetDir) {
5645
5762
  "s3 (S3-compatible)"
5646
5763
  ]);
5647
5764
  const adminEmail = await promptText("Admin email");
5648
- consola34.start("Scaffolding IdP starter...");
5765
+ consola35.start("Scaffolding IdP starter...");
5649
5766
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
5650
- consola34.success("Scaffolded from openape-idp-starter");
5651
- consola34.start("Installing dependencies...");
5767
+ consola35.success("Scaffolded from openape-idp-starter");
5768
+ consola35.start("Installing dependencies...");
5652
5769
  installDeps(dir);
5653
- consola34.success("Dependencies installed");
5770
+ consola35.success("Dependencies installed");
5654
5771
  const sessionSecret = randomBytes(32).toString("hex");
5655
5772
  const managementToken = randomBytes(32).toString("hex");
5656
- consola34.success("Secrets generated");
5773
+ consola35.success("Secrets generated");
5657
5774
  const isLocalhost = domain === "localhost";
5658
5775
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
5659
5776
  const envContent = [
@@ -5667,11 +5784,11 @@ async function initIdP(targetDir) {
5667
5784
  `NUXT_OPENAPE_RP_ID=${domain}`,
5668
5785
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
5669
5786
  ].join("\n");
5670
- writeFileSync8(join11(dir, ".env"), `${envContent}
5787
+ writeFileSync9(join13(dir, ".env"), `${envContent}
5671
5788
  `, { mode: 384 });
5672
- consola34.success(".env created");
5789
+ consola35.success(".env created");
5673
5790
  console.log("");
5674
- consola34.box([
5791
+ consola35.box([
5675
5792
  `cd ${dir}`,
5676
5793
  "npm run dev",
5677
5794
  "",
@@ -5688,11 +5805,11 @@ async function initIdP(targetDir) {
5688
5805
 
5689
5806
  // src/commands/enroll.ts
5690
5807
  import { Buffer as Buffer5 } from "buffer";
5691
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
5808
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
5692
5809
  import { execFile as execFile2 } from "child_process";
5693
5810
  import { sign as sign2 } from "crypto";
5694
- import { defineCommand as defineCommand43 } from "citty";
5695
- import consola35 from "consola";
5811
+ import { defineCommand as defineCommand44 } from "citty";
5812
+ import consola36 from "consola";
5696
5813
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
5697
5814
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
5698
5815
  var POLL_INTERVAL = 3e3;
@@ -5704,7 +5821,7 @@ function openBrowser2(url) {
5704
5821
  }
5705
5822
  async function pollForEnrollment(idp, agentEmail, keyPath) {
5706
5823
  const resolvedKey = resolveKeyPath(keyPath);
5707
- const keyContent = readFileSync11(resolvedKey, "utf-8");
5824
+ const keyContent = readFileSync12(resolvedKey, "utf-8");
5708
5825
  const privateKey = loadEd25519PrivateKey(keyContent);
5709
5826
  const challengeUrl = await getAgentChallengeEndpoint(idp);
5710
5827
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -5735,7 +5852,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
5735
5852
  }
5736
5853
  throw new Error("Enrollment timed out. Please check the browser and try again.");
5737
5854
  }
5738
- var enrollCommand = defineCommand43({
5855
+ var enrollCommand = defineCommand44({
5739
5856
  meta: {
5740
5857
  name: "enroll",
5741
5858
  description: "Enroll an agent with an Identity Provider"
@@ -5755,38 +5872,38 @@ var enrollCommand = defineCommand43({
5755
5872
  }
5756
5873
  },
5757
5874
  async run({ args }) {
5758
- const idp = args.idp || await consola35.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
5875
+ const idp = args.idp || await consola36.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
5759
5876
  if (typeof r === "symbol") throw new CliExit(0);
5760
5877
  return r;
5761
5878
  }) || DEFAULT_IDP_URL2;
5762
- const agentName = args.name || await consola35.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
5879
+ const agentName = args.name || await consola36.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
5763
5880
  if (typeof r === "symbol") throw new CliExit(0);
5764
5881
  return r;
5765
5882
  });
5766
5883
  if (!agentName) {
5767
5884
  throw new CliError("Agent name is required.");
5768
5885
  }
5769
- const keyPath = args.key || await consola35.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
5886
+ const keyPath = args.key || await consola36.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
5770
5887
  if (typeof r === "symbol") throw new CliExit(0);
5771
5888
  return r;
5772
5889
  }) || DEFAULT_KEY_PATH;
5773
5890
  const resolvedKey = resolveKeyPath(keyPath);
5774
5891
  let publicKey;
5775
- if (existsSync13(resolvedKey)) {
5892
+ if (existsSync15(resolvedKey)) {
5776
5893
  publicKey = readPublicKey(resolvedKey);
5777
- consola35.success(`Using existing key ${keyPath}`);
5894
+ consola36.success(`Using existing key ${keyPath}`);
5778
5895
  } else {
5779
- consola35.start(`Generating Ed25519 key pair at ${keyPath}...`);
5896
+ consola36.start(`Generating Ed25519 key pair at ${keyPath}...`);
5780
5897
  publicKey = generateAndSaveKey(keyPath);
5781
- consola35.success(`Key pair generated at ${keyPath}`);
5898
+ consola36.success(`Key pair generated at ${keyPath}`);
5782
5899
  }
5783
5900
  const encodedKey = encodeURIComponent(publicKey);
5784
5901
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
5785
- consola35.info("Opening browser for enrollment...");
5786
- consola35.info(`\u2192 ${idp}/enroll`);
5902
+ consola36.info("Opening browser for enrollment...");
5903
+ consola36.info(`\u2192 ${idp}/enroll`);
5787
5904
  openBrowser2(enrollUrl);
5788
5905
  console.log("");
5789
- const agentEmail = await consola35.prompt(
5906
+ const agentEmail = await consola36.prompt(
5790
5907
  "Agent email (shown in browser after enrollment)",
5791
5908
  { type: "text", placeholder: `agent+${agentName}@...` }
5792
5909
  ).then((r) => {
@@ -5796,7 +5913,7 @@ var enrollCommand = defineCommand43({
5796
5913
  if (!agentEmail) {
5797
5914
  throw new CliError("Agent email is required to verify enrollment.");
5798
5915
  }
5799
- consola35.start("Verifying enrollment...");
5916
+ consola36.start("Verifying enrollment...");
5800
5917
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
5801
5918
  saveAuth({
5802
5919
  idp,
@@ -5808,18 +5925,18 @@ var enrollCommand = defineCommand43({
5808
5925
  config.defaults = { ...config.defaults, idp };
5809
5926
  config.agent = { key: keyPath, email: agentEmail };
5810
5927
  saveConfig(config);
5811
- consola35.success(`Agent enrolled as ${agentEmail}`);
5812
- consola35.success("Config saved to ~/.config/apes/");
5928
+ consola36.success(`Agent enrolled as ${agentEmail}`);
5929
+ consola36.success("Config saved to ~/.config/apes/");
5813
5930
  console.log("");
5814
- consola35.info("Verify with: apes whoami");
5931
+ consola36.info("Verify with: apes whoami");
5815
5932
  }
5816
5933
  });
5817
5934
 
5818
5935
  // src/commands/register-user.ts
5819
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
5820
- import { defineCommand as defineCommand44 } from "citty";
5821
- import consola36 from "consola";
5822
- var registerUserCommand = defineCommand44({
5936
+ import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
5937
+ import { defineCommand as defineCommand45 } from "citty";
5938
+ import consola37 from "consola";
5939
+ var registerUserCommand = defineCommand45({
5823
5940
  meta: {
5824
5941
  name: "register-user",
5825
5942
  description: "Register a sub-user with SSH key"
@@ -5855,8 +5972,8 @@ var registerUserCommand = defineCommand44({
5855
5972
  throw new CliError("No IdP URL configured. Run `apes login` first.");
5856
5973
  }
5857
5974
  let publicKey = args.key;
5858
- if (existsSync14(args.key)) {
5859
- publicKey = readFileSync12(args.key, "utf-8").trim();
5975
+ if (existsSync16(args.key)) {
5976
+ publicKey = readFileSync13(args.key, "utf-8").trim();
5860
5977
  }
5861
5978
  if (!publicKey.startsWith("ssh-ed25519 ")) {
5862
5979
  throw new CliError("Public key must be in ssh-ed25519 format.");
@@ -5874,18 +5991,18 @@ var registerUserCommand = defineCommand44({
5874
5991
  ...userType ? { type: userType } : {}
5875
5992
  }
5876
5993
  });
5877
- consola36.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
5994
+ consola37.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
5878
5995
  }
5879
5996
  });
5880
5997
 
5881
5998
  // src/commands/utils/index.ts
5882
- import { defineCommand as defineCommand46 } from "citty";
5999
+ import { defineCommand as defineCommand47 } from "citty";
5883
6000
 
5884
6001
  // src/commands/utils/dig.ts
5885
- import { defineCommand as defineCommand45 } from "citty";
5886
- import consola37 from "consola";
6002
+ import { defineCommand as defineCommand46 } from "citty";
6003
+ import consola38 from "consola";
5887
6004
  import { resolveDDISA as resolveDDISA2 } from "@openape/core";
5888
- var digCommand = defineCommand45({
6005
+ var digCommand = defineCommand46({
5889
6006
  meta: {
5890
6007
  name: "dig",
5891
6008
  description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
@@ -5958,12 +6075,12 @@ var digCommand = defineCommand45({
5958
6075
  console.log(` domain: ${domain}`);
5959
6076
  console.log("");
5960
6077
  if (!result.ddisa.found) {
5961
- consola37.warn(`No DDISA record at _ddisa.${domain}`);
6078
+ consola38.warn(`No DDISA record at _ddisa.${domain}`);
5962
6079
  if (result.hint) console.log(`
5963
6080
  ${result.hint}`);
5964
6081
  throw new CliError(`No DDISA record found for ${domain}`);
5965
6082
  }
5966
- consola37.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
6083
+ consola38.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
5967
6084
  console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
5968
6085
  console.log(` IdP URL: ${result.ddisa.idp}`);
5969
6086
  if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
@@ -5973,13 +6090,13 @@ ${result.hint}`);
5973
6090
  return;
5974
6091
  }
5975
6092
  if (result.idpDiscovery.ok) {
5976
- consola37.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
6093
+ consola38.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
5977
6094
  if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
5978
6095
  if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
5979
6096
  if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
5980
6097
  if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
5981
6098
  } else {
5982
- consola37.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
6099
+ consola38.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
5983
6100
  if (result.hint) console.log(`
5984
6101
  ${result.hint}`);
5985
6102
  throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
@@ -5988,7 +6105,7 @@ ${result.hint}`);
5988
6105
  });
5989
6106
 
5990
6107
  // src/commands/utils/index.ts
5991
- var utilsCommand = defineCommand46({
6108
+ var utilsCommand = defineCommand47({
5992
6109
  meta: {
5993
6110
  name: "utils",
5994
6111
  description: "Admin/diagnostic utilities (dig, \u2026)"
@@ -5999,12 +6116,12 @@ var utilsCommand = defineCommand46({
5999
6116
  });
6000
6117
 
6001
6118
  // src/commands/sessions/index.ts
6002
- import { defineCommand as defineCommand49 } from "citty";
6119
+ import { defineCommand as defineCommand50 } from "citty";
6003
6120
 
6004
6121
  // src/commands/sessions/list.ts
6005
- import { defineCommand as defineCommand47 } from "citty";
6006
- import consola38 from "consola";
6007
- var sessionsListCommand = defineCommand47({
6122
+ import { defineCommand as defineCommand48 } from "citty";
6123
+ import consola39 from "consola";
6124
+ var sessionsListCommand = defineCommand48({
6008
6125
  meta: {
6009
6126
  name: "list",
6010
6127
  description: "List your active refresh-token families (one per logged-in device)."
@@ -6022,7 +6139,7 @@ var sessionsListCommand = defineCommand47({
6022
6139
  return;
6023
6140
  }
6024
6141
  if (result.data.length === 0) {
6025
- consola38.info("No active sessions.");
6142
+ consola39.info("No active sessions.");
6026
6143
  return;
6027
6144
  }
6028
6145
  for (const f of result.data) {
@@ -6034,9 +6151,9 @@ var sessionsListCommand = defineCommand47({
6034
6151
  });
6035
6152
 
6036
6153
  // src/commands/sessions/remove.ts
6037
- import { defineCommand as defineCommand48 } from "citty";
6038
- import consola39 from "consola";
6039
- var sessionsRemoveCommand = defineCommand48({
6154
+ import { defineCommand as defineCommand49 } from "citty";
6155
+ import consola40 from "consola";
6156
+ var sessionsRemoveCommand = defineCommand49({
6040
6157
  meta: {
6041
6158
  name: "remove",
6042
6159
  description: "Revoke one of your active refresh-token families by id."
@@ -6052,12 +6169,12 @@ var sessionsRemoveCommand = defineCommand48({
6052
6169
  const id = String(args.familyId).trim();
6053
6170
  if (!id) throw new CliError("familyId required");
6054
6171
  await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
6055
- consola39.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
6172
+ consola40.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
6056
6173
  }
6057
6174
  });
6058
6175
 
6059
6176
  // src/commands/sessions/index.ts
6060
- var sessionsCommand = defineCommand49({
6177
+ var sessionsCommand = defineCommand50({
6061
6178
  meta: {
6062
6179
  name: "sessions",
6063
6180
  description: "Manage your active refresh-token sessions across devices"
@@ -6069,10 +6186,10 @@ var sessionsCommand = defineCommand49({
6069
6186
  });
6070
6187
 
6071
6188
  // src/commands/dns-check.ts
6072
- import { defineCommand as defineCommand50 } from "citty";
6073
- import consola40 from "consola";
6189
+ import { defineCommand as defineCommand51 } from "citty";
6190
+ import consola41 from "consola";
6074
6191
  import { resolveDDISA as resolveDDISA3 } from "@openape/core";
6075
- var dnsCheckCommand = defineCommand50({
6192
+ var dnsCheckCommand = defineCommand51({
6076
6193
  meta: {
6077
6194
  name: "dns-check",
6078
6195
  description: "Validate DDISA DNS TXT records for a domain"
@@ -6086,7 +6203,7 @@ var dnsCheckCommand = defineCommand50({
6086
6203
  },
6087
6204
  async run({ args }) {
6088
6205
  const domain = args.domain;
6089
- consola40.start(`Checking _ddisa.${domain}...`);
6206
+ consola41.start(`Checking _ddisa.${domain}...`);
6090
6207
  try {
6091
6208
  const result = await resolveDDISA3(domain);
6092
6209
  if (!result) {
@@ -6095,7 +6212,7 @@ var dnsCheckCommand = defineCommand50({
6095
6212
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
6096
6213
  throw new CliError(`No DDISA record found for ${domain}`);
6097
6214
  }
6098
- consola40.success(`_ddisa.${domain} \u2192 ${result.idp}`);
6215
+ consola41.success(`_ddisa.${domain} \u2192 ${result.idp}`);
6099
6216
  console.log("");
6100
6217
  console.log(` Version: ${result.version || "ddisa1"}`);
6101
6218
  console.log(` IdP URL: ${result.idp}`);
@@ -6104,14 +6221,14 @@ var dnsCheckCommand = defineCommand50({
6104
6221
  if (result.priority !== void 0)
6105
6222
  console.log(` Priority: ${result.priority}`);
6106
6223
  console.log("");
6107
- consola40.start(`Verifying IdP at ${result.idp}...`);
6224
+ consola41.start(`Verifying IdP at ${result.idp}...`);
6108
6225
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
6109
6226
  if (!discoResp.ok) {
6110
- consola40.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
6227
+ consola41.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
6111
6228
  return;
6112
6229
  }
6113
6230
  const disco = await discoResp.json();
6114
- consola40.success(`IdP is reachable`);
6231
+ consola41.success(`IdP is reachable`);
6115
6232
  console.log(` Issuer: ${disco.issuer}`);
6116
6233
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
6117
6234
  if (disco.ddisa_auth_methods_supported) {
@@ -6129,7 +6246,7 @@ var dnsCheckCommand = defineCommand50({
6129
6246
  // src/commands/health.ts
6130
6247
  import { exec } from "child_process";
6131
6248
  import { promisify } from "util";
6132
- import { defineCommand as defineCommand51 } from "citty";
6249
+ import { defineCommand as defineCommand52 } from "citty";
6133
6250
  var execAsync = promisify(exec);
6134
6251
  async function resolveApeShellPath() {
6135
6252
  try {
@@ -6165,7 +6282,7 @@ async function bestEffortGrantCount(idp) {
6165
6282
  }
6166
6283
  }
6167
6284
  async function runHealth(args) {
6168
- const version = true ? "1.5.0" : "0.0.0";
6285
+ const version = true ? "1.6.0" : "0.0.0";
6169
6286
  const auth = loadAuth();
6170
6287
  if (!auth) {
6171
6288
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -6228,7 +6345,7 @@ async function runHealth(args) {
6228
6345
  throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
6229
6346
  }
6230
6347
  }
6231
- var healthCommand = defineCommand51({
6348
+ var healthCommand = defineCommand52({
6232
6349
  meta: {
6233
6350
  name: "health",
6234
6351
  description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
@@ -6246,8 +6363,8 @@ var healthCommand = defineCommand51({
6246
6363
  });
6247
6364
 
6248
6365
  // src/commands/workflows.ts
6249
- import { defineCommand as defineCommand52 } from "citty";
6250
- import consola41 from "consola";
6366
+ import { defineCommand as defineCommand53 } from "citty";
6367
+ import consola42 from "consola";
6251
6368
 
6252
6369
  // src/guides/index.ts
6253
6370
  var guides = [
@@ -6297,7 +6414,7 @@ var guides = [
6297
6414
  ];
6298
6415
 
6299
6416
  // src/commands/workflows.ts
6300
- var workflowsCommand = defineCommand52({
6417
+ var workflowsCommand = defineCommand53({
6301
6418
  meta: {
6302
6419
  name: "workflows",
6303
6420
  description: "Discover workflow guides"
@@ -6318,7 +6435,7 @@ var workflowsCommand = defineCommand52({
6318
6435
  if (args.id) {
6319
6436
  const guide = guides.find((g) => g.id === String(args.id));
6320
6437
  if (!guide) {
6321
- consola41.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
6438
+ consola42.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
6322
6439
  throw new CliError(`Guide not found: ${args.id}`);
6323
6440
  }
6324
6441
  if (args.json) {
@@ -6358,26 +6475,26 @@ var workflowsCommand = defineCommand52({
6358
6475
  });
6359
6476
 
6360
6477
  // src/version-check.ts
6361
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
6362
- import { homedir as homedir12 } from "os";
6363
- import { join as join12 } from "path";
6364
- import consola42 from "consola";
6478
+ import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
6479
+ import { homedir as homedir13 } from "os";
6480
+ import { join as join14 } from "path";
6481
+ import consola43 from "consola";
6365
6482
  var PACKAGE_NAME = "@openape/apes";
6366
6483
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
6367
- var CACHE_FILE = join12(homedir12(), ".config", "apes", ".version-check.json");
6484
+ var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
6368
6485
  function readCache() {
6369
- if (!existsSync15(CACHE_FILE)) return null;
6486
+ if (!existsSync17(CACHE_FILE)) return null;
6370
6487
  try {
6371
- return JSON.parse(readFileSync13(CACHE_FILE, "utf-8"));
6488
+ return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
6372
6489
  } catch {
6373
6490
  return null;
6374
6491
  }
6375
6492
  }
6376
6493
  function writeCache(entry) {
6377
6494
  try {
6378
- const dir = join12(homedir12(), ".config", "apes");
6379
- if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
6380
- writeFileSync9(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
6495
+ const dir = join14(homedir13(), ".config", "apes");
6496
+ if (!existsSync17(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
6497
+ writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
6381
6498
  } catch {
6382
6499
  }
6383
6500
  }
@@ -6406,7 +6523,7 @@ async function fetchLatestVersion() {
6406
6523
  }
6407
6524
  function warnIfBehind(currentVersion, latest) {
6408
6525
  if (compareSemver(currentVersion, latest) < 0) {
6409
- consola42.warn(
6526
+ consola43.warn(
6410
6527
  `apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
6411
6528
  );
6412
6529
  }
@@ -6438,10 +6555,10 @@ if (shellRewrite) {
6438
6555
  if (shellRewrite.action === "rewrite") {
6439
6556
  process.argv = shellRewrite.argv;
6440
6557
  } else if (shellRewrite.action === "version") {
6441
- console.log(`ape-shell ${"1.5.0"} (OpenApe DDISA shell wrapper)`);
6558
+ console.log(`ape-shell ${"1.6.0"} (OpenApe DDISA shell wrapper)`);
6442
6559
  process.exit(0);
6443
6560
  } else if (shellRewrite.action === "help") {
6444
- console.log(`ape-shell ${"1.5.0"} \u2014 OpenApe DDISA shell wrapper`);
6561
+ console.log(`ape-shell ${"1.6.0"} \u2014 OpenApe DDISA shell wrapper`);
6445
6562
  console.log("");
6446
6563
  console.log("Usage:");
6447
6564
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -6465,7 +6582,7 @@ if (shellRewrite) {
6465
6582
  }
6466
6583
  }
6467
6584
  var debug = process.argv.includes("--debug");
6468
- var grantsCommand = defineCommand53({
6585
+ var grantsCommand = defineCommand54({
6469
6586
  meta: {
6470
6587
  name: "grants",
6471
6588
  description: "Grant management"
@@ -6486,7 +6603,7 @@ var grantsCommand = defineCommand53({
6486
6603
  "delegation-revoke": delegationRevokeCommand
6487
6604
  }
6488
6605
  });
6489
- var configCommand = defineCommand53({
6606
+ var configCommand = defineCommand54({
6490
6607
  meta: {
6491
6608
  name: "config",
6492
6609
  description: "Configuration management"
@@ -6496,10 +6613,10 @@ var configCommand = defineCommand53({
6496
6613
  set: configSetCommand
6497
6614
  }
6498
6615
  });
6499
- var main = defineCommand53({
6616
+ var main = defineCommand54({
6500
6617
  meta: {
6501
6618
  name: "apes",
6502
- version: "1.5.0",
6619
+ version: "1.6.0",
6503
6620
  description: "Unified CLI for OpenApe"
6504
6621
  },
6505
6622
  subCommands: {
@@ -6555,20 +6672,20 @@ async function maybeRefreshAuth() {
6555
6672
  }
6556
6673
  }
6557
6674
  await maybeRefreshAuth();
6558
- await maybeWarnStaleVersion("1.5.0").catch(() => {
6675
+ await maybeWarnStaleVersion("1.6.0").catch(() => {
6559
6676
  });
6560
6677
  runMain(main).catch((err) => {
6561
6678
  if (err instanceof CliExit) {
6562
6679
  process.exit(err.exitCode);
6563
6680
  }
6564
6681
  if (err instanceof CliError) {
6565
- consola43.error(err.message);
6682
+ consola44.error(err.message);
6566
6683
  process.exit(err.exitCode);
6567
6684
  }
6568
6685
  if (debug) {
6569
- consola43.error(err);
6686
+ consola44.error(err);
6570
6687
  } else {
6571
- consola43.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
6688
+ consola44.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
6572
6689
  }
6573
6690
  process.exit(1);
6574
6691
  });