@openape/apes 1.4.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 consola42 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 defineCommand52, 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,24 +3918,259 @@ var agentsCommand = defineCommand28({
3918
3918
  });
3919
3919
 
3920
3920
  // src/commands/nest/index.ts
3921
- import { defineCommand as defineCommand32 } from "citty";
3921
+ import { defineCommand as defineCommand34 } from "citty";
3922
3922
 
3923
- // src/commands/nest/install.ts
3924
- import { execFileSync as execFileSync9 } from "child_process";
3925
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
3926
- import { homedir as homedir10, userInfo as userInfo2 } from "os";
3923
+ // src/commands/nest/authorize.ts
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";
3927
3932
  import { join as join8 } from "path";
3928
3933
  import { defineCommand as defineCommand29 } from "citty";
3929
3934
  import consola25 from "consola";
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({
3944
+ meta: {
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."
3947
+ },
3948
+ args: {
3949
+ name: {
3950
+ type: "string",
3951
+ description: "Override the nest agent name (default: nest-<short-hostname>)"
3952
+ },
3953
+ force: {
3954
+ type: "boolean",
3955
+ description: "Re-enroll even if ~/.openape/nest/.config/apes/auth.json already exists"
3956
+ }
3957
+ },
3958
+ async run({ args }) {
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}`);
4003
+ consola25.info("");
4004
+ consola25.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
4005
+ consola25.info("");
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}`);
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.");
4082
+ }
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
+ }
4093
+
4094
+ // src/commands/nest/install.ts
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";
4101
+
4102
+ // src/commands/nest/apes-agents-adapter.ts
4103
+ var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
4104
+
4105
+ # Adapter for the \`apes agents\` subtree \u2014 written by \`apes nest install\`.
4106
+ # A capability-grant with selector \`name=*\` covers any agent name
4107
+ # (selectorValueMatches treats '*' as a glob), letting the nest spawn
4108
+ # and destroy without per-agent DDISA prompts.
4109
+
4110
+ [cli]
4111
+ id = "apes-agents"
4112
+ executable = "apes"
4113
+ audience = "shapes"
4114
+ version = "1"
4115
+
4116
+ # \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
4117
+ # AGENT LIFECYCLE \u2014 spawn / destroy / sync
4118
+ # \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
4119
+
4120
+ [[operation]]
4121
+ id = "agents.spawn"
4122
+ command = ["agents", "spawn"]
4123
+ positionals = ["name"]
4124
+ display = "Spawn agent {name}"
4125
+ action = "create"
4126
+ risk = "high"
4127
+ resource_chain = ["agents:name={name}"]
4128
+
4129
+ [[operation]]
4130
+ id = "agents.destroy"
4131
+ command = ["agents", "destroy"]
4132
+ positionals = ["name"]
4133
+ display = "Destroy agent {name}"
4134
+ action = "delete"
4135
+ risk = "critical"
4136
+ resource_chain = ["agents:name={name}"]
4137
+
4138
+ [[operation]]
4139
+ id = "agents.sync"
4140
+ command = ["agents", "sync"]
4141
+ display = "Sync agent state with troop"
4142
+ action = "edit"
4143
+ risk = "low"
4144
+ resource_chain = ["agents:*"]
4145
+
4146
+ [[operation]]
4147
+ id = "agents.list"
4148
+ command = ["agents", "list"]
4149
+ display = "List agents"
4150
+ action = "list"
4151
+ risk = "low"
4152
+ resource_chain = ["agents:*"]
4153
+
4154
+ [[operation]]
4155
+ id = "agents.allow"
4156
+ command = ["agents", "allow"]
4157
+ positionals = ["name", "peer_email"]
4158
+ display = "Allow agent {name} to accept contact requests from {peer_email}"
4159
+ action = "edit"
4160
+ risk = "medium"
4161
+ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
4162
+ `;
4163
+
4164
+ // src/commands/nest/install.ts
3930
4165
  var PLIST_LABEL = "ai.openape.nest";
3931
4166
  function plistPath() {
3932
- return join8(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4167
+ return join10(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
3933
4168
  }
3934
4169
  function escape2(s) {
3935
4170
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3936
4171
  }
3937
4172
  function buildPlist(args) {
3938
- const logsDir = join8(args.homeDir, "Library", "Logs");
4173
+ const logsDir = join10(args.userHome, "Library", "Logs");
3939
4174
  return `<?xml version="1.0" encoding="UTF-8"?>
3940
4175
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3941
4176
  <plist version="1.0">
@@ -3947,7 +4182,7 @@ function buildPlist(args) {
3947
4182
  <string>${escape2(args.nestBin)}</string>
3948
4183
  </array>
3949
4184
  <key>WorkingDirectory</key>
3950
- <string>${escape2(args.homeDir)}</string>
4185
+ <string>${escape2(args.nestHome)}</string>
3951
4186
  <key>RunAtLoad</key>
3952
4187
  <true/>
3953
4188
  <key>KeepAlive</key>
@@ -3956,8 +4191,8 @@ function buildPlist(args) {
3956
4191
  <integer>10</integer>
3957
4192
  <key>EnvironmentVariables</key>
3958
4193
  <dict>
3959
- <key>HOME</key><string>${escape2(args.homeDir)}</string>
3960
- <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>
3961
4196
  <key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
3962
4197
  <key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
3963
4198
  </dict>
@@ -3969,19 +4204,32 @@ function buildPlist(args) {
3969
4204
  </plist>
3970
4205
  `;
3971
4206
  }
4207
+ function installAdapter2() {
4208
+ const target = join10(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
4209
+ mkdirSync5(dirname3(target), { recursive: true });
4210
+ let existing = "";
4211
+ try {
4212
+ existing = readFileSync11(target, "utf8");
4213
+ } catch {
4214
+ }
4215
+ if (existing === APES_AGENTS_ADAPTER_TOML) return false;
4216
+ writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4217
+ consola27.success(`Wrote shapes adapter ${target}`);
4218
+ return true;
4219
+ }
3972
4220
  function findBinary(name) {
3973
4221
  for (const dir of [
3974
- join8(homedir10(), ".bun", "bin"),
4222
+ join10(homedir11(), ".bun", "bin"),
3975
4223
  "/opt/homebrew/bin",
3976
4224
  "/usr/local/bin",
3977
4225
  "/usr/bin"
3978
4226
  ]) {
3979
- const p = join8(dir, name);
3980
- if (existsSync10(p)) return p;
4227
+ const p = join10(dir, name);
4228
+ if (existsSync12(p)) return p;
3981
4229
  }
3982
4230
  throw new Error(`could not locate ${name} on PATH; install it first`);
3983
4231
  }
3984
- var installNestCommand = defineCommand29({
4232
+ var installNestCommand = defineCommand31({
3985
4233
  meta: {
3986
4234
  name: "install",
3987
4235
  description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
@@ -3993,29 +4241,31 @@ var installNestCommand = defineCommand29({
3993
4241
  }
3994
4242
  },
3995
4243
  async run({ args }) {
3996
- const homeDir = homedir10();
4244
+ const homeDir = homedir11();
3997
4245
  const port = Number(args.port ?? 9091);
3998
4246
  if (!Number.isInteger(port) || port < 1024 || port > 65535) {
3999
4247
  throw new Error(`invalid port ${port}`);
4000
4248
  }
4001
4249
  const nestBin = findBinary("openape-nest");
4002
4250
  const apesBin = findBinary("apes");
4003
- consola25.info(`Installing nest at ${plistPath()}`);
4004
- consola25.info(` nest binary: ${nestBin}`);
4005
- consola25.info(` apes binary: ${apesBin}`);
4006
- consola25.info(` HTTP port: ${port}`);
4007
- mkdirSync4(join8(homeDir, "Library", "LaunchAgents"), { recursive: true });
4008
- const desired = buildPlist({ nestBin, apesBin, homeDir, 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}`);
4255
+ installAdapter2();
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 });
4009
4259
  let existing = "";
4010
4260
  try {
4011
- existing = readFileSync10(plistPath(), "utf8");
4261
+ existing = readFileSync11(plistPath(), "utf8");
4012
4262
  } catch {
4013
4263
  }
4014
4264
  if (existing !== desired) {
4015
- writeFileSync6(plistPath(), desired, { mode: 420 });
4016
- consola25.success("Wrote launchd plist");
4265
+ writeFileSync7(plistPath(), desired, { mode: 420 });
4266
+ consola27.success("Wrote launchd plist");
4017
4267
  } else {
4018
- consola25.info("plist already up to date");
4268
+ consola27.info("plist already up to date");
4019
4269
  }
4020
4270
  const uid = userInfo2().uid;
4021
4271
  try {
@@ -4023,25 +4273,23 @@ var installNestCommand = defineCommand29({
4023
4273
  } catch {
4024
4274
  }
4025
4275
  execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4026
- consola25.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4027
- consola25.info("");
4028
- consola25.info("Next: approve the always-grant for nest-managed spawn/destroy.");
4029
- consola25.info('Run this once and choose "Always" when the IdP UI prompts:');
4030
- consola25.info("");
4031
- consola25.info(' apes run --as root --approval=always --reason "nest-managed agent spawn" \\');
4032
- consola25.info(" -- apes agents spawn _grant_pattern_seed_");
4033
- consola25.info("");
4034
- consola25.info('(The seed-spawn will fail because "_grant_pattern_seed_" is not a valid');
4035
- consola25.info("agent name \u2014 that's expected. The grant just needs to be approved-as-always.)");
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.");
4036
4284
  }
4037
4285
  });
4038
4286
 
4039
4287
  // src/commands/nest/status.ts
4040
4288
  import process2 from "process";
4041
- import { defineCommand as defineCommand30 } from "citty";
4042
- import consola26 from "consola";
4289
+ import { defineCommand as defineCommand32 } from "citty";
4290
+ import consola28 from "consola";
4043
4291
  var DEFAULT_PORT = 9091;
4044
- var statusNestCommand = defineCommand30({
4292
+ var statusNestCommand = defineCommand32({
4045
4293
  meta: {
4046
4294
  name: "status",
4047
4295
  description: "Print state of the local nest-daemon (agents registered, processes supervised)"
@@ -4061,8 +4309,8 @@ var statusNestCommand = defineCommand30({
4061
4309
  } catch (err) {
4062
4310
  const msg = err instanceof Error ? err.message : String(err);
4063
4311
  if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
4064
- consola26.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
4065
- consola26.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");
4066
4314
  process2.exit(2);
4067
4315
  }
4068
4316
  throw err;
@@ -4071,15 +4319,15 @@ var statusNestCommand = defineCommand30({
4071
4319
  console.log(JSON.stringify(status, null, 2));
4072
4320
  return;
4073
4321
  }
4074
- consola26.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`);
4075
4323
  if (status.processes.length === 0) {
4076
- consola26.info(" (no processes running)");
4324
+ consola28.info(" (no processes running)");
4077
4325
  return;
4078
4326
  }
4079
4327
  for (const p of status.processes) {
4080
4328
  const uptime = humanDuration(p.uptimeSec);
4081
4329
  const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
4082
- consola26.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}`);
4083
4331
  }
4084
4332
  }
4085
4333
  });
@@ -4092,57 +4340,59 @@ function humanDuration(sec) {
4092
4340
 
4093
4341
  // src/commands/nest/uninstall.ts
4094
4342
  import { execFileSync as execFileSync10 } from "child_process";
4095
- import { existsSync as existsSync11, unlinkSync } from "fs";
4096
- import { homedir as homedir11, userInfo as userInfo3 } from "os";
4097
- import { join as join9 } from "path";
4098
- import { defineCommand as defineCommand31 } from "citty";
4099
- import consola27 from "consola";
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";
4100
4348
  var PLIST_LABEL2 = "ai.openape.nest";
4101
- var uninstallNestCommand = defineCommand31({
4349
+ var uninstallNestCommand = defineCommand33({
4102
4350
  meta: {
4103
4351
  name: "uninstall",
4104
4352
  description: "Stop + remove the local nest-daemon (registry + agents preserved)"
4105
4353
  },
4106
4354
  async run() {
4107
4355
  const uid = userInfo3().uid;
4108
- const path2 = join9(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4356
+ const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4109
4357
  try {
4110
4358
  execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4111
- consola27.success("Nest daemon stopped");
4359
+ consola29.success("Nest daemon stopped");
4112
4360
  } catch {
4113
- consola27.info("Nest daemon was not loaded");
4361
+ consola29.info("Nest daemon was not loaded");
4114
4362
  }
4115
- if (existsSync11(path2)) {
4363
+ if (existsSync13(path2)) {
4116
4364
  unlinkSync(path2);
4117
- consola27.success(`Removed ${path2}`);
4365
+ consola29.success(`Removed ${path2}`);
4118
4366
  }
4119
- consola27.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.");
4120
4368
  }
4121
4369
  });
4122
4370
 
4123
4371
  // src/commands/nest/index.ts
4124
- var nestCommand = defineCommand32({
4372
+ var nestCommand = defineCommand34({
4125
4373
  meta: {
4126
4374
  name: "nest",
4127
- description: "Manage the local Nest control-plane daemon (install, status, uninstall). The Nest hosts agents on this computer \u2014 once installed, `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."
4128
4376
  },
4129
4377
  subCommands: {
4130
4378
  install: installNestCommand,
4379
+ enroll: enrollNestCommand,
4380
+ authorize: authorizeNestCommand,
4131
4381
  status: statusNestCommand,
4132
4382
  uninstall: uninstallNestCommand
4133
4383
  }
4134
4384
  });
4135
4385
 
4136
4386
  // src/commands/adapter/index.ts
4137
- import { defineCommand as defineCommand33 } from "citty";
4138
- import consola28 from "consola";
4139
- var adapterCommand = defineCommand33({
4387
+ import { defineCommand as defineCommand35 } from "citty";
4388
+ import consola30 from "consola";
4389
+ var adapterCommand = defineCommand35({
4140
4390
  meta: {
4141
4391
  name: "adapter",
4142
4392
  description: "Manage CLI adapters"
4143
4393
  },
4144
4394
  subCommands: {
4145
- list: defineCommand33({
4395
+ list: defineCommand35({
4146
4396
  meta: {
4147
4397
  name: "list",
4148
4398
  description: "List available adapters"
@@ -4173,7 +4423,7 @@ var adapterCommand = defineCommand33({
4173
4423
  `);
4174
4424
  return;
4175
4425
  }
4176
- consola28.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4426
+ consola30.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4177
4427
  for (const a of index2.adapters) {
4178
4428
  const installed = isInstalled(a.id, false) ? " [installed]" : "";
4179
4429
  console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
@@ -4195,7 +4445,7 @@ var adapterCommand = defineCommand33({
4195
4445
  return;
4196
4446
  }
4197
4447
  if (local.length === 0) {
4198
- consola28.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.");
4199
4449
  return;
4200
4450
  }
4201
4451
  for (const a of local) {
@@ -4203,7 +4453,7 @@ var adapterCommand = defineCommand33({
4203
4453
  }
4204
4454
  }
4205
4455
  }),
4206
- install: defineCommand33({
4456
+ install: defineCommand35({
4207
4457
  meta: {
4208
4458
  name: "install",
4209
4459
  description: "Install an adapter from the registry"
@@ -4232,24 +4482,24 @@ var adapterCommand = defineCommand33({
4232
4482
  for (const id of ids) {
4233
4483
  const entry = findAdapter(index, id);
4234
4484
  if (!entry) {
4235
- consola28.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.`);
4236
4486
  continue;
4237
4487
  }
4238
4488
  const conflicts = findConflictingAdapters(entry.executable, id);
4239
4489
  if (conflicts.length > 0) {
4240
4490
  for (const c of conflicts) {
4241
- consola28.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
4242
- consola28.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}`);
4243
4493
  }
4244
4494
  }
4245
4495
  const result = await installAdapter(entry, { local });
4246
4496
  const verb = result.updated ? "Updated" : "Installed";
4247
- consola28.success(`${verb} ${result.id} \u2192 ${result.path}`);
4248
- consola28.info(`Digest: ${result.digest}`);
4497
+ consola30.success(`${verb} ${result.id} \u2192 ${result.path}`);
4498
+ consola30.info(`Digest: ${result.digest}`);
4249
4499
  }
4250
4500
  }
4251
4501
  }),
4252
- remove: defineCommand33({
4502
+ remove: defineCommand35({
4253
4503
  meta: {
4254
4504
  name: "remove",
4255
4505
  description: "Remove an installed adapter"
@@ -4272,9 +4522,9 @@ var adapterCommand = defineCommand33({
4272
4522
  let failed = false;
4273
4523
  for (const id of ids) {
4274
4524
  if (removeAdapter(id, local)) {
4275
- consola28.success(`Removed adapter: ${id}`);
4525
+ consola30.success(`Removed adapter: ${id}`);
4276
4526
  } else {
4277
- consola28.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4527
+ consola30.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4278
4528
  failed = true;
4279
4529
  }
4280
4530
  }
@@ -4282,7 +4532,7 @@ var adapterCommand = defineCommand33({
4282
4532
  throw new CliError("Some adapters could not be removed");
4283
4533
  }
4284
4534
  }),
4285
- info: defineCommand33({
4535
+ info: defineCommand35({
4286
4536
  meta: {
4287
4537
  name: "info",
4288
4538
  description: "Show detailed adapter information"
@@ -4324,7 +4574,7 @@ var adapterCommand = defineCommand33({
4324
4574
  }
4325
4575
  }
4326
4576
  }),
4327
- search: defineCommand33({
4577
+ search: defineCommand35({
4328
4578
  meta: {
4329
4579
  name: "search",
4330
4580
  description: "Search adapters in the registry"
@@ -4356,7 +4606,7 @@ var adapterCommand = defineCommand33({
4356
4606
  return;
4357
4607
  }
4358
4608
  if (results.length === 0) {
4359
- consola28.info(`No adapters matching "${query}"`);
4609
+ consola30.info(`No adapters matching "${query}"`);
4360
4610
  return;
4361
4611
  }
4362
4612
  for (const a of results) {
@@ -4365,7 +4615,7 @@ var adapterCommand = defineCommand33({
4365
4615
  }
4366
4616
  }
4367
4617
  }),
4368
- update: defineCommand33({
4618
+ update: defineCommand35({
4369
4619
  meta: {
4370
4620
  name: "update",
4371
4621
  description: "Update installed adapters"
@@ -4391,33 +4641,33 @@ var adapterCommand = defineCommand33({
4391
4641
  const targetId = args.id ? String(args.id) : void 0;
4392
4642
  const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
4393
4643
  if (targets.length === 0) {
4394
- consola28.info("No adapters installed to update.");
4644
+ consola30.info("No adapters installed to update.");
4395
4645
  return;
4396
4646
  }
4397
4647
  for (const id of targets) {
4398
4648
  const entry = findAdapter(index, id);
4399
4649
  if (!entry) {
4400
- consola28.warn(`${id}: not found in registry, skipping`);
4650
+ consola30.warn(`${id}: not found in registry, skipping`);
4401
4651
  continue;
4402
4652
  }
4403
4653
  const localDigest = getInstalledDigest(id, false);
4404
4654
  if (localDigest === entry.digest) {
4405
- consola28.info(`${id}: already up to date`);
4655
+ consola30.info(`${id}: already up to date`);
4406
4656
  continue;
4407
4657
  }
4408
4658
  if (localDigest && !args.yes) {
4409
- consola28.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
4410
- consola28.info(` Old: ${localDigest}`);
4411
- consola28.info(` New: ${entry.digest}`);
4412
- consola28.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");
4413
4663
  continue;
4414
4664
  }
4415
4665
  const result = await installAdapter(entry);
4416
- consola28.success(`Updated ${result.id} \u2192 ${result.path}`);
4666
+ consola30.success(`Updated ${result.id} \u2192 ${result.path}`);
4417
4667
  }
4418
4668
  }
4419
4669
  }),
4420
- verify: defineCommand33({
4670
+ verify: defineCommand35({
4421
4671
  meta: {
4422
4672
  name: "verify",
4423
4673
  description: "Verify installed adapter against registry digest"
@@ -4450,7 +4700,7 @@ var adapterCommand = defineCommand33({
4450
4700
  if (!localDigest)
4451
4701
  throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4452
4702
  if (localDigest === entry.digest) {
4453
- consola28.success(`${id}: digest matches registry`);
4703
+ consola30.success(`${id}: digest matches registry`);
4454
4704
  } else {
4455
4705
  console.log(` Local: ${localDigest}`);
4456
4706
  console.log(` Registry: ${entry.digest}`);
@@ -4463,10 +4713,10 @@ var adapterCommand = defineCommand33({
4463
4713
 
4464
4714
  // src/commands/run.ts
4465
4715
  import { execFileSync as execFileSync11 } from "child_process";
4466
- import { hostname as hostname4 } from "os";
4716
+ import { hostname as hostname5 } from "os";
4467
4717
  import { basename } from "path";
4468
- import { defineCommand as defineCommand34 } from "citty";
4469
- import consola29 from "consola";
4718
+ import { defineCommand as defineCommand36 } from "citty";
4719
+ import consola31 from "consola";
4470
4720
  function shouldWaitForGrant(args) {
4471
4721
  return args.wait === true || process.env.APE_WAIT === "1";
4472
4722
  }
@@ -4503,7 +4753,7 @@ function printPendingGrantInfo(grant, idp) {
4503
4753
  const statusCmd = `apes grants status ${grant.id}`;
4504
4754
  const executeCmd = `apes grants run ${grant.id}`;
4505
4755
  if (mode === "human") {
4506
- consola29.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
4756
+ consola31.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
4507
4757
  console.log(` Approve in browser: ${approveUrl}`);
4508
4758
  console.log(` Check status: ${statusCmd}`);
4509
4759
  console.log(` Run after approval: ${executeCmd}`);
@@ -4513,7 +4763,7 @@ function printPendingGrantInfo(grant, idp) {
4513
4763
  return;
4514
4764
  }
4515
4765
  const maxMin = getPollMaxMinutes();
4516
- consola29.success(`Grant ${grant.id} created (pending approval)`);
4766
+ consola31.success(`Grant ${grant.id} created (pending approval)`);
4517
4767
  console.log(` Approve: ${approveUrl}`);
4518
4768
  console.log(` Status: ${statusCmd} [--json]`);
4519
4769
  console.log(` Execute: ${executeCmd} --wait`);
@@ -4535,7 +4785,7 @@ function printPendingGrantInfo(grant, idp) {
4535
4785
  console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
4536
4786
  console.log(" grant be reused on subsequent invocations without re-approval.");
4537
4787
  }
4538
- var runCommand = defineCommand34({
4788
+ var runCommand = defineCommand36({
4539
4789
  meta: {
4540
4790
  name: "run",
4541
4791
  description: "Execute a grant-secured command"
@@ -4624,7 +4874,7 @@ async function runShellMode(command, args) {
4624
4874
  const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
4625
4875
  if (adapterHandled) return;
4626
4876
  const grantsUrl = await getGrantsEndpoint(idp);
4627
- const targetHost = args.host || hostname4();
4877
+ const targetHost = args.host || hostname5();
4628
4878
  try {
4629
4879
  const grants = await apiFetch(
4630
4880
  `${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
@@ -4638,7 +4888,7 @@ async function runShellMode(command, args) {
4638
4888
  }
4639
4889
  } catch {
4640
4890
  }
4641
- consola29.info(`Requesting ape-shell session grant on ${targetHost}`);
4891
+ consola31.info(`Requesting ape-shell session grant on ${targetHost}`);
4642
4892
  const grant = await apiFetch(grantsUrl, {
4643
4893
  method: "POST",
4644
4894
  body: {
@@ -4658,8 +4908,8 @@ async function runShellMode(command, args) {
4658
4908
  host: targetHost
4659
4909
  });
4660
4910
  if (shouldWaitForGrant(args)) {
4661
- consola29.info(`Grant requested: ${grant.id}`);
4662
- consola29.info("Waiting for approval...");
4911
+ consola31.info(`Grant requested: ${grant.id}`);
4912
+ consola31.info("Waiting for approval...");
4663
4913
  const maxWait = 3e5;
4664
4914
  const interval = 3e3;
4665
4915
  const start = Date.now();
@@ -4690,13 +4940,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
4690
4940
  try {
4691
4941
  resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
4692
4942
  } catch (err) {
4693
- consola29.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
4943
+ consola31.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
4694
4944
  return false;
4695
4945
  }
4696
4946
  try {
4697
4947
  const existingGrantId = await findExistingGrant(resolved, idp);
4698
4948
  if (existingGrantId) {
4699
- consola29.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
4949
+ consola31.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
4700
4950
  const token = await fetchGrantToken(idp, existingGrantId);
4701
4951
  await verifyAndExecute(token, resolved, existingGrantId);
4702
4952
  return true;
@@ -4704,7 +4954,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
4704
4954
  } catch {
4705
4955
  }
4706
4956
  const approval = args.approval ?? "once";
4707
- consola29.info(`Requesting grant for: ${resolved.detail.display}`);
4957
+ consola31.info(`Requesting grant for: ${resolved.detail.display}`);
4708
4958
  const grant = await createShapesGrant(resolved, {
4709
4959
  idp,
4710
4960
  approval,
@@ -4712,19 +4962,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
4712
4962
  });
4713
4963
  if (grant.similar_grants?.similar_grants?.length) {
4714
4964
  const n = grant.similar_grants.similar_grants.length;
4715
- consola29.info("");
4716
- consola29.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.`);
4717
4967
  }
4718
4968
  notifyGrantPending({
4719
4969
  grantId: grant.id,
4720
4970
  approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
4721
4971
  command: resolved.detail?.display || parsed?.raw || "unknown",
4722
4972
  audience: resolved.adapter?.cli?.audience ?? "shapes",
4723
- host: args.host || hostname4()
4973
+ host: args.host || hostname5()
4724
4974
  });
4725
4975
  if (shouldWaitForGrant(args)) {
4726
- consola29.info(`Grant requested: ${grant.id}`);
4727
- consola29.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}`);
4728
4978
  const status = await waitForGrantStatus(idp, grant.id);
4729
4979
  if (status !== "approved")
4730
4980
  throw new CliError(`Grant ${status}`);
@@ -4798,7 +5048,7 @@ async function runAdapterMode(command, rawArgs, args) {
4798
5048
  try {
4799
5049
  const existingGrantId = await findExistingGrant(resolved, idp);
4800
5050
  if (existingGrantId) {
4801
- consola29.info(`Reusing existing grant: ${existingGrantId}`);
5051
+ consola31.info(`Reusing existing grant: ${existingGrantId}`);
4802
5052
  const token = await fetchGrantToken(idp, existingGrantId);
4803
5053
  await verifyAndExecute(token, resolved, existingGrantId);
4804
5054
  return;
@@ -4812,17 +5062,17 @@ async function runAdapterMode(command, rawArgs, args) {
4812
5062
  });
4813
5063
  if (grant.similar_grants?.similar_grants?.length) {
4814
5064
  const n = grant.similar_grants.similar_grants.length;
4815
- consola29.info("");
4816
- consola29.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.`);
4817
5067
  if (grant.similar_grants.widened_details?.length) {
4818
5068
  const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
4819
- consola29.info(` Broader scope: ${wider}`);
5069
+ consola31.info(` Broader scope: ${wider}`);
4820
5070
  }
4821
- consola29.info("");
5071
+ consola31.info("");
4822
5072
  }
4823
5073
  if (shouldWaitForGrant(args)) {
4824
- consola29.info(`Grant requested: ${grant.id}`);
4825
- consola29.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}`);
4826
5076
  const status = await waitForGrantStatus(idp, grant.id);
4827
5077
  if (status !== "approved")
4828
5078
  throw new Error(`Grant ${status}`);
@@ -4841,8 +5091,8 @@ async function runAudienceMode(audience, action, args) {
4841
5091
  const idp = getIdpUrl(args.idp);
4842
5092
  const grantsUrl = await getGrantsEndpoint(idp);
4843
5093
  const command = action.split(" ");
4844
- const targetHost = args.host || hostname4();
4845
- consola29.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
5094
+ const targetHost = args.host || hostname5();
5095
+ consola31.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
4846
5096
  const grant = await apiFetch(grantsUrl, {
4847
5097
  method: "POST",
4848
5098
  body: {
@@ -4859,9 +5109,9 @@ async function runAudienceMode(audience, action, args) {
4859
5109
  printPendingGrantInfo(grant, idp);
4860
5110
  throw new CliExit(getAsyncExitCode());
4861
5111
  }
4862
- consola29.success(`Grant requested: ${grant.id}`);
4863
- consola29.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
4864
- consola29.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...");
4865
5115
  const maxWait = 15 * 60 * 1e3;
4866
5116
  const interval = 3e3;
4867
5117
  const start = Date.now();
@@ -4869,7 +5119,7 @@ async function runAudienceMode(audience, action, args) {
4869
5119
  while (Date.now() - start < maxWait) {
4870
5120
  const status = await apiFetch(`${grantsUrl}/${grant.id}`);
4871
5121
  if (status.status === "approved") {
4872
- consola29.success("Grant approved!");
5122
+ consola31.success("Grant approved!");
4873
5123
  approved = true;
4874
5124
  break;
4875
5125
  }
@@ -4884,12 +5134,12 @@ async function runAudienceMode(audience, action, args) {
4884
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.`
4885
5135
  );
4886
5136
  }
4887
- consola29.info("Fetching grant token...");
5137
+ consola31.info("Fetching grant token...");
4888
5138
  const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
4889
5139
  method: "POST"
4890
5140
  });
4891
5141
  if (audience === "escapes") {
4892
- consola29.info(`Executing: ${command.join(" ")}`);
5142
+ consola31.info(`Executing: ${command.join(" ")}`);
4893
5143
  try {
4894
5144
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
4895
5145
  execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
@@ -4907,8 +5157,8 @@ async function runAudienceMode(audience, action, args) {
4907
5157
 
4908
5158
  // src/commands/proxy.ts
4909
5159
  import { spawn as spawn2 } from "child_process";
4910
- import { defineCommand as defineCommand35 } from "citty";
4911
- import consola30 from "consola";
5160
+ import { defineCommand as defineCommand37 } from "citty";
5161
+ import consola32 from "consola";
4912
5162
 
4913
5163
  // src/proxy/config.ts
4914
5164
  function buildDefaultProxyConfigToml(opts) {
@@ -4942,10 +5192,10 @@ note = "VPC-internal hostname suffix"
4942
5192
 
4943
5193
  // src/proxy/local-proxy.ts
4944
5194
  import { spawn } from "child_process";
4945
- 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";
4946
5196
  import { createRequire } from "module";
4947
5197
  import { tmpdir as tmpdir3 } from "os";
4948
- import { dirname as dirname3, join as join10, resolve as resolve4 } from "path";
5198
+ import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
4949
5199
  var require2 = createRequire(import.meta.url);
4950
5200
  function findProxyBin() {
4951
5201
  const pkgPath = require2.resolve("@openape/proxy/package.json");
@@ -4954,12 +5204,12 @@ function findProxyBin() {
4954
5204
  if (!binRel) {
4955
5205
  throw new Error("@openape/proxy is missing the openape-proxy bin entry");
4956
5206
  }
4957
- return resolve4(dirname3(pkgPath), binRel);
5207
+ return resolve4(dirname4(pkgPath), binRel);
4958
5208
  }
4959
5209
  async function startEphemeralProxy(configToml) {
4960
- const tmpDir = mkdtempSync3(join10(tmpdir3(), "openape-proxy-"));
4961
- const configPath = join10(tmpDir, "config.toml");
4962
- 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 });
4963
5213
  const binPath = findProxyBin();
4964
5214
  const child = spawn(process.execPath, [binPath, "-c", configPath], {
4965
5215
  stdio: ["ignore", "pipe", "pipe"],
@@ -5051,10 +5301,10 @@ function resolveProxyConfigOptions() {
5051
5301
  77
5052
5302
  );
5053
5303
  }
5054
- consola30.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}`);
5055
5305
  return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
5056
5306
  }
5057
- var proxyCommand = defineCommand35({
5307
+ var proxyCommand = defineCommand37({
5058
5308
  meta: {
5059
5309
  name: "proxy",
5060
5310
  description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
@@ -5076,12 +5326,12 @@ var proxyCommand = defineCommand35({
5076
5326
  let close = null;
5077
5327
  if (reuseUrl) {
5078
5328
  proxyUrl = reuseUrl;
5079
- consola30.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5329
+ consola32.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5080
5330
  } else {
5081
5331
  const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
5082
5332
  proxyUrl = ephemeral.url;
5083
5333
  close = ephemeral.close;
5084
- consola30.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5334
+ consola32.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5085
5335
  }
5086
5336
  const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
5087
5337
  const childEnv = {
@@ -5113,7 +5363,7 @@ var proxyCommand = defineCommand35({
5113
5363
  else resolveExit(code ?? 0);
5114
5364
  });
5115
5365
  child.once("error", (err) => {
5116
- consola30.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5366
+ consola32.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5117
5367
  resolveExit(127);
5118
5368
  });
5119
5369
  });
@@ -5127,8 +5377,8 @@ function signalNumber(signal) {
5127
5377
  }
5128
5378
 
5129
5379
  // src/commands/explain.ts
5130
- import { defineCommand as defineCommand36 } from "citty";
5131
- var explainCommand = defineCommand36({
5380
+ import { defineCommand as defineCommand38 } from "citty";
5381
+ var explainCommand = defineCommand38({
5132
5382
  meta: {
5133
5383
  name: "explain",
5134
5384
  description: "Show what permission a command would need"
@@ -5166,9 +5416,9 @@ var explainCommand = defineCommand36({
5166
5416
  });
5167
5417
 
5168
5418
  // src/commands/config/get.ts
5169
- import { defineCommand as defineCommand37 } from "citty";
5170
- import consola31 from "consola";
5171
- var configGetCommand = defineCommand37({
5419
+ import { defineCommand as defineCommand39 } from "citty";
5420
+ import consola33 from "consola";
5421
+ var configGetCommand = defineCommand39({
5172
5422
  meta: {
5173
5423
  name: "get",
5174
5424
  description: "Get a configuration value"
@@ -5188,7 +5438,7 @@ var configGetCommand = defineCommand37({
5188
5438
  if (idp)
5189
5439
  console.log(idp);
5190
5440
  else
5191
- consola31.info("No IdP configured.");
5441
+ consola33.info("No IdP configured.");
5192
5442
  break;
5193
5443
  }
5194
5444
  case "email": {
@@ -5196,7 +5446,7 @@ var configGetCommand = defineCommand37({
5196
5446
  if (auth?.email)
5197
5447
  console.log(auth.email);
5198
5448
  else
5199
- consola31.info("Not logged in.");
5449
+ consola33.info("Not logged in.");
5200
5450
  break;
5201
5451
  }
5202
5452
  default: {
@@ -5209,7 +5459,7 @@ var configGetCommand = defineCommand37({
5209
5459
  if (sectionObj && field in sectionObj) {
5210
5460
  console.log(sectionObj[field]);
5211
5461
  } else {
5212
- consola31.info(`Key "${key}" not set.`);
5462
+ consola33.info(`Key "${key}" not set.`);
5213
5463
  }
5214
5464
  } else {
5215
5465
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -5220,9 +5470,9 @@ var configGetCommand = defineCommand37({
5220
5470
  });
5221
5471
 
5222
5472
  // src/commands/config/set.ts
5223
- import { defineCommand as defineCommand38 } from "citty";
5224
- import consola32 from "consola";
5225
- var configSetCommand = defineCommand38({
5473
+ import { defineCommand as defineCommand40 } from "citty";
5474
+ import consola34 from "consola";
5475
+ var configSetCommand = defineCommand40({
5226
5476
  meta: {
5227
5477
  name: "set",
5228
5478
  description: "Set a configuration value"
@@ -5258,12 +5508,12 @@ var configSetCommand = defineCommand38({
5258
5508
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
5259
5509
  }
5260
5510
  saveConfig(config);
5261
- consola32.success(`Set ${key} = ${value}`);
5511
+ consola34.success(`Set ${key} = ${value}`);
5262
5512
  }
5263
5513
  });
5264
5514
 
5265
5515
  // src/commands/fetch/index.ts
5266
- import { defineCommand as defineCommand39 } from "citty";
5516
+ import { defineCommand as defineCommand41 } from "citty";
5267
5517
  async function doRequest(method, url, body, contentType, raw, showHeaders) {
5268
5518
  const token = getAuthToken();
5269
5519
  if (!token) {
@@ -5299,13 +5549,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
5299
5549
  throw new CliError(`HTTP ${response.status} ${response.statusText}`);
5300
5550
  }
5301
5551
  }
5302
- var fetchCommand = defineCommand39({
5552
+ var fetchCommand = defineCommand41({
5303
5553
  meta: {
5304
5554
  name: "fetch",
5305
5555
  description: "Make authenticated HTTP requests"
5306
5556
  },
5307
5557
  subCommands: {
5308
- get: defineCommand39({
5558
+ get: defineCommand41({
5309
5559
  meta: {
5310
5560
  name: "get",
5311
5561
  description: "GET request with auth token"
@@ -5331,7 +5581,7 @@ var fetchCommand = defineCommand39({
5331
5581
  await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
5332
5582
  }
5333
5583
  }),
5334
- post: defineCommand39({
5584
+ post: defineCommand41({
5335
5585
  meta: {
5336
5586
  name: "post",
5337
5587
  description: "POST request with auth token"
@@ -5370,8 +5620,8 @@ var fetchCommand = defineCommand39({
5370
5620
  });
5371
5621
 
5372
5622
  // src/commands/mcp/index.ts
5373
- import { defineCommand as defineCommand40 } from "citty";
5374
- var mcpCommand = defineCommand40({
5623
+ import { defineCommand as defineCommand42 } from "citty";
5624
+ var mcpCommand = defineCommand42({
5375
5625
  meta: {
5376
5626
  name: "mcp",
5377
5627
  description: "Start MCP server for AI agents"
@@ -5394,25 +5644,25 @@ var mcpCommand = defineCommand40({
5394
5644
  if (transport !== "stdio" && transport !== "sse") {
5395
5645
  throw new Error('Transport must be "stdio" or "sse"');
5396
5646
  }
5397
- const { startMcpServer } = await import("./server-A56K7VDD.js");
5647
+ const { startMcpServer } = await import("./server-PHANS7PS.js");
5398
5648
  await startMcpServer(transport, port);
5399
5649
  }
5400
5650
  });
5401
5651
 
5402
5652
  // src/commands/init/index.ts
5403
- import { existsSync as existsSync12, copyFileSync, writeFileSync as writeFileSync8 } from "fs";
5653
+ import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
5404
5654
  import { randomBytes } from "crypto";
5405
5655
  import { execFileSync as execFileSync12 } from "child_process";
5406
- import { join as join11 } from "path";
5407
- import { defineCommand as defineCommand41 } from "citty";
5408
- import consola33 from "consola";
5656
+ import { join as join13 } from "path";
5657
+ import { defineCommand as defineCommand43 } from "citty";
5658
+ import consola35 from "consola";
5409
5659
  var DEFAULT_IDP_URL = "https://id.openape.at";
5410
5660
  async function downloadTemplate(repo, targetDir) {
5411
5661
  const { downloadTemplate: gigetDownload } = await import("giget");
5412
5662
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
5413
5663
  }
5414
5664
  function installDeps(dir) {
5415
- const hasLockFile = (name) => existsSync12(join11(dir, name));
5665
+ const hasLockFile = (name) => existsSync14(join13(dir, name));
5416
5666
  if (hasLockFile("pnpm-lock.yaml")) {
5417
5667
  execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
5418
5668
  } else if (hasLockFile("bun.lockb")) {
@@ -5422,20 +5672,20 @@ function installDeps(dir) {
5422
5672
  }
5423
5673
  }
5424
5674
  async function promptChoice(message, choices) {
5425
- const result = await consola33.prompt(message, { type: "select", options: choices });
5675
+ const result = await consola35.prompt(message, { type: "select", options: choices });
5426
5676
  if (typeof result === "symbol") {
5427
5677
  throw new CliExit(0);
5428
5678
  }
5429
5679
  return result;
5430
5680
  }
5431
5681
  async function promptText(message, defaultValue) {
5432
- const result = await consola33.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
5682
+ const result = await consola35.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
5433
5683
  if (typeof result === "symbol") {
5434
5684
  throw new CliExit(0);
5435
5685
  }
5436
5686
  return result || defaultValue || "";
5437
5687
  }
5438
- var initCommand = defineCommand41({
5688
+ var initCommand = defineCommand43({
5439
5689
  meta: {
5440
5690
  name: "init",
5441
5691
  description: "Scaffold a new OpenApe project"
@@ -5477,23 +5727,23 @@ var initCommand = defineCommand41({
5477
5727
  });
5478
5728
  async function initSP(targetDir) {
5479
5729
  const dir = targetDir || "my-app";
5480
- if (existsSync12(join11(dir, "package.json"))) {
5730
+ if (existsSync14(join13(dir, "package.json"))) {
5481
5731
  throw new CliError(`Directory "${dir}" already contains a project.`);
5482
5732
  }
5483
- consola33.start("Scaffolding SP starter...");
5733
+ consola35.start("Scaffolding SP starter...");
5484
5734
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
5485
- consola33.success("Scaffolded from openape-sp-starter");
5486
- consola33.start("Installing dependencies...");
5735
+ consola35.success("Scaffolded from openape-sp-starter");
5736
+ consola35.start("Installing dependencies...");
5487
5737
  installDeps(dir);
5488
- consola33.success("Dependencies installed");
5489
- const envExample = join11(dir, ".env.example");
5490
- const envFile = join11(dir, ".env");
5491
- 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)) {
5492
5742
  copyFileSync(envExample, envFile);
5493
- consola33.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
5743
+ consola35.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
5494
5744
  }
5495
5745
  console.log("");
5496
- consola33.box([
5746
+ consola35.box([
5497
5747
  `cd ${dir}`,
5498
5748
  "npm run dev",
5499
5749
  "",
@@ -5502,7 +5752,7 @@ async function initSP(targetDir) {
5502
5752
  }
5503
5753
  async function initIdP(targetDir) {
5504
5754
  const dir = targetDir || "my-idp";
5505
- if (existsSync12(join11(dir, "package.json"))) {
5755
+ if (existsSync14(join13(dir, "package.json"))) {
5506
5756
  throw new CliError(`Directory "${dir}" already contains a project.`);
5507
5757
  }
5508
5758
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -5512,15 +5762,15 @@ async function initIdP(targetDir) {
5512
5762
  "s3 (S3-compatible)"
5513
5763
  ]);
5514
5764
  const adminEmail = await promptText("Admin email");
5515
- consola33.start("Scaffolding IdP starter...");
5765
+ consola35.start("Scaffolding IdP starter...");
5516
5766
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
5517
- consola33.success("Scaffolded from openape-idp-starter");
5518
- consola33.start("Installing dependencies...");
5767
+ consola35.success("Scaffolded from openape-idp-starter");
5768
+ consola35.start("Installing dependencies...");
5519
5769
  installDeps(dir);
5520
- consola33.success("Dependencies installed");
5770
+ consola35.success("Dependencies installed");
5521
5771
  const sessionSecret = randomBytes(32).toString("hex");
5522
5772
  const managementToken = randomBytes(32).toString("hex");
5523
- consola33.success("Secrets generated");
5773
+ consola35.success("Secrets generated");
5524
5774
  const isLocalhost = domain === "localhost";
5525
5775
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
5526
5776
  const envContent = [
@@ -5534,11 +5784,11 @@ async function initIdP(targetDir) {
5534
5784
  `NUXT_OPENAPE_RP_ID=${domain}`,
5535
5785
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
5536
5786
  ].join("\n");
5537
- writeFileSync8(join11(dir, ".env"), `${envContent}
5787
+ writeFileSync9(join13(dir, ".env"), `${envContent}
5538
5788
  `, { mode: 384 });
5539
- consola33.success(".env created");
5789
+ consola35.success(".env created");
5540
5790
  console.log("");
5541
- consola33.box([
5791
+ consola35.box([
5542
5792
  `cd ${dir}`,
5543
5793
  "npm run dev",
5544
5794
  "",
@@ -5555,11 +5805,11 @@ async function initIdP(targetDir) {
5555
5805
 
5556
5806
  // src/commands/enroll.ts
5557
5807
  import { Buffer as Buffer5 } from "buffer";
5558
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
5808
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
5559
5809
  import { execFile as execFile2 } from "child_process";
5560
5810
  import { sign as sign2 } from "crypto";
5561
- import { defineCommand as defineCommand42 } from "citty";
5562
- import consola34 from "consola";
5811
+ import { defineCommand as defineCommand44 } from "citty";
5812
+ import consola36 from "consola";
5563
5813
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
5564
5814
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
5565
5815
  var POLL_INTERVAL = 3e3;
@@ -5571,7 +5821,7 @@ function openBrowser2(url) {
5571
5821
  }
5572
5822
  async function pollForEnrollment(idp, agentEmail, keyPath) {
5573
5823
  const resolvedKey = resolveKeyPath(keyPath);
5574
- const keyContent = readFileSync11(resolvedKey, "utf-8");
5824
+ const keyContent = readFileSync12(resolvedKey, "utf-8");
5575
5825
  const privateKey = loadEd25519PrivateKey(keyContent);
5576
5826
  const challengeUrl = await getAgentChallengeEndpoint(idp);
5577
5827
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -5602,7 +5852,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
5602
5852
  }
5603
5853
  throw new Error("Enrollment timed out. Please check the browser and try again.");
5604
5854
  }
5605
- var enrollCommand = defineCommand42({
5855
+ var enrollCommand = defineCommand44({
5606
5856
  meta: {
5607
5857
  name: "enroll",
5608
5858
  description: "Enroll an agent with an Identity Provider"
@@ -5622,38 +5872,38 @@ var enrollCommand = defineCommand42({
5622
5872
  }
5623
5873
  },
5624
5874
  async run({ args }) {
5625
- const idp = args.idp || await consola34.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) => {
5626
5876
  if (typeof r === "symbol") throw new CliExit(0);
5627
5877
  return r;
5628
5878
  }) || DEFAULT_IDP_URL2;
5629
- const agentName = args.name || await consola34.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) => {
5630
5880
  if (typeof r === "symbol") throw new CliExit(0);
5631
5881
  return r;
5632
5882
  });
5633
5883
  if (!agentName) {
5634
5884
  throw new CliError("Agent name is required.");
5635
5885
  }
5636
- const keyPath = args.key || await consola34.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) => {
5637
5887
  if (typeof r === "symbol") throw new CliExit(0);
5638
5888
  return r;
5639
5889
  }) || DEFAULT_KEY_PATH;
5640
5890
  const resolvedKey = resolveKeyPath(keyPath);
5641
5891
  let publicKey;
5642
- if (existsSync13(resolvedKey)) {
5892
+ if (existsSync15(resolvedKey)) {
5643
5893
  publicKey = readPublicKey(resolvedKey);
5644
- consola34.success(`Using existing key ${keyPath}`);
5894
+ consola36.success(`Using existing key ${keyPath}`);
5645
5895
  } else {
5646
- consola34.start(`Generating Ed25519 key pair at ${keyPath}...`);
5896
+ consola36.start(`Generating Ed25519 key pair at ${keyPath}...`);
5647
5897
  publicKey = generateAndSaveKey(keyPath);
5648
- consola34.success(`Key pair generated at ${keyPath}`);
5898
+ consola36.success(`Key pair generated at ${keyPath}`);
5649
5899
  }
5650
5900
  const encodedKey = encodeURIComponent(publicKey);
5651
5901
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
5652
- consola34.info("Opening browser for enrollment...");
5653
- consola34.info(`\u2192 ${idp}/enroll`);
5902
+ consola36.info("Opening browser for enrollment...");
5903
+ consola36.info(`\u2192 ${idp}/enroll`);
5654
5904
  openBrowser2(enrollUrl);
5655
5905
  console.log("");
5656
- const agentEmail = await consola34.prompt(
5906
+ const agentEmail = await consola36.prompt(
5657
5907
  "Agent email (shown in browser after enrollment)",
5658
5908
  { type: "text", placeholder: `agent+${agentName}@...` }
5659
5909
  ).then((r) => {
@@ -5663,7 +5913,7 @@ var enrollCommand = defineCommand42({
5663
5913
  if (!agentEmail) {
5664
5914
  throw new CliError("Agent email is required to verify enrollment.");
5665
5915
  }
5666
- consola34.start("Verifying enrollment...");
5916
+ consola36.start("Verifying enrollment...");
5667
5917
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
5668
5918
  saveAuth({
5669
5919
  idp,
@@ -5675,18 +5925,18 @@ var enrollCommand = defineCommand42({
5675
5925
  config.defaults = { ...config.defaults, idp };
5676
5926
  config.agent = { key: keyPath, email: agentEmail };
5677
5927
  saveConfig(config);
5678
- consola34.success(`Agent enrolled as ${agentEmail}`);
5679
- consola34.success("Config saved to ~/.config/apes/");
5928
+ consola36.success(`Agent enrolled as ${agentEmail}`);
5929
+ consola36.success("Config saved to ~/.config/apes/");
5680
5930
  console.log("");
5681
- consola34.info("Verify with: apes whoami");
5931
+ consola36.info("Verify with: apes whoami");
5682
5932
  }
5683
5933
  });
5684
5934
 
5685
5935
  // src/commands/register-user.ts
5686
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
5687
- import { defineCommand as defineCommand43 } from "citty";
5688
- import consola35 from "consola";
5689
- var registerUserCommand = defineCommand43({
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({
5690
5940
  meta: {
5691
5941
  name: "register-user",
5692
5942
  description: "Register a sub-user with SSH key"
@@ -5722,8 +5972,8 @@ var registerUserCommand = defineCommand43({
5722
5972
  throw new CliError("No IdP URL configured. Run `apes login` first.");
5723
5973
  }
5724
5974
  let publicKey = args.key;
5725
- if (existsSync14(args.key)) {
5726
- publicKey = readFileSync12(args.key, "utf-8").trim();
5975
+ if (existsSync16(args.key)) {
5976
+ publicKey = readFileSync13(args.key, "utf-8").trim();
5727
5977
  }
5728
5978
  if (!publicKey.startsWith("ssh-ed25519 ")) {
5729
5979
  throw new CliError("Public key must be in ssh-ed25519 format.");
@@ -5741,18 +5991,18 @@ var registerUserCommand = defineCommand43({
5741
5991
  ...userType ? { type: userType } : {}
5742
5992
  }
5743
5993
  });
5744
- consola35.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
5994
+ consola37.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
5745
5995
  }
5746
5996
  });
5747
5997
 
5748
5998
  // src/commands/utils/index.ts
5749
- import { defineCommand as defineCommand45 } from "citty";
5999
+ import { defineCommand as defineCommand47 } from "citty";
5750
6000
 
5751
6001
  // src/commands/utils/dig.ts
5752
- import { defineCommand as defineCommand44 } from "citty";
5753
- import consola36 from "consola";
6002
+ import { defineCommand as defineCommand46 } from "citty";
6003
+ import consola38 from "consola";
5754
6004
  import { resolveDDISA as resolveDDISA2 } from "@openape/core";
5755
- var digCommand = defineCommand44({
6005
+ var digCommand = defineCommand46({
5756
6006
  meta: {
5757
6007
  name: "dig",
5758
6008
  description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
@@ -5825,12 +6075,12 @@ var digCommand = defineCommand44({
5825
6075
  console.log(` domain: ${domain}`);
5826
6076
  console.log("");
5827
6077
  if (!result.ddisa.found) {
5828
- consola36.warn(`No DDISA record at _ddisa.${domain}`);
6078
+ consola38.warn(`No DDISA record at _ddisa.${domain}`);
5829
6079
  if (result.hint) console.log(`
5830
6080
  ${result.hint}`);
5831
6081
  throw new CliError(`No DDISA record found for ${domain}`);
5832
6082
  }
5833
- consola36.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
6083
+ consola38.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
5834
6084
  console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
5835
6085
  console.log(` IdP URL: ${result.ddisa.idp}`);
5836
6086
  if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
@@ -5840,13 +6090,13 @@ ${result.hint}`);
5840
6090
  return;
5841
6091
  }
5842
6092
  if (result.idpDiscovery.ok) {
5843
- consola36.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
6093
+ consola38.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
5844
6094
  if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
5845
6095
  if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
5846
6096
  if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
5847
6097
  if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
5848
6098
  } else {
5849
- consola36.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
6099
+ consola38.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
5850
6100
  if (result.hint) console.log(`
5851
6101
  ${result.hint}`);
5852
6102
  throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
@@ -5855,7 +6105,7 @@ ${result.hint}`);
5855
6105
  });
5856
6106
 
5857
6107
  // src/commands/utils/index.ts
5858
- var utilsCommand = defineCommand45({
6108
+ var utilsCommand = defineCommand47({
5859
6109
  meta: {
5860
6110
  name: "utils",
5861
6111
  description: "Admin/diagnostic utilities (dig, \u2026)"
@@ -5866,12 +6116,12 @@ var utilsCommand = defineCommand45({
5866
6116
  });
5867
6117
 
5868
6118
  // src/commands/sessions/index.ts
5869
- import { defineCommand as defineCommand48 } from "citty";
6119
+ import { defineCommand as defineCommand50 } from "citty";
5870
6120
 
5871
6121
  // src/commands/sessions/list.ts
5872
- import { defineCommand as defineCommand46 } from "citty";
5873
- import consola37 from "consola";
5874
- var sessionsListCommand = defineCommand46({
6122
+ import { defineCommand as defineCommand48 } from "citty";
6123
+ import consola39 from "consola";
6124
+ var sessionsListCommand = defineCommand48({
5875
6125
  meta: {
5876
6126
  name: "list",
5877
6127
  description: "List your active refresh-token families (one per logged-in device)."
@@ -5889,7 +6139,7 @@ var sessionsListCommand = defineCommand46({
5889
6139
  return;
5890
6140
  }
5891
6141
  if (result.data.length === 0) {
5892
- consola37.info("No active sessions.");
6142
+ consola39.info("No active sessions.");
5893
6143
  return;
5894
6144
  }
5895
6145
  for (const f of result.data) {
@@ -5901,9 +6151,9 @@ var sessionsListCommand = defineCommand46({
5901
6151
  });
5902
6152
 
5903
6153
  // src/commands/sessions/remove.ts
5904
- import { defineCommand as defineCommand47 } from "citty";
5905
- import consola38 from "consola";
5906
- var sessionsRemoveCommand = defineCommand47({
6154
+ import { defineCommand as defineCommand49 } from "citty";
6155
+ import consola40 from "consola";
6156
+ var sessionsRemoveCommand = defineCommand49({
5907
6157
  meta: {
5908
6158
  name: "remove",
5909
6159
  description: "Revoke one of your active refresh-token families by id."
@@ -5919,12 +6169,12 @@ var sessionsRemoveCommand = defineCommand47({
5919
6169
  const id = String(args.familyId).trim();
5920
6170
  if (!id) throw new CliError("familyId required");
5921
6171
  await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
5922
- consola38.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.`);
5923
6173
  }
5924
6174
  });
5925
6175
 
5926
6176
  // src/commands/sessions/index.ts
5927
- var sessionsCommand = defineCommand48({
6177
+ var sessionsCommand = defineCommand50({
5928
6178
  meta: {
5929
6179
  name: "sessions",
5930
6180
  description: "Manage your active refresh-token sessions across devices"
@@ -5936,10 +6186,10 @@ var sessionsCommand = defineCommand48({
5936
6186
  });
5937
6187
 
5938
6188
  // src/commands/dns-check.ts
5939
- import { defineCommand as defineCommand49 } from "citty";
5940
- import consola39 from "consola";
6189
+ import { defineCommand as defineCommand51 } from "citty";
6190
+ import consola41 from "consola";
5941
6191
  import { resolveDDISA as resolveDDISA3 } from "@openape/core";
5942
- var dnsCheckCommand = defineCommand49({
6192
+ var dnsCheckCommand = defineCommand51({
5943
6193
  meta: {
5944
6194
  name: "dns-check",
5945
6195
  description: "Validate DDISA DNS TXT records for a domain"
@@ -5953,7 +6203,7 @@ var dnsCheckCommand = defineCommand49({
5953
6203
  },
5954
6204
  async run({ args }) {
5955
6205
  const domain = args.domain;
5956
- consola39.start(`Checking _ddisa.${domain}...`);
6206
+ consola41.start(`Checking _ddisa.${domain}...`);
5957
6207
  try {
5958
6208
  const result = await resolveDDISA3(domain);
5959
6209
  if (!result) {
@@ -5962,7 +6212,7 @@ var dnsCheckCommand = defineCommand49({
5962
6212
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
5963
6213
  throw new CliError(`No DDISA record found for ${domain}`);
5964
6214
  }
5965
- consola39.success(`_ddisa.${domain} \u2192 ${result.idp}`);
6215
+ consola41.success(`_ddisa.${domain} \u2192 ${result.idp}`);
5966
6216
  console.log("");
5967
6217
  console.log(` Version: ${result.version || "ddisa1"}`);
5968
6218
  console.log(` IdP URL: ${result.idp}`);
@@ -5971,14 +6221,14 @@ var dnsCheckCommand = defineCommand49({
5971
6221
  if (result.priority !== void 0)
5972
6222
  console.log(` Priority: ${result.priority}`);
5973
6223
  console.log("");
5974
- consola39.start(`Verifying IdP at ${result.idp}...`);
6224
+ consola41.start(`Verifying IdP at ${result.idp}...`);
5975
6225
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
5976
6226
  if (!discoResp.ok) {
5977
- consola39.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}?`);
5978
6228
  return;
5979
6229
  }
5980
6230
  const disco = await discoResp.json();
5981
- consola39.success(`IdP is reachable`);
6231
+ consola41.success(`IdP is reachable`);
5982
6232
  console.log(` Issuer: ${disco.issuer}`);
5983
6233
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
5984
6234
  if (disco.ddisa_auth_methods_supported) {
@@ -5996,7 +6246,7 @@ var dnsCheckCommand = defineCommand49({
5996
6246
  // src/commands/health.ts
5997
6247
  import { exec } from "child_process";
5998
6248
  import { promisify } from "util";
5999
- import { defineCommand as defineCommand50 } from "citty";
6249
+ import { defineCommand as defineCommand52 } from "citty";
6000
6250
  var execAsync = promisify(exec);
6001
6251
  async function resolveApeShellPath() {
6002
6252
  try {
@@ -6032,7 +6282,7 @@ async function bestEffortGrantCount(idp) {
6032
6282
  }
6033
6283
  }
6034
6284
  async function runHealth(args) {
6035
- const version = true ? "1.4.0" : "0.0.0";
6285
+ const version = true ? "1.6.0" : "0.0.0";
6036
6286
  const auth = loadAuth();
6037
6287
  if (!auth) {
6038
6288
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -6095,7 +6345,7 @@ async function runHealth(args) {
6095
6345
  throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
6096
6346
  }
6097
6347
  }
6098
- var healthCommand = defineCommand50({
6348
+ var healthCommand = defineCommand52({
6099
6349
  meta: {
6100
6350
  name: "health",
6101
6351
  description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
@@ -6113,8 +6363,8 @@ var healthCommand = defineCommand50({
6113
6363
  });
6114
6364
 
6115
6365
  // src/commands/workflows.ts
6116
- import { defineCommand as defineCommand51 } from "citty";
6117
- import consola40 from "consola";
6366
+ import { defineCommand as defineCommand53 } from "citty";
6367
+ import consola42 from "consola";
6118
6368
 
6119
6369
  // src/guides/index.ts
6120
6370
  var guides = [
@@ -6164,7 +6414,7 @@ var guides = [
6164
6414
  ];
6165
6415
 
6166
6416
  // src/commands/workflows.ts
6167
- var workflowsCommand = defineCommand51({
6417
+ var workflowsCommand = defineCommand53({
6168
6418
  meta: {
6169
6419
  name: "workflows",
6170
6420
  description: "Discover workflow guides"
@@ -6185,7 +6435,7 @@ var workflowsCommand = defineCommand51({
6185
6435
  if (args.id) {
6186
6436
  const guide = guides.find((g) => g.id === String(args.id));
6187
6437
  if (!guide) {
6188
- consola40.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
6438
+ consola42.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
6189
6439
  throw new CliError(`Guide not found: ${args.id}`);
6190
6440
  }
6191
6441
  if (args.json) {
@@ -6225,26 +6475,26 @@ var workflowsCommand = defineCommand51({
6225
6475
  });
6226
6476
 
6227
6477
  // src/version-check.ts
6228
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
6229
- import { homedir as homedir12 } from "os";
6230
- import { join as join12 } from "path";
6231
- import consola41 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";
6232
6482
  var PACKAGE_NAME = "@openape/apes";
6233
6483
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
6234
- var CACHE_FILE = join12(homedir12(), ".config", "apes", ".version-check.json");
6484
+ var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
6235
6485
  function readCache() {
6236
- if (!existsSync15(CACHE_FILE)) return null;
6486
+ if (!existsSync17(CACHE_FILE)) return null;
6237
6487
  try {
6238
- return JSON.parse(readFileSync13(CACHE_FILE, "utf-8"));
6488
+ return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
6239
6489
  } catch {
6240
6490
  return null;
6241
6491
  }
6242
6492
  }
6243
6493
  function writeCache(entry) {
6244
6494
  try {
6245
- const dir = join12(homedir12(), ".config", "apes");
6246
- if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
6247
- 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 });
6248
6498
  } catch {
6249
6499
  }
6250
6500
  }
@@ -6273,7 +6523,7 @@ async function fetchLatestVersion() {
6273
6523
  }
6274
6524
  function warnIfBehind(currentVersion, latest) {
6275
6525
  if (compareSemver(currentVersion, latest) < 0) {
6276
- consola41.warn(
6526
+ consola43.warn(
6277
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.)`
6278
6528
  );
6279
6529
  }
@@ -6305,10 +6555,10 @@ if (shellRewrite) {
6305
6555
  if (shellRewrite.action === "rewrite") {
6306
6556
  process.argv = shellRewrite.argv;
6307
6557
  } else if (shellRewrite.action === "version") {
6308
- console.log(`ape-shell ${"1.4.0"} (OpenApe DDISA shell wrapper)`);
6558
+ console.log(`ape-shell ${"1.6.0"} (OpenApe DDISA shell wrapper)`);
6309
6559
  process.exit(0);
6310
6560
  } else if (shellRewrite.action === "help") {
6311
- console.log(`ape-shell ${"1.4.0"} \u2014 OpenApe DDISA shell wrapper`);
6561
+ console.log(`ape-shell ${"1.6.0"} \u2014 OpenApe DDISA shell wrapper`);
6312
6562
  console.log("");
6313
6563
  console.log("Usage:");
6314
6564
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -6332,7 +6582,7 @@ if (shellRewrite) {
6332
6582
  }
6333
6583
  }
6334
6584
  var debug = process.argv.includes("--debug");
6335
- var grantsCommand = defineCommand52({
6585
+ var grantsCommand = defineCommand54({
6336
6586
  meta: {
6337
6587
  name: "grants",
6338
6588
  description: "Grant management"
@@ -6353,7 +6603,7 @@ var grantsCommand = defineCommand52({
6353
6603
  "delegation-revoke": delegationRevokeCommand
6354
6604
  }
6355
6605
  });
6356
- var configCommand = defineCommand52({
6606
+ var configCommand = defineCommand54({
6357
6607
  meta: {
6358
6608
  name: "config",
6359
6609
  description: "Configuration management"
@@ -6363,10 +6613,10 @@ var configCommand = defineCommand52({
6363
6613
  set: configSetCommand
6364
6614
  }
6365
6615
  });
6366
- var main = defineCommand52({
6616
+ var main = defineCommand54({
6367
6617
  meta: {
6368
6618
  name: "apes",
6369
- version: "1.4.0",
6619
+ version: "1.6.0",
6370
6620
  description: "Unified CLI for OpenApe"
6371
6621
  },
6372
6622
  subCommands: {
@@ -6422,20 +6672,20 @@ async function maybeRefreshAuth() {
6422
6672
  }
6423
6673
  }
6424
6674
  await maybeRefreshAuth();
6425
- await maybeWarnStaleVersion("1.4.0").catch(() => {
6675
+ await maybeWarnStaleVersion("1.6.0").catch(() => {
6426
6676
  });
6427
6677
  runMain(main).catch((err) => {
6428
6678
  if (err instanceof CliExit) {
6429
6679
  process.exit(err.exitCode);
6430
6680
  }
6431
6681
  if (err instanceof CliError) {
6432
- consola42.error(err.message);
6682
+ consola44.error(err.message);
6433
6683
  process.exit(err.exitCode);
6434
6684
  }
6435
6685
  if (debug) {
6436
- consola42.error(err);
6686
+ consola44.error(err);
6437
6687
  } else {
6438
- consola42.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));
6439
6689
  }
6440
6690
  process.exit(1);
6441
6691
  });