@openape/apes 1.5.0 → 1.6.1

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,193 @@ 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
+ // Outer spawn-grant — what the nest's HTTP handler invokes.
4013
+ "apes agents spawn *",
4014
+ "apes agents destroy *",
4015
+ "apes agents sync",
4016
+ // Inner setup.sh-grant — `apes agents spawn` itself shells out to
4017
+ // `apes run --as root --wait -- bash <tempdir>/setup.sh` to do the
4018
+ // dscl/launchctl/heredoc-write work. Path looks like
4019
+ // `bash /var/folders/.../apes-spawn-<name>-XXXX/setup.sh`. The narrow
4020
+ // glob below limits the auto-approval to that exact lifecycle path
4021
+ // — `bash *` would be unsafe.
4022
+ "bash *apes-spawn-*setup.sh",
4023
+ // Bridge invocation the nest's process supervisor uses (Stage 1
4024
+ // supervisor work). Intentionally precise — not a generic
4025
+ // `apes run --as *` wildcard — so a compromised nest can't pivot
4026
+ // to running arbitrary commands as arbitrary users.
4027
+ "apes run --as * -- openape-chat-bridge"
4028
+ ];
4029
+ var authorizeNestCommand = defineCommand30({
4030
+ meta: {
4031
+ name: "authorize",
4032
+ description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts"
4033
+ },
4034
+ args: {
4035
+ "allow": {
4036
+ type: "string",
4037
+ description: "Override allow_patterns (comma-separated globs). Default: nest-managed agent lifecycle."
4038
+ },
4039
+ "mode": {
4040
+ type: "string",
4041
+ description: "Policy mode (allow-list | deny-list). Default: allow-list \u2014 auto-approve only matched patterns."
4042
+ },
4043
+ "expires-in": {
4044
+ type: "string",
4045
+ description: "Optional duration like 30d, 6h. Omit for no expiry."
4046
+ }
4047
+ },
4048
+ async run({ args }) {
4049
+ const ownerAuth = loadAuth();
4050
+ if (!ownerAuth?.email || !ownerAuth.access_token) {
4051
+ throw new CliError("Run `apes login <email>` first \u2014 only the nest's owner can set its YOLO-policy.");
4052
+ }
4053
+ const nestAuthPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
4054
+ if (!existsSync11(nestAuthPath)) {
4055
+ throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
4056
+ }
4057
+ const nestAuth = JSON.parse(readFileSync10(nestAuthPath, "utf8"));
4058
+ if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
4059
+ const idp = getIdpUrl();
4060
+ if (!idp) throw new CliError("No IdP configured.");
4061
+ const allowPatterns = typeof args.allow === "string" && args.allow ? args.allow.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_ALLOW_PATTERNS;
4062
+ const mode = args.mode ?? "allow-list";
4063
+ const expiresAt = parseExpiresIn(args["expires-in"]);
4064
+ consola26.info(`Setting YOLO-policy on ${nestAuth.email}`);
4065
+ consola26.info(` mode: ${mode}`);
4066
+ consola26.info(` allow_patterns:`);
4067
+ for (const p of allowPatterns) consola26.info(` - ${p}`);
4068
+ if (expiresAt) consola26.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
4069
+ const url = `${idp}/api/users/${encodeURIComponent(nestAuth.email)}/yolo-policy`;
4070
+ const res = await fetch(url, {
4071
+ method: "PUT",
4072
+ headers: {
4073
+ "Authorization": `Bearer ${ownerAuth.access_token}`,
4074
+ "Content-Type": "application/json"
4075
+ },
4076
+ body: JSON.stringify({
4077
+ mode,
4078
+ allowPatterns,
4079
+ denyPatterns: [],
4080
+ expiresAt: expiresAt ?? null
4081
+ })
4082
+ });
4083
+ if (!res.ok) {
4084
+ const text = await res.text().catch(() => "");
4085
+ throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
3975
4086
  }
4087
+ consola26.success("YOLO-policy applied. Nest-driven agent lifecycle is now zero-prompt.");
4088
+ consola26.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
3976
4089
  }
3977
4090
  });
4091
+ function parseExpiresIn(s) {
4092
+ if (!s) return null;
4093
+ const m = s.match(/^(\d+)([hdw])$/);
4094
+ if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
4095
+ const n = Number(m[1]);
4096
+ const unit = m[2];
4097
+ const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
4098
+ return Math.floor(Date.now() / 1e3) + n * seconds;
4099
+ }
3978
4100
 
3979
4101
  // 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";
4102
+ import { execFileSync as execFileSync9 } from "child_process";
4103
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
4104
+ import { homedir as homedir11, userInfo as userInfo2 } from "os";
4105
+ import { dirname as dirname3, join as join10 } from "path";
4106
+ import { defineCommand as defineCommand31 } from "citty";
4107
+ import consola27 from "consola";
3986
4108
 
3987
4109
  // src/commands/nest/apes-agents-adapter.ts
3988
4110
  var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
@@ -4049,13 +4171,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
4049
4171
  // src/commands/nest/install.ts
4050
4172
  var PLIST_LABEL = "ai.openape.nest";
4051
4173
  function plistPath() {
4052
- return join8(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4174
+ return join10(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4053
4175
  }
4054
4176
  function escape2(s) {
4055
4177
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4056
4178
  }
4057
4179
  function buildPlist(args) {
4058
- const logsDir = join8(args.homeDir, "Library", "Logs");
4180
+ const logsDir = join10(args.userHome, "Library", "Logs");
4059
4181
  return `<?xml version="1.0" encoding="UTF-8"?>
4060
4182
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4061
4183
  <plist version="1.0">
@@ -4067,7 +4189,7 @@ function buildPlist(args) {
4067
4189
  <string>${escape2(args.nestBin)}</string>
4068
4190
  </array>
4069
4191
  <key>WorkingDirectory</key>
4070
- <string>${escape2(args.homeDir)}</string>
4192
+ <string>${escape2(args.nestHome)}</string>
4071
4193
  <key>RunAtLoad</key>
4072
4194
  <true/>
4073
4195
  <key>KeepAlive</key>
@@ -4076,8 +4198,8 @@ function buildPlist(args) {
4076
4198
  <integer>10</integer>
4077
4199
  <key>EnvironmentVariables</key>
4078
4200
  <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>
4201
+ <key>HOME</key><string>${escape2(args.nestHome)}</string>
4202
+ <key>PATH</key><string>${escape2(args.userHome)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
4081
4203
  <key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
4082
4204
  <key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
4083
4205
  </dict>
@@ -4090,31 +4212,31 @@ function buildPlist(args) {
4090
4212
  `;
4091
4213
  }
4092
4214
  function installAdapter2() {
4093
- const target = join8(homedir10(), ".openape", "shapes", "adapters", "apes-agents.toml");
4094
- mkdirSync4(dirname3(target), { recursive: true });
4215
+ const target = join10(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
4216
+ mkdirSync5(dirname3(target), { recursive: true });
4095
4217
  let existing = "";
4096
4218
  try {
4097
- existing = readFileSync10(target, "utf8");
4219
+ existing = readFileSync11(target, "utf8");
4098
4220
  } catch {
4099
4221
  }
4100
4222
  if (existing === APES_AGENTS_ADAPTER_TOML) return false;
4101
- writeFileSync6(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4102
- consola26.success(`Wrote shapes adapter ${target}`);
4223
+ writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4224
+ consola27.success(`Wrote shapes adapter ${target}`);
4103
4225
  return true;
4104
4226
  }
4105
4227
  function findBinary(name) {
4106
4228
  for (const dir of [
4107
- join8(homedir10(), ".bun", "bin"),
4229
+ join10(homedir11(), ".bun", "bin"),
4108
4230
  "/opt/homebrew/bin",
4109
4231
  "/usr/local/bin",
4110
4232
  "/usr/bin"
4111
4233
  ]) {
4112
- const p = join8(dir, name);
4113
- if (existsSync10(p)) return p;
4234
+ const p = join10(dir, name);
4235
+ if (existsSync12(p)) return p;
4114
4236
  }
4115
4237
  throw new Error(`could not locate ${name} on PATH; install it first`);
4116
4238
  }
4117
- var installNestCommand = defineCommand30({
4239
+ var installNestCommand = defineCommand31({
4118
4240
  meta: {
4119
4241
  name: "install",
4120
4242
  description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
@@ -4126,54 +4248,55 @@ var installNestCommand = defineCommand30({
4126
4248
  }
4127
4249
  },
4128
4250
  async run({ args }) {
4129
- const homeDir = homedir10();
4251
+ const homeDir = homedir11();
4130
4252
  const port = Number(args.port ?? 9091);
4131
4253
  if (!Number.isInteger(port) || port < 1024 || port > 65535) {
4132
4254
  throw new Error(`invalid port ${port}`);
4133
4255
  }
4134
4256
  const nestBin = findBinary("openape-nest");
4135
4257
  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}`);
4258
+ consola27.info(`Installing nest at ${plistPath()}`);
4259
+ consola27.info(` nest binary: ${nestBin}`);
4260
+ consola27.info(` apes binary: ${apesBin}`);
4261
+ consola27.info(` HTTP port: ${port}`);
4140
4262
  installAdapter2();
4141
- mkdirSync4(join8(homeDir, "Library", "LaunchAgents"), { recursive: true });
4142
- const desired = buildPlist({ nestBin, apesBin, homeDir, port });
4263
+ mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
4264
+ mkdirSync5(NEST_DATA_DIR, { recursive: true });
4265
+ const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
4143
4266
  let existing = "";
4144
4267
  try {
4145
- existing = readFileSync10(plistPath(), "utf8");
4268
+ existing = readFileSync11(plistPath(), "utf8");
4146
4269
  } catch {
4147
4270
  }
4148
4271
  if (existing !== desired) {
4149
- writeFileSync6(plistPath(), desired, { mode: 420 });
4150
- consola26.success("Wrote launchd plist");
4272
+ writeFileSync7(plistPath(), desired, { mode: 420 });
4273
+ consola27.success("Wrote launchd plist");
4151
4274
  } else {
4152
- consola26.info("plist already up to date");
4275
+ consola27.info("plist already up to date");
4153
4276
  }
4154
4277
  const uid = userInfo2().uid;
4155
4278
  try {
4156
- execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4279
+ execFileSync9("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
4157
4280
  } catch {
4158
4281
  }
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.");
4282
+ execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4283
+ consola27.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4284
+ consola27.info("");
4285
+ consola27.info("Next steps for zero-prompt spawn \u2014 both one-time:");
4286
+ consola27.info("");
4287
+ consola27.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
4288
+ consola27.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
4289
+ consola27.info("");
4290
+ consola27.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
4168
4291
  }
4169
4292
  });
4170
4293
 
4171
4294
  // src/commands/nest/status.ts
4172
4295
  import process2 from "process";
4173
- import { defineCommand as defineCommand31 } from "citty";
4174
- import consola27 from "consola";
4296
+ import { defineCommand as defineCommand32 } from "citty";
4297
+ import consola28 from "consola";
4175
4298
  var DEFAULT_PORT = 9091;
4176
- var statusNestCommand = defineCommand31({
4299
+ var statusNestCommand = defineCommand32({
4177
4300
  meta: {
4178
4301
  name: "status",
4179
4302
  description: "Print state of the local nest-daemon (agents registered, processes supervised)"
@@ -4193,8 +4316,8 @@ var statusNestCommand = defineCommand31({
4193
4316
  } catch (err) {
4194
4317
  const msg = err instanceof Error ? err.message : String(err);
4195
4318
  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");
4319
+ consola28.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
4320
+ consola28.info(" Run: apes nest install");
4198
4321
  process2.exit(2);
4199
4322
  }
4200
4323
  throw err;
@@ -4203,15 +4326,15 @@ var statusNestCommand = defineCommand31({
4203
4326
  console.log(JSON.stringify(status, null, 2));
4204
4327
  return;
4205
4328
  }
4206
- consola27.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
4329
+ consola28.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
4207
4330
  if (status.processes.length === 0) {
4208
- consola27.info(" (no processes running)");
4331
+ consola28.info(" (no processes running)");
4209
4332
  return;
4210
4333
  }
4211
4334
  for (const p of status.processes) {
4212
4335
  const uptime = humanDuration(p.uptimeSec);
4213
4336
  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}`);
4337
+ consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
4215
4338
  }
4216
4339
  }
4217
4340
  });
@@ -4223,43 +4346,44 @@ function humanDuration(sec) {
4223
4346
  }
4224
4347
 
4225
4348
  // 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";
4349
+ import { execFileSync as execFileSync10 } from "child_process";
4350
+ import { existsSync as existsSync13, unlinkSync } from "fs";
4351
+ import { homedir as homedir12, userInfo as userInfo3 } from "os";
4352
+ import { join as join11 } from "path";
4353
+ import { defineCommand as defineCommand33 } from "citty";
4354
+ import consola29 from "consola";
4232
4355
  var PLIST_LABEL2 = "ai.openape.nest";
4233
- var uninstallNestCommand = defineCommand32({
4356
+ var uninstallNestCommand = defineCommand33({
4234
4357
  meta: {
4235
4358
  name: "uninstall",
4236
4359
  description: "Stop + remove the local nest-daemon (registry + agents preserved)"
4237
4360
  },
4238
4361
  async run() {
4239
4362
  const uid = userInfo3().uid;
4240
- const path2 = join9(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4363
+ const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4241
4364
  try {
4242
- execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4243
- consola28.success("Nest daemon stopped");
4365
+ execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4366
+ consola29.success("Nest daemon stopped");
4244
4367
  } catch {
4245
- consola28.info("Nest daemon was not loaded");
4368
+ consola29.info("Nest daemon was not loaded");
4246
4369
  }
4247
- if (existsSync11(path2)) {
4370
+ if (existsSync13(path2)) {
4248
4371
  unlinkSync(path2);
4249
- consola28.success(`Removed ${path2}`);
4372
+ consola29.success(`Removed ${path2}`);
4250
4373
  }
4251
- consola28.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
4374
+ consola29.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
4252
4375
  }
4253
4376
  });
4254
4377
 
4255
4378
  // src/commands/nest/index.ts
4256
- var nestCommand = defineCommand33({
4379
+ var nestCommand = defineCommand34({
4257
4380
  meta: {
4258
4381
  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."
4382
+ 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
4383
  },
4261
4384
  subCommands: {
4262
4385
  install: installNestCommand,
4386
+ enroll: enrollNestCommand,
4263
4387
  authorize: authorizeNestCommand,
4264
4388
  status: statusNestCommand,
4265
4389
  uninstall: uninstallNestCommand
@@ -4267,15 +4391,15 @@ var nestCommand = defineCommand33({
4267
4391
  });
4268
4392
 
4269
4393
  // src/commands/adapter/index.ts
4270
- import { defineCommand as defineCommand34 } from "citty";
4271
- import consola29 from "consola";
4272
- var adapterCommand = defineCommand34({
4394
+ import { defineCommand as defineCommand35 } from "citty";
4395
+ import consola30 from "consola";
4396
+ var adapterCommand = defineCommand35({
4273
4397
  meta: {
4274
4398
  name: "adapter",
4275
4399
  description: "Manage CLI adapters"
4276
4400
  },
4277
4401
  subCommands: {
4278
- list: defineCommand34({
4402
+ list: defineCommand35({
4279
4403
  meta: {
4280
4404
  name: "list",
4281
4405
  description: "List available adapters"
@@ -4306,7 +4430,7 @@ var adapterCommand = defineCommand34({
4306
4430
  `);
4307
4431
  return;
4308
4432
  }
4309
- consola29.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4433
+ consola30.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4310
4434
  for (const a of index2.adapters) {
4311
4435
  const installed = isInstalled(a.id, false) ? " [installed]" : "";
4312
4436
  console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
@@ -4328,7 +4452,7 @@ var adapterCommand = defineCommand34({
4328
4452
  return;
4329
4453
  }
4330
4454
  if (local.length === 0) {
4331
- consola29.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
4455
+ consola30.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
4332
4456
  return;
4333
4457
  }
4334
4458
  for (const a of local) {
@@ -4336,7 +4460,7 @@ var adapterCommand = defineCommand34({
4336
4460
  }
4337
4461
  }
4338
4462
  }),
4339
- install: defineCommand34({
4463
+ install: defineCommand35({
4340
4464
  meta: {
4341
4465
  name: "install",
4342
4466
  description: "Install an adapter from the registry"
@@ -4365,24 +4489,24 @@ var adapterCommand = defineCommand34({
4365
4489
  for (const id of ids) {
4366
4490
  const entry = findAdapter(index, id);
4367
4491
  if (!entry) {
4368
- consola29.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
4492
+ consola30.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
4369
4493
  continue;
4370
4494
  }
4371
4495
  const conflicts = findConflictingAdapters(entry.executable, id);
4372
4496
  if (conflicts.length > 0) {
4373
4497
  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}`);
4498
+ consola30.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
4499
+ consola30.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
4376
4500
  }
4377
4501
  }
4378
4502
  const result = await installAdapter(entry, { local });
4379
4503
  const verb = result.updated ? "Updated" : "Installed";
4380
- consola29.success(`${verb} ${result.id} \u2192 ${result.path}`);
4381
- consola29.info(`Digest: ${result.digest}`);
4504
+ consola30.success(`${verb} ${result.id} \u2192 ${result.path}`);
4505
+ consola30.info(`Digest: ${result.digest}`);
4382
4506
  }
4383
4507
  }
4384
4508
  }),
4385
- remove: defineCommand34({
4509
+ remove: defineCommand35({
4386
4510
  meta: {
4387
4511
  name: "remove",
4388
4512
  description: "Remove an installed adapter"
@@ -4405,9 +4529,9 @@ var adapterCommand = defineCommand34({
4405
4529
  let failed = false;
4406
4530
  for (const id of ids) {
4407
4531
  if (removeAdapter(id, local)) {
4408
- consola29.success(`Removed adapter: ${id}`);
4532
+ consola30.success(`Removed adapter: ${id}`);
4409
4533
  } else {
4410
- consola29.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4534
+ consola30.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4411
4535
  failed = true;
4412
4536
  }
4413
4537
  }
@@ -4415,7 +4539,7 @@ var adapterCommand = defineCommand34({
4415
4539
  throw new CliError("Some adapters could not be removed");
4416
4540
  }
4417
4541
  }),
4418
- info: defineCommand34({
4542
+ info: defineCommand35({
4419
4543
  meta: {
4420
4544
  name: "info",
4421
4545
  description: "Show detailed adapter information"
@@ -4457,7 +4581,7 @@ var adapterCommand = defineCommand34({
4457
4581
  }
4458
4582
  }
4459
4583
  }),
4460
- search: defineCommand34({
4584
+ search: defineCommand35({
4461
4585
  meta: {
4462
4586
  name: "search",
4463
4587
  description: "Search adapters in the registry"
@@ -4489,7 +4613,7 @@ var adapterCommand = defineCommand34({
4489
4613
  return;
4490
4614
  }
4491
4615
  if (results.length === 0) {
4492
- consola29.info(`No adapters matching "${query}"`);
4616
+ consola30.info(`No adapters matching "${query}"`);
4493
4617
  return;
4494
4618
  }
4495
4619
  for (const a of results) {
@@ -4498,7 +4622,7 @@ var adapterCommand = defineCommand34({
4498
4622
  }
4499
4623
  }
4500
4624
  }),
4501
- update: defineCommand34({
4625
+ update: defineCommand35({
4502
4626
  meta: {
4503
4627
  name: "update",
4504
4628
  description: "Update installed adapters"
@@ -4524,33 +4648,33 @@ var adapterCommand = defineCommand34({
4524
4648
  const targetId = args.id ? String(args.id) : void 0;
4525
4649
  const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
4526
4650
  if (targets.length === 0) {
4527
- consola29.info("No adapters installed to update.");
4651
+ consola30.info("No adapters installed to update.");
4528
4652
  return;
4529
4653
  }
4530
4654
  for (const id of targets) {
4531
4655
  const entry = findAdapter(index, id);
4532
4656
  if (!entry) {
4533
- consola29.warn(`${id}: not found in registry, skipping`);
4657
+ consola30.warn(`${id}: not found in registry, skipping`);
4534
4658
  continue;
4535
4659
  }
4536
4660
  const localDigest = getInstalledDigest(id, false);
4537
4661
  if (localDigest === entry.digest) {
4538
- consola29.info(`${id}: already up to date`);
4662
+ consola30.info(`${id}: already up to date`);
4539
4663
  continue;
4540
4664
  }
4541
4665
  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");
4666
+ consola30.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
4667
+ consola30.info(` Old: ${localDigest}`);
4668
+ consola30.info(` New: ${entry.digest}`);
4669
+ consola30.info(" Use --yes to confirm");
4546
4670
  continue;
4547
4671
  }
4548
4672
  const result = await installAdapter(entry);
4549
- consola29.success(`Updated ${result.id} \u2192 ${result.path}`);
4673
+ consola30.success(`Updated ${result.id} \u2192 ${result.path}`);
4550
4674
  }
4551
4675
  }
4552
4676
  }),
4553
- verify: defineCommand34({
4677
+ verify: defineCommand35({
4554
4678
  meta: {
4555
4679
  name: "verify",
4556
4680
  description: "Verify installed adapter against registry digest"
@@ -4583,7 +4707,7 @@ var adapterCommand = defineCommand34({
4583
4707
  if (!localDigest)
4584
4708
  throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4585
4709
  if (localDigest === entry.digest) {
4586
- consola29.success(`${id}: digest matches registry`);
4710
+ consola30.success(`${id}: digest matches registry`);
4587
4711
  } else {
4588
4712
  console.log(` Local: ${localDigest}`);
4589
4713
  console.log(` Registry: ${entry.digest}`);
@@ -4595,11 +4719,11 @@ var adapterCommand = defineCommand34({
4595
4719
  });
4596
4720
 
4597
4721
  // src/commands/run.ts
4598
- import { execFileSync as execFileSync12 } from "child_process";
4599
- import { hostname as hostname4 } from "os";
4722
+ import { execFileSync as execFileSync11 } from "child_process";
4723
+ import { hostname as hostname5 } from "os";
4600
4724
  import { basename } from "path";
4601
- import { defineCommand as defineCommand35 } from "citty";
4602
- import consola30 from "consola";
4725
+ import { defineCommand as defineCommand36 } from "citty";
4726
+ import consola31 from "consola";
4603
4727
  function shouldWaitForGrant(args) {
4604
4728
  return args.wait === true || process.env.APE_WAIT === "1";
4605
4729
  }
@@ -4636,7 +4760,7 @@ function printPendingGrantInfo(grant, idp) {
4636
4760
  const statusCmd = `apes grants status ${grant.id}`;
4637
4761
  const executeCmd = `apes grants run ${grant.id}`;
4638
4762
  if (mode === "human") {
4639
- consola30.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
4763
+ consola31.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
4640
4764
  console.log(` Approve in browser: ${approveUrl}`);
4641
4765
  console.log(` Check status: ${statusCmd}`);
4642
4766
  console.log(` Run after approval: ${executeCmd}`);
@@ -4646,7 +4770,7 @@ function printPendingGrantInfo(grant, idp) {
4646
4770
  return;
4647
4771
  }
4648
4772
  const maxMin = getPollMaxMinutes();
4649
- consola30.success(`Grant ${grant.id} created (pending approval)`);
4773
+ consola31.success(`Grant ${grant.id} created (pending approval)`);
4650
4774
  console.log(` Approve: ${approveUrl}`);
4651
4775
  console.log(` Status: ${statusCmd} [--json]`);
4652
4776
  console.log(` Execute: ${executeCmd} --wait`);
@@ -4668,7 +4792,7 @@ function printPendingGrantInfo(grant, idp) {
4668
4792
  console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
4669
4793
  console.log(" grant be reused on subsequent invocations without re-approval.");
4670
4794
  }
4671
- var runCommand = defineCommand35({
4795
+ var runCommand = defineCommand36({
4672
4796
  meta: {
4673
4797
  name: "run",
4674
4798
  description: "Execute a grant-secured command"
@@ -4757,7 +4881,7 @@ async function runShellMode(command, args) {
4757
4881
  const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
4758
4882
  if (adapterHandled) return;
4759
4883
  const grantsUrl = await getGrantsEndpoint(idp);
4760
- const targetHost = args.host || hostname4();
4884
+ const targetHost = args.host || hostname5();
4761
4885
  try {
4762
4886
  const grants = await apiFetch(
4763
4887
  `${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
@@ -4771,7 +4895,7 @@ async function runShellMode(command, args) {
4771
4895
  }
4772
4896
  } catch {
4773
4897
  }
4774
- consola30.info(`Requesting ape-shell session grant on ${targetHost}`);
4898
+ consola31.info(`Requesting ape-shell session grant on ${targetHost}`);
4775
4899
  const grant = await apiFetch(grantsUrl, {
4776
4900
  method: "POST",
4777
4901
  body: {
@@ -4791,8 +4915,8 @@ async function runShellMode(command, args) {
4791
4915
  host: targetHost
4792
4916
  });
4793
4917
  if (shouldWaitForGrant(args)) {
4794
- consola30.info(`Grant requested: ${grant.id}`);
4795
- consola30.info("Waiting for approval...");
4918
+ consola31.info(`Grant requested: ${grant.id}`);
4919
+ consola31.info("Waiting for approval...");
4796
4920
  const maxWait = 3e5;
4797
4921
  const interval = 3e3;
4798
4922
  const start = Date.now();
@@ -4823,13 +4947,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
4823
4947
  try {
4824
4948
  resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
4825
4949
  } catch (err) {
4826
- consola30.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
4950
+ consola31.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
4827
4951
  return false;
4828
4952
  }
4829
4953
  try {
4830
4954
  const existingGrantId = await findExistingGrant(resolved, idp);
4831
4955
  if (existingGrantId) {
4832
- consola30.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
4956
+ consola31.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
4833
4957
  const token = await fetchGrantToken(idp, existingGrantId);
4834
4958
  await verifyAndExecute(token, resolved, existingGrantId);
4835
4959
  return true;
@@ -4837,7 +4961,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
4837
4961
  } catch {
4838
4962
  }
4839
4963
  const approval = args.approval ?? "once";
4840
- consola30.info(`Requesting grant for: ${resolved.detail.display}`);
4964
+ consola31.info(`Requesting grant for: ${resolved.detail.display}`);
4841
4965
  const grant = await createShapesGrant(resolved, {
4842
4966
  idp,
4843
4967
  approval,
@@ -4845,19 +4969,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
4845
4969
  });
4846
4970
  if (grant.similar_grants?.similar_grants?.length) {
4847
4971
  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.`);
4972
+ consola31.info("");
4973
+ consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
4850
4974
  }
4851
4975
  notifyGrantPending({
4852
4976
  grantId: grant.id,
4853
4977
  approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
4854
4978
  command: resolved.detail?.display || parsed?.raw || "unknown",
4855
4979
  audience: resolved.adapter?.cli?.audience ?? "shapes",
4856
- host: args.host || hostname4()
4980
+ host: args.host || hostname5()
4857
4981
  });
4858
4982
  if (shouldWaitForGrant(args)) {
4859
- consola30.info(`Grant requested: ${grant.id}`);
4860
- consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4983
+ consola31.info(`Grant requested: ${grant.id}`);
4984
+ consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4861
4985
  const status = await waitForGrantStatus(idp, grant.id);
4862
4986
  if (status !== "approved")
4863
4987
  throw new CliError(`Grant ${status}`);
@@ -4873,7 +4997,7 @@ function execShellCommand(command) {
4873
4997
  throw new CliError("No command to execute");
4874
4998
  try {
4875
4999
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
4876
- execFileSync12(command[0], command.slice(1), {
5000
+ execFileSync11(command[0], command.slice(1), {
4877
5001
  stdio: "inherit",
4878
5002
  env: inheritedEnv
4879
5003
  });
@@ -4931,7 +5055,7 @@ async function runAdapterMode(command, rawArgs, args) {
4931
5055
  try {
4932
5056
  const existingGrantId = await findExistingGrant(resolved, idp);
4933
5057
  if (existingGrantId) {
4934
- consola30.info(`Reusing existing grant: ${existingGrantId}`);
5058
+ consola31.info(`Reusing existing grant: ${existingGrantId}`);
4935
5059
  const token = await fetchGrantToken(idp, existingGrantId);
4936
5060
  await verifyAndExecute(token, resolved, existingGrantId);
4937
5061
  return;
@@ -4945,17 +5069,17 @@ async function runAdapterMode(command, rawArgs, args) {
4945
5069
  });
4946
5070
  if (grant.similar_grants?.similar_grants?.length) {
4947
5071
  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.`);
5072
+ consola31.info("");
5073
+ consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
4950
5074
  if (grant.similar_grants.widened_details?.length) {
4951
5075
  const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
4952
- consola30.info(` Broader scope: ${wider}`);
5076
+ consola31.info(` Broader scope: ${wider}`);
4953
5077
  }
4954
- consola30.info("");
5078
+ consola31.info("");
4955
5079
  }
4956
5080
  if (shouldWaitForGrant(args)) {
4957
- consola30.info(`Grant requested: ${grant.id}`);
4958
- consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5081
+ consola31.info(`Grant requested: ${grant.id}`);
5082
+ consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4959
5083
  const status = await waitForGrantStatus(idp, grant.id);
4960
5084
  if (status !== "approved")
4961
5085
  throw new Error(`Grant ${status}`);
@@ -4974,8 +5098,8 @@ async function runAudienceMode(audience, action, args) {
4974
5098
  const idp = getIdpUrl(args.idp);
4975
5099
  const grantsUrl = await getGrantsEndpoint(idp);
4976
5100
  const command = action.split(" ");
4977
- const targetHost = args.host || hostname4();
4978
- consola30.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
5101
+ const targetHost = args.host || hostname5();
5102
+ consola31.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
4979
5103
  const grant = await apiFetch(grantsUrl, {
4980
5104
  method: "POST",
4981
5105
  body: {
@@ -4992,9 +5116,9 @@ async function runAudienceMode(audience, action, args) {
4992
5116
  printPendingGrantInfo(grant, idp);
4993
5117
  throw new CliExit(getAsyncExitCode());
4994
5118
  }
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...");
5119
+ consola31.success(`Grant requested: ${grant.id}`);
5120
+ consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5121
+ consola31.info("Waiting for approval...");
4998
5122
  const maxWait = 15 * 60 * 1e3;
4999
5123
  const interval = 3e3;
5000
5124
  const start = Date.now();
@@ -5002,7 +5126,7 @@ async function runAudienceMode(audience, action, args) {
5002
5126
  while (Date.now() - start < maxWait) {
5003
5127
  const status = await apiFetch(`${grantsUrl}/${grant.id}`);
5004
5128
  if (status.status === "approved") {
5005
- consola30.success("Grant approved!");
5129
+ consola31.success("Grant approved!");
5006
5130
  approved = true;
5007
5131
  break;
5008
5132
  }
@@ -5017,15 +5141,15 @@ async function runAudienceMode(audience, action, args) {
5017
5141
  `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
5142
  );
5019
5143
  }
5020
- consola30.info("Fetching grant token...");
5144
+ consola31.info("Fetching grant token...");
5021
5145
  const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
5022
5146
  method: "POST"
5023
5147
  });
5024
5148
  if (audience === "escapes") {
5025
- consola30.info(`Executing: ${command.join(" ")}`);
5149
+ consola31.info(`Executing: ${command.join(" ")}`);
5026
5150
  try {
5027
5151
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5028
- execFileSync12(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
5152
+ execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
5029
5153
  stdio: "inherit",
5030
5154
  env: inheritedEnv
5031
5155
  });
@@ -5040,8 +5164,8 @@ async function runAudienceMode(audience, action, args) {
5040
5164
 
5041
5165
  // src/commands/proxy.ts
5042
5166
  import { spawn as spawn2 } from "child_process";
5043
- import { defineCommand as defineCommand36 } from "citty";
5044
- import consola31 from "consola";
5167
+ import { defineCommand as defineCommand37 } from "citty";
5168
+ import consola32 from "consola";
5045
5169
 
5046
5170
  // src/proxy/config.ts
5047
5171
  function buildDefaultProxyConfigToml(opts) {
@@ -5075,10 +5199,10 @@ note = "VPC-internal hostname suffix"
5075
5199
 
5076
5200
  // src/proxy/local-proxy.ts
5077
5201
  import { spawn } from "child_process";
5078
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "fs";
5202
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
5079
5203
  import { createRequire } from "module";
5080
5204
  import { tmpdir as tmpdir3 } from "os";
5081
- import { dirname as dirname4, join as join10, resolve as resolve4 } from "path";
5205
+ import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
5082
5206
  var require2 = createRequire(import.meta.url);
5083
5207
  function findProxyBin() {
5084
5208
  const pkgPath = require2.resolve("@openape/proxy/package.json");
@@ -5090,9 +5214,9 @@ function findProxyBin() {
5090
5214
  return resolve4(dirname4(pkgPath), binRel);
5091
5215
  }
5092
5216
  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 });
5217
+ const tmpDir = mkdtempSync3(join12(tmpdir3(), "openape-proxy-"));
5218
+ const configPath = join12(tmpDir, "config.toml");
5219
+ writeFileSync8(configPath, configToml, { mode: 384 });
5096
5220
  const binPath = findProxyBin();
5097
5221
  const child = spawn(process.execPath, [binPath, "-c", configPath], {
5098
5222
  stdio: ["ignore", "pipe", "pipe"],
@@ -5184,10 +5308,10 @@ function resolveProxyConfigOptions() {
5184
5308
  77
5185
5309
  );
5186
5310
  }
5187
- consola31.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
5311
+ consola32.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
5188
5312
  return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
5189
5313
  }
5190
- var proxyCommand = defineCommand36({
5314
+ var proxyCommand = defineCommand37({
5191
5315
  meta: {
5192
5316
  name: "proxy",
5193
5317
  description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
@@ -5209,12 +5333,12 @@ var proxyCommand = defineCommand36({
5209
5333
  let close = null;
5210
5334
  if (reuseUrl) {
5211
5335
  proxyUrl = reuseUrl;
5212
- consola31.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5336
+ consola32.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5213
5337
  } else {
5214
5338
  const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
5215
5339
  proxyUrl = ephemeral.url;
5216
5340
  close = ephemeral.close;
5217
- consola31.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5341
+ consola32.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5218
5342
  }
5219
5343
  const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
5220
5344
  const childEnv = {
@@ -5246,7 +5370,7 @@ var proxyCommand = defineCommand36({
5246
5370
  else resolveExit(code ?? 0);
5247
5371
  });
5248
5372
  child.once("error", (err) => {
5249
- consola31.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5373
+ consola32.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5250
5374
  resolveExit(127);
5251
5375
  });
5252
5376
  });
@@ -5260,8 +5384,8 @@ function signalNumber(signal) {
5260
5384
  }
5261
5385
 
5262
5386
  // src/commands/explain.ts
5263
- import { defineCommand as defineCommand37 } from "citty";
5264
- var explainCommand = defineCommand37({
5387
+ import { defineCommand as defineCommand38 } from "citty";
5388
+ var explainCommand = defineCommand38({
5265
5389
  meta: {
5266
5390
  name: "explain",
5267
5391
  description: "Show what permission a command would need"
@@ -5299,9 +5423,9 @@ var explainCommand = defineCommand37({
5299
5423
  });
5300
5424
 
5301
5425
  // src/commands/config/get.ts
5302
- import { defineCommand as defineCommand38 } from "citty";
5303
- import consola32 from "consola";
5304
- var configGetCommand = defineCommand38({
5426
+ import { defineCommand as defineCommand39 } from "citty";
5427
+ import consola33 from "consola";
5428
+ var configGetCommand = defineCommand39({
5305
5429
  meta: {
5306
5430
  name: "get",
5307
5431
  description: "Get a configuration value"
@@ -5321,7 +5445,7 @@ var configGetCommand = defineCommand38({
5321
5445
  if (idp)
5322
5446
  console.log(idp);
5323
5447
  else
5324
- consola32.info("No IdP configured.");
5448
+ consola33.info("No IdP configured.");
5325
5449
  break;
5326
5450
  }
5327
5451
  case "email": {
@@ -5329,7 +5453,7 @@ var configGetCommand = defineCommand38({
5329
5453
  if (auth?.email)
5330
5454
  console.log(auth.email);
5331
5455
  else
5332
- consola32.info("Not logged in.");
5456
+ consola33.info("Not logged in.");
5333
5457
  break;
5334
5458
  }
5335
5459
  default: {
@@ -5342,7 +5466,7 @@ var configGetCommand = defineCommand38({
5342
5466
  if (sectionObj && field in sectionObj) {
5343
5467
  console.log(sectionObj[field]);
5344
5468
  } else {
5345
- consola32.info(`Key "${key}" not set.`);
5469
+ consola33.info(`Key "${key}" not set.`);
5346
5470
  }
5347
5471
  } else {
5348
5472
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -5353,9 +5477,9 @@ var configGetCommand = defineCommand38({
5353
5477
  });
5354
5478
 
5355
5479
  // src/commands/config/set.ts
5356
- import { defineCommand as defineCommand39 } from "citty";
5357
- import consola33 from "consola";
5358
- var configSetCommand = defineCommand39({
5480
+ import { defineCommand as defineCommand40 } from "citty";
5481
+ import consola34 from "consola";
5482
+ var configSetCommand = defineCommand40({
5359
5483
  meta: {
5360
5484
  name: "set",
5361
5485
  description: "Set a configuration value"
@@ -5391,12 +5515,12 @@ var configSetCommand = defineCommand39({
5391
5515
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
5392
5516
  }
5393
5517
  saveConfig(config);
5394
- consola33.success(`Set ${key} = ${value}`);
5518
+ consola34.success(`Set ${key} = ${value}`);
5395
5519
  }
5396
5520
  });
5397
5521
 
5398
5522
  // src/commands/fetch/index.ts
5399
- import { defineCommand as defineCommand40 } from "citty";
5523
+ import { defineCommand as defineCommand41 } from "citty";
5400
5524
  async function doRequest(method, url, body, contentType, raw, showHeaders) {
5401
5525
  const token = getAuthToken();
5402
5526
  if (!token) {
@@ -5432,13 +5556,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
5432
5556
  throw new CliError(`HTTP ${response.status} ${response.statusText}`);
5433
5557
  }
5434
5558
  }
5435
- var fetchCommand = defineCommand40({
5559
+ var fetchCommand = defineCommand41({
5436
5560
  meta: {
5437
5561
  name: "fetch",
5438
5562
  description: "Make authenticated HTTP requests"
5439
5563
  },
5440
5564
  subCommands: {
5441
- get: defineCommand40({
5565
+ get: defineCommand41({
5442
5566
  meta: {
5443
5567
  name: "get",
5444
5568
  description: "GET request with auth token"
@@ -5464,7 +5588,7 @@ var fetchCommand = defineCommand40({
5464
5588
  await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
5465
5589
  }
5466
5590
  }),
5467
- post: defineCommand40({
5591
+ post: defineCommand41({
5468
5592
  meta: {
5469
5593
  name: "post",
5470
5594
  description: "POST request with auth token"
@@ -5503,8 +5627,8 @@ var fetchCommand = defineCommand40({
5503
5627
  });
5504
5628
 
5505
5629
  // src/commands/mcp/index.ts
5506
- import { defineCommand as defineCommand41 } from "citty";
5507
- var mcpCommand = defineCommand41({
5630
+ import { defineCommand as defineCommand42 } from "citty";
5631
+ var mcpCommand = defineCommand42({
5508
5632
  meta: {
5509
5633
  name: "mcp",
5510
5634
  description: "Start MCP server for AI agents"
@@ -5527,48 +5651,48 @@ var mcpCommand = defineCommand41({
5527
5651
  if (transport !== "stdio" && transport !== "sse") {
5528
5652
  throw new Error('Transport must be "stdio" or "sse"');
5529
5653
  }
5530
- const { startMcpServer } = await import("./server-AK2JBMJD.js");
5654
+ const { startMcpServer } = await import("./server-UKQ5QFOZ.js");
5531
5655
  await startMcpServer(transport, port);
5532
5656
  }
5533
5657
  });
5534
5658
 
5535
5659
  // src/commands/init/index.ts
5536
- import { existsSync as existsSync12, copyFileSync, writeFileSync as writeFileSync8 } from "fs";
5660
+ import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
5537
5661
  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";
5662
+ import { execFileSync as execFileSync12 } from "child_process";
5663
+ import { join as join13 } from "path";
5664
+ import { defineCommand as defineCommand43 } from "citty";
5665
+ import consola35 from "consola";
5542
5666
  var DEFAULT_IDP_URL = "https://id.openape.at";
5543
5667
  async function downloadTemplate(repo, targetDir) {
5544
5668
  const { downloadTemplate: gigetDownload } = await import("giget");
5545
5669
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
5546
5670
  }
5547
5671
  function installDeps(dir) {
5548
- const hasLockFile = (name) => existsSync12(join11(dir, name));
5672
+ const hasLockFile = (name) => existsSync14(join13(dir, name));
5549
5673
  if (hasLockFile("pnpm-lock.yaml")) {
5550
- execFileSync13("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
5674
+ execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
5551
5675
  } else if (hasLockFile("bun.lockb")) {
5552
- execFileSync13("bun", ["install"], { cwd: dir, stdio: "inherit" });
5676
+ execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
5553
5677
  } else {
5554
- execFileSync13("npm", ["install"], { cwd: dir, stdio: "inherit" });
5678
+ execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
5555
5679
  }
5556
5680
  }
5557
5681
  async function promptChoice(message, choices) {
5558
- const result = await consola34.prompt(message, { type: "select", options: choices });
5682
+ const result = await consola35.prompt(message, { type: "select", options: choices });
5559
5683
  if (typeof result === "symbol") {
5560
5684
  throw new CliExit(0);
5561
5685
  }
5562
5686
  return result;
5563
5687
  }
5564
5688
  async function promptText(message, defaultValue) {
5565
- const result = await consola34.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
5689
+ const result = await consola35.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
5566
5690
  if (typeof result === "symbol") {
5567
5691
  throw new CliExit(0);
5568
5692
  }
5569
5693
  return result || defaultValue || "";
5570
5694
  }
5571
- var initCommand = defineCommand42({
5695
+ var initCommand = defineCommand43({
5572
5696
  meta: {
5573
5697
  name: "init",
5574
5698
  description: "Scaffold a new OpenApe project"
@@ -5610,23 +5734,23 @@ var initCommand = defineCommand42({
5610
5734
  });
5611
5735
  async function initSP(targetDir) {
5612
5736
  const dir = targetDir || "my-app";
5613
- if (existsSync12(join11(dir, "package.json"))) {
5737
+ if (existsSync14(join13(dir, "package.json"))) {
5614
5738
  throw new CliError(`Directory "${dir}" already contains a project.`);
5615
5739
  }
5616
- consola34.start("Scaffolding SP starter...");
5740
+ consola35.start("Scaffolding SP starter...");
5617
5741
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
5618
- consola34.success("Scaffolded from openape-sp-starter");
5619
- consola34.start("Installing dependencies...");
5742
+ consola35.success("Scaffolded from openape-sp-starter");
5743
+ consola35.start("Installing dependencies...");
5620
5744
  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)) {
5745
+ consola35.success("Dependencies installed");
5746
+ const envExample = join13(dir, ".env.example");
5747
+ const envFile = join13(dir, ".env");
5748
+ if (existsSync14(envExample) && !existsSync14(envFile)) {
5625
5749
  copyFileSync(envExample, envFile);
5626
- consola34.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
5750
+ consola35.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
5627
5751
  }
5628
5752
  console.log("");
5629
- consola34.box([
5753
+ consola35.box([
5630
5754
  `cd ${dir}`,
5631
5755
  "npm run dev",
5632
5756
  "",
@@ -5635,7 +5759,7 @@ async function initSP(targetDir) {
5635
5759
  }
5636
5760
  async function initIdP(targetDir) {
5637
5761
  const dir = targetDir || "my-idp";
5638
- if (existsSync12(join11(dir, "package.json"))) {
5762
+ if (existsSync14(join13(dir, "package.json"))) {
5639
5763
  throw new CliError(`Directory "${dir}" already contains a project.`);
5640
5764
  }
5641
5765
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -5645,15 +5769,15 @@ async function initIdP(targetDir) {
5645
5769
  "s3 (S3-compatible)"
5646
5770
  ]);
5647
5771
  const adminEmail = await promptText("Admin email");
5648
- consola34.start("Scaffolding IdP starter...");
5772
+ consola35.start("Scaffolding IdP starter...");
5649
5773
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
5650
- consola34.success("Scaffolded from openape-idp-starter");
5651
- consola34.start("Installing dependencies...");
5774
+ consola35.success("Scaffolded from openape-idp-starter");
5775
+ consola35.start("Installing dependencies...");
5652
5776
  installDeps(dir);
5653
- consola34.success("Dependencies installed");
5777
+ consola35.success("Dependencies installed");
5654
5778
  const sessionSecret = randomBytes(32).toString("hex");
5655
5779
  const managementToken = randomBytes(32).toString("hex");
5656
- consola34.success("Secrets generated");
5780
+ consola35.success("Secrets generated");
5657
5781
  const isLocalhost = domain === "localhost";
5658
5782
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
5659
5783
  const envContent = [
@@ -5667,11 +5791,11 @@ async function initIdP(targetDir) {
5667
5791
  `NUXT_OPENAPE_RP_ID=${domain}`,
5668
5792
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
5669
5793
  ].join("\n");
5670
- writeFileSync8(join11(dir, ".env"), `${envContent}
5794
+ writeFileSync9(join13(dir, ".env"), `${envContent}
5671
5795
  `, { mode: 384 });
5672
- consola34.success(".env created");
5796
+ consola35.success(".env created");
5673
5797
  console.log("");
5674
- consola34.box([
5798
+ consola35.box([
5675
5799
  `cd ${dir}`,
5676
5800
  "npm run dev",
5677
5801
  "",
@@ -5688,11 +5812,11 @@ async function initIdP(targetDir) {
5688
5812
 
5689
5813
  // src/commands/enroll.ts
5690
5814
  import { Buffer as Buffer5 } from "buffer";
5691
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
5815
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
5692
5816
  import { execFile as execFile2 } from "child_process";
5693
5817
  import { sign as sign2 } from "crypto";
5694
- import { defineCommand as defineCommand43 } from "citty";
5695
- import consola35 from "consola";
5818
+ import { defineCommand as defineCommand44 } from "citty";
5819
+ import consola36 from "consola";
5696
5820
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
5697
5821
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
5698
5822
  var POLL_INTERVAL = 3e3;
@@ -5704,7 +5828,7 @@ function openBrowser2(url) {
5704
5828
  }
5705
5829
  async function pollForEnrollment(idp, agentEmail, keyPath) {
5706
5830
  const resolvedKey = resolveKeyPath(keyPath);
5707
- const keyContent = readFileSync11(resolvedKey, "utf-8");
5831
+ const keyContent = readFileSync12(resolvedKey, "utf-8");
5708
5832
  const privateKey = loadEd25519PrivateKey(keyContent);
5709
5833
  const challengeUrl = await getAgentChallengeEndpoint(idp);
5710
5834
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -5735,7 +5859,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
5735
5859
  }
5736
5860
  throw new Error("Enrollment timed out. Please check the browser and try again.");
5737
5861
  }
5738
- var enrollCommand = defineCommand43({
5862
+ var enrollCommand = defineCommand44({
5739
5863
  meta: {
5740
5864
  name: "enroll",
5741
5865
  description: "Enroll an agent with an Identity Provider"
@@ -5755,38 +5879,38 @@ var enrollCommand = defineCommand43({
5755
5879
  }
5756
5880
  },
5757
5881
  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) => {
5882
+ const idp = args.idp || await consola36.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
5759
5883
  if (typeof r === "symbol") throw new CliExit(0);
5760
5884
  return r;
5761
5885
  }) || DEFAULT_IDP_URL2;
5762
- const agentName = args.name || await consola35.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
5886
+ const agentName = args.name || await consola36.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
5763
5887
  if (typeof r === "symbol") throw new CliExit(0);
5764
5888
  return r;
5765
5889
  });
5766
5890
  if (!agentName) {
5767
5891
  throw new CliError("Agent name is required.");
5768
5892
  }
5769
- const keyPath = args.key || await consola35.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
5893
+ const keyPath = args.key || await consola36.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
5770
5894
  if (typeof r === "symbol") throw new CliExit(0);
5771
5895
  return r;
5772
5896
  }) || DEFAULT_KEY_PATH;
5773
5897
  const resolvedKey = resolveKeyPath(keyPath);
5774
5898
  let publicKey;
5775
- if (existsSync13(resolvedKey)) {
5899
+ if (existsSync15(resolvedKey)) {
5776
5900
  publicKey = readPublicKey(resolvedKey);
5777
- consola35.success(`Using existing key ${keyPath}`);
5901
+ consola36.success(`Using existing key ${keyPath}`);
5778
5902
  } else {
5779
- consola35.start(`Generating Ed25519 key pair at ${keyPath}...`);
5903
+ consola36.start(`Generating Ed25519 key pair at ${keyPath}...`);
5780
5904
  publicKey = generateAndSaveKey(keyPath);
5781
- consola35.success(`Key pair generated at ${keyPath}`);
5905
+ consola36.success(`Key pair generated at ${keyPath}`);
5782
5906
  }
5783
5907
  const encodedKey = encodeURIComponent(publicKey);
5784
5908
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
5785
- consola35.info("Opening browser for enrollment...");
5786
- consola35.info(`\u2192 ${idp}/enroll`);
5909
+ consola36.info("Opening browser for enrollment...");
5910
+ consola36.info(`\u2192 ${idp}/enroll`);
5787
5911
  openBrowser2(enrollUrl);
5788
5912
  console.log("");
5789
- const agentEmail = await consola35.prompt(
5913
+ const agentEmail = await consola36.prompt(
5790
5914
  "Agent email (shown in browser after enrollment)",
5791
5915
  { type: "text", placeholder: `agent+${agentName}@...` }
5792
5916
  ).then((r) => {
@@ -5796,7 +5920,7 @@ var enrollCommand = defineCommand43({
5796
5920
  if (!agentEmail) {
5797
5921
  throw new CliError("Agent email is required to verify enrollment.");
5798
5922
  }
5799
- consola35.start("Verifying enrollment...");
5923
+ consola36.start("Verifying enrollment...");
5800
5924
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
5801
5925
  saveAuth({
5802
5926
  idp,
@@ -5808,18 +5932,18 @@ var enrollCommand = defineCommand43({
5808
5932
  config.defaults = { ...config.defaults, idp };
5809
5933
  config.agent = { key: keyPath, email: agentEmail };
5810
5934
  saveConfig(config);
5811
- consola35.success(`Agent enrolled as ${agentEmail}`);
5812
- consola35.success("Config saved to ~/.config/apes/");
5935
+ consola36.success(`Agent enrolled as ${agentEmail}`);
5936
+ consola36.success("Config saved to ~/.config/apes/");
5813
5937
  console.log("");
5814
- consola35.info("Verify with: apes whoami");
5938
+ consola36.info("Verify with: apes whoami");
5815
5939
  }
5816
5940
  });
5817
5941
 
5818
5942
  // 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({
5943
+ import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
5944
+ import { defineCommand as defineCommand45 } from "citty";
5945
+ import consola37 from "consola";
5946
+ var registerUserCommand = defineCommand45({
5823
5947
  meta: {
5824
5948
  name: "register-user",
5825
5949
  description: "Register a sub-user with SSH key"
@@ -5855,8 +5979,8 @@ var registerUserCommand = defineCommand44({
5855
5979
  throw new CliError("No IdP URL configured. Run `apes login` first.");
5856
5980
  }
5857
5981
  let publicKey = args.key;
5858
- if (existsSync14(args.key)) {
5859
- publicKey = readFileSync12(args.key, "utf-8").trim();
5982
+ if (existsSync16(args.key)) {
5983
+ publicKey = readFileSync13(args.key, "utf-8").trim();
5860
5984
  }
5861
5985
  if (!publicKey.startsWith("ssh-ed25519 ")) {
5862
5986
  throw new CliError("Public key must be in ssh-ed25519 format.");
@@ -5874,18 +5998,18 @@ var registerUserCommand = defineCommand44({
5874
5998
  ...userType ? { type: userType } : {}
5875
5999
  }
5876
6000
  });
5877
- consola36.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
6001
+ consola37.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
5878
6002
  }
5879
6003
  });
5880
6004
 
5881
6005
  // src/commands/utils/index.ts
5882
- import { defineCommand as defineCommand46 } from "citty";
6006
+ import { defineCommand as defineCommand47 } from "citty";
5883
6007
 
5884
6008
  // src/commands/utils/dig.ts
5885
- import { defineCommand as defineCommand45 } from "citty";
5886
- import consola37 from "consola";
6009
+ import { defineCommand as defineCommand46 } from "citty";
6010
+ import consola38 from "consola";
5887
6011
  import { resolveDDISA as resolveDDISA2 } from "@openape/core";
5888
- var digCommand = defineCommand45({
6012
+ var digCommand = defineCommand46({
5889
6013
  meta: {
5890
6014
  name: "dig",
5891
6015
  description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
@@ -5958,12 +6082,12 @@ var digCommand = defineCommand45({
5958
6082
  console.log(` domain: ${domain}`);
5959
6083
  console.log("");
5960
6084
  if (!result.ddisa.found) {
5961
- consola37.warn(`No DDISA record at _ddisa.${domain}`);
6085
+ consola38.warn(`No DDISA record at _ddisa.${domain}`);
5962
6086
  if (result.hint) console.log(`
5963
6087
  ${result.hint}`);
5964
6088
  throw new CliError(`No DDISA record found for ${domain}`);
5965
6089
  }
5966
- consola37.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
6090
+ consola38.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
5967
6091
  console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
5968
6092
  console.log(` IdP URL: ${result.ddisa.idp}`);
5969
6093
  if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
@@ -5973,13 +6097,13 @@ ${result.hint}`);
5973
6097
  return;
5974
6098
  }
5975
6099
  if (result.idpDiscovery.ok) {
5976
- consola37.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
6100
+ consola38.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
5977
6101
  if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
5978
6102
  if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
5979
6103
  if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
5980
6104
  if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
5981
6105
  } else {
5982
- consola37.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
6106
+ consola38.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
5983
6107
  if (result.hint) console.log(`
5984
6108
  ${result.hint}`);
5985
6109
  throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
@@ -5988,7 +6112,7 @@ ${result.hint}`);
5988
6112
  });
5989
6113
 
5990
6114
  // src/commands/utils/index.ts
5991
- var utilsCommand = defineCommand46({
6115
+ var utilsCommand = defineCommand47({
5992
6116
  meta: {
5993
6117
  name: "utils",
5994
6118
  description: "Admin/diagnostic utilities (dig, \u2026)"
@@ -5999,12 +6123,12 @@ var utilsCommand = defineCommand46({
5999
6123
  });
6000
6124
 
6001
6125
  // src/commands/sessions/index.ts
6002
- import { defineCommand as defineCommand49 } from "citty";
6126
+ import { defineCommand as defineCommand50 } from "citty";
6003
6127
 
6004
6128
  // src/commands/sessions/list.ts
6005
- import { defineCommand as defineCommand47 } from "citty";
6006
- import consola38 from "consola";
6007
- var sessionsListCommand = defineCommand47({
6129
+ import { defineCommand as defineCommand48 } from "citty";
6130
+ import consola39 from "consola";
6131
+ var sessionsListCommand = defineCommand48({
6008
6132
  meta: {
6009
6133
  name: "list",
6010
6134
  description: "List your active refresh-token families (one per logged-in device)."
@@ -6022,7 +6146,7 @@ var sessionsListCommand = defineCommand47({
6022
6146
  return;
6023
6147
  }
6024
6148
  if (result.data.length === 0) {
6025
- consola38.info("No active sessions.");
6149
+ consola39.info("No active sessions.");
6026
6150
  return;
6027
6151
  }
6028
6152
  for (const f of result.data) {
@@ -6034,9 +6158,9 @@ var sessionsListCommand = defineCommand47({
6034
6158
  });
6035
6159
 
6036
6160
  // src/commands/sessions/remove.ts
6037
- import { defineCommand as defineCommand48 } from "citty";
6038
- import consola39 from "consola";
6039
- var sessionsRemoveCommand = defineCommand48({
6161
+ import { defineCommand as defineCommand49 } from "citty";
6162
+ import consola40 from "consola";
6163
+ var sessionsRemoveCommand = defineCommand49({
6040
6164
  meta: {
6041
6165
  name: "remove",
6042
6166
  description: "Revoke one of your active refresh-token families by id."
@@ -6052,12 +6176,12 @@ var sessionsRemoveCommand = defineCommand48({
6052
6176
  const id = String(args.familyId).trim();
6053
6177
  if (!id) throw new CliError("familyId required");
6054
6178
  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.`);
6179
+ consola40.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
6056
6180
  }
6057
6181
  });
6058
6182
 
6059
6183
  // src/commands/sessions/index.ts
6060
- var sessionsCommand = defineCommand49({
6184
+ var sessionsCommand = defineCommand50({
6061
6185
  meta: {
6062
6186
  name: "sessions",
6063
6187
  description: "Manage your active refresh-token sessions across devices"
@@ -6069,10 +6193,10 @@ var sessionsCommand = defineCommand49({
6069
6193
  });
6070
6194
 
6071
6195
  // src/commands/dns-check.ts
6072
- import { defineCommand as defineCommand50 } from "citty";
6073
- import consola40 from "consola";
6196
+ import { defineCommand as defineCommand51 } from "citty";
6197
+ import consola41 from "consola";
6074
6198
  import { resolveDDISA as resolveDDISA3 } from "@openape/core";
6075
- var dnsCheckCommand = defineCommand50({
6199
+ var dnsCheckCommand = defineCommand51({
6076
6200
  meta: {
6077
6201
  name: "dns-check",
6078
6202
  description: "Validate DDISA DNS TXT records for a domain"
@@ -6086,7 +6210,7 @@ var dnsCheckCommand = defineCommand50({
6086
6210
  },
6087
6211
  async run({ args }) {
6088
6212
  const domain = args.domain;
6089
- consola40.start(`Checking _ddisa.${domain}...`);
6213
+ consola41.start(`Checking _ddisa.${domain}...`);
6090
6214
  try {
6091
6215
  const result = await resolveDDISA3(domain);
6092
6216
  if (!result) {
@@ -6095,7 +6219,7 @@ var dnsCheckCommand = defineCommand50({
6095
6219
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
6096
6220
  throw new CliError(`No DDISA record found for ${domain}`);
6097
6221
  }
6098
- consola40.success(`_ddisa.${domain} \u2192 ${result.idp}`);
6222
+ consola41.success(`_ddisa.${domain} \u2192 ${result.idp}`);
6099
6223
  console.log("");
6100
6224
  console.log(` Version: ${result.version || "ddisa1"}`);
6101
6225
  console.log(` IdP URL: ${result.idp}`);
@@ -6104,14 +6228,14 @@ var dnsCheckCommand = defineCommand50({
6104
6228
  if (result.priority !== void 0)
6105
6229
  console.log(` Priority: ${result.priority}`);
6106
6230
  console.log("");
6107
- consola40.start(`Verifying IdP at ${result.idp}...`);
6231
+ consola41.start(`Verifying IdP at ${result.idp}...`);
6108
6232
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
6109
6233
  if (!discoResp.ok) {
6110
- consola40.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
6234
+ consola41.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
6111
6235
  return;
6112
6236
  }
6113
6237
  const disco = await discoResp.json();
6114
- consola40.success(`IdP is reachable`);
6238
+ consola41.success(`IdP is reachable`);
6115
6239
  console.log(` Issuer: ${disco.issuer}`);
6116
6240
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
6117
6241
  if (disco.ddisa_auth_methods_supported) {
@@ -6129,7 +6253,7 @@ var dnsCheckCommand = defineCommand50({
6129
6253
  // src/commands/health.ts
6130
6254
  import { exec } from "child_process";
6131
6255
  import { promisify } from "util";
6132
- import { defineCommand as defineCommand51 } from "citty";
6256
+ import { defineCommand as defineCommand52 } from "citty";
6133
6257
  var execAsync = promisify(exec);
6134
6258
  async function resolveApeShellPath() {
6135
6259
  try {
@@ -6165,7 +6289,7 @@ async function bestEffortGrantCount(idp) {
6165
6289
  }
6166
6290
  }
6167
6291
  async function runHealth(args) {
6168
- const version = true ? "1.5.0" : "0.0.0";
6292
+ const version = true ? "1.6.1" : "0.0.0";
6169
6293
  const auth = loadAuth();
6170
6294
  if (!auth) {
6171
6295
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -6228,7 +6352,7 @@ async function runHealth(args) {
6228
6352
  throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
6229
6353
  }
6230
6354
  }
6231
- var healthCommand = defineCommand51({
6355
+ var healthCommand = defineCommand52({
6232
6356
  meta: {
6233
6357
  name: "health",
6234
6358
  description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
@@ -6246,8 +6370,8 @@ var healthCommand = defineCommand51({
6246
6370
  });
6247
6371
 
6248
6372
  // src/commands/workflows.ts
6249
- import { defineCommand as defineCommand52 } from "citty";
6250
- import consola41 from "consola";
6373
+ import { defineCommand as defineCommand53 } from "citty";
6374
+ import consola42 from "consola";
6251
6375
 
6252
6376
  // src/guides/index.ts
6253
6377
  var guides = [
@@ -6297,7 +6421,7 @@ var guides = [
6297
6421
  ];
6298
6422
 
6299
6423
  // src/commands/workflows.ts
6300
- var workflowsCommand = defineCommand52({
6424
+ var workflowsCommand = defineCommand53({
6301
6425
  meta: {
6302
6426
  name: "workflows",
6303
6427
  description: "Discover workflow guides"
@@ -6318,7 +6442,7 @@ var workflowsCommand = defineCommand52({
6318
6442
  if (args.id) {
6319
6443
  const guide = guides.find((g) => g.id === String(args.id));
6320
6444
  if (!guide) {
6321
- consola41.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
6445
+ consola42.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
6322
6446
  throw new CliError(`Guide not found: ${args.id}`);
6323
6447
  }
6324
6448
  if (args.json) {
@@ -6358,26 +6482,26 @@ var workflowsCommand = defineCommand52({
6358
6482
  });
6359
6483
 
6360
6484
  // 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";
6485
+ import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
6486
+ import { homedir as homedir13 } from "os";
6487
+ import { join as join14 } from "path";
6488
+ import consola43 from "consola";
6365
6489
  var PACKAGE_NAME = "@openape/apes";
6366
6490
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
6367
- var CACHE_FILE = join12(homedir12(), ".config", "apes", ".version-check.json");
6491
+ var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
6368
6492
  function readCache() {
6369
- if (!existsSync15(CACHE_FILE)) return null;
6493
+ if (!existsSync17(CACHE_FILE)) return null;
6370
6494
  try {
6371
- return JSON.parse(readFileSync13(CACHE_FILE, "utf-8"));
6495
+ return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
6372
6496
  } catch {
6373
6497
  return null;
6374
6498
  }
6375
6499
  }
6376
6500
  function writeCache(entry) {
6377
6501
  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 });
6502
+ const dir = join14(homedir13(), ".config", "apes");
6503
+ if (!existsSync17(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
6504
+ writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
6381
6505
  } catch {
6382
6506
  }
6383
6507
  }
@@ -6406,7 +6530,7 @@ async function fetchLatestVersion() {
6406
6530
  }
6407
6531
  function warnIfBehind(currentVersion, latest) {
6408
6532
  if (compareSemver(currentVersion, latest) < 0) {
6409
- consola42.warn(
6533
+ consola43.warn(
6410
6534
  `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
6535
  );
6412
6536
  }
@@ -6438,10 +6562,10 @@ if (shellRewrite) {
6438
6562
  if (shellRewrite.action === "rewrite") {
6439
6563
  process.argv = shellRewrite.argv;
6440
6564
  } else if (shellRewrite.action === "version") {
6441
- console.log(`ape-shell ${"1.5.0"} (OpenApe DDISA shell wrapper)`);
6565
+ console.log(`ape-shell ${"1.6.1"} (OpenApe DDISA shell wrapper)`);
6442
6566
  process.exit(0);
6443
6567
  } else if (shellRewrite.action === "help") {
6444
- console.log(`ape-shell ${"1.5.0"} \u2014 OpenApe DDISA shell wrapper`);
6568
+ console.log(`ape-shell ${"1.6.1"} \u2014 OpenApe DDISA shell wrapper`);
6445
6569
  console.log("");
6446
6570
  console.log("Usage:");
6447
6571
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -6465,7 +6589,7 @@ if (shellRewrite) {
6465
6589
  }
6466
6590
  }
6467
6591
  var debug = process.argv.includes("--debug");
6468
- var grantsCommand = defineCommand53({
6592
+ var grantsCommand = defineCommand54({
6469
6593
  meta: {
6470
6594
  name: "grants",
6471
6595
  description: "Grant management"
@@ -6486,7 +6610,7 @@ var grantsCommand = defineCommand53({
6486
6610
  "delegation-revoke": delegationRevokeCommand
6487
6611
  }
6488
6612
  });
6489
- var configCommand = defineCommand53({
6613
+ var configCommand = defineCommand54({
6490
6614
  meta: {
6491
6615
  name: "config",
6492
6616
  description: "Configuration management"
@@ -6496,10 +6620,10 @@ var configCommand = defineCommand53({
6496
6620
  set: configSetCommand
6497
6621
  }
6498
6622
  });
6499
- var main = defineCommand53({
6623
+ var main = defineCommand54({
6500
6624
  meta: {
6501
6625
  name: "apes",
6502
- version: "1.5.0",
6626
+ version: "1.6.1",
6503
6627
  description: "Unified CLI for OpenApe"
6504
6628
  },
6505
6629
  subCommands: {
@@ -6555,20 +6679,20 @@ async function maybeRefreshAuth() {
6555
6679
  }
6556
6680
  }
6557
6681
  await maybeRefreshAuth();
6558
- await maybeWarnStaleVersion("1.5.0").catch(() => {
6682
+ await maybeWarnStaleVersion("1.6.1").catch(() => {
6559
6683
  });
6560
6684
  runMain(main).catch((err) => {
6561
6685
  if (err instanceof CliExit) {
6562
6686
  process.exit(err.exitCode);
6563
6687
  }
6564
6688
  if (err instanceof CliError) {
6565
- consola43.error(err.message);
6689
+ consola44.error(err.message);
6566
6690
  process.exit(err.exitCode);
6567
6691
  }
6568
6692
  if (debug) {
6569
- consola43.error(err);
6693
+ consola44.error(err);
6570
6694
  } else {
6571
- consola43.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
6695
+ consola44.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
6572
6696
  }
6573
6697
  process.exit(1);
6574
6698
  });