@openape/apes 0.6.1 → 0.7.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
@@ -5,13 +5,13 @@ import {
5
5
  parseDuration
6
6
  } from "./chunk-ZSJU7IXE.js";
7
7
  import {
8
- loadEd25519PrivateKey
9
- } from "./chunk-KVBHBOED.js";
8
+ loadEd25519PrivateKey,
9
+ readPublicKeyComment
10
+ } from "./chunk-ION3CWD5.js";
10
11
  import {
11
12
  ApiError,
12
13
  apiFetch,
13
14
  buildStructuredCliGrantRequest,
14
- clearAuth,
15
15
  createShapesGrant,
16
16
  extractOption,
17
17
  extractShellCommandString,
@@ -23,45 +23,58 @@ import {
23
23
  findExistingGrant,
24
24
  getAgentAuthenticateEndpoint,
25
25
  getAgentChallengeEndpoint,
26
- getAuthToken,
27
26
  getDelegationsEndpoint,
28
27
  getGrantsEndpoint,
29
- getIdpUrl,
30
28
  getInstalledDigest,
31
29
  installAdapter,
32
30
  isInstalled,
33
31
  loadAdapter,
34
- loadAuth,
35
- loadConfig,
36
32
  loadOrInstallAdapter,
37
33
  parseShellCommand,
38
34
  removeAdapter,
39
35
  resolveCapabilityRequest,
40
36
  resolveCommand,
41
- saveAuth,
42
- saveConfig,
43
37
  searchAdapters,
44
38
  verifyAndExecute,
45
39
  waitForGrantStatus
46
- } from "./chunk-G3Q2TMAI.js";
40
+ } from "./chunk-B32ZQP5K.js";
41
+ import {
42
+ clearAuth,
43
+ getAuthToken,
44
+ getIdpUrl,
45
+ loadAuth,
46
+ loadConfig,
47
+ saveAuth,
48
+ saveConfig
49
+ } from "./chunk-TBYYREL6.js";
47
50
 
48
51
  // src/cli.ts
49
- import consola25 from "consola";
52
+ import consola26 from "consola";
50
53
 
51
54
  // src/ape-shell.ts
52
55
  import path from "path";
53
- function rewriteApeShellArgs(argv) {
54
- const invokedAs = path.basename(argv[1] ?? "");
55
- if (invokedAs !== "ape-shell" && invokedAs !== "ape-shell.js")
56
+ function rewriteApeShellArgs(argv, argv0) {
57
+ const rawInvokedAs = argv[1] ?? "";
58
+ const dashFromArgv1 = rawInvokedAs.startsWith("-");
59
+ const dashFromArgv0 = typeof argv0 === "string" && argv0.startsWith("-");
60
+ const looksLikeLoginShell = dashFromArgv1 || dashFromArgv0;
61
+ const normalizedInvokedAs = dashFromArgv1 ? rawInvokedAs.slice(1) : rawInvokedAs;
62
+ const invokedAs = path.basename(normalizedInvokedAs);
63
+ const wrapperEnv = typeof process !== "undefined" && process.env?.APES_SHELL_WRAPPER === "1";
64
+ const argvMatch = invokedAs === "ape-shell" || invokedAs === "ape-shell.js";
65
+ if (!wrapperEnv && !argvMatch)
56
66
  return null;
57
67
  const shellArgs = argv.slice(2);
58
68
  if (shellArgs[0] === "-c" && shellArgs.length > 1) {
59
69
  return { action: "rewrite", argv: [argv[0], argv[1], "run", "--shell", "--", "bash", "-c", ...shellArgs.slice(1)] };
60
70
  }
61
- if (shellArgs[0] === "--version")
71
+ if (shellArgs[0] === "--version" || shellArgs[0] === "-v")
62
72
  return { action: "version" };
63
73
  if (shellArgs[0] === "--help" || shellArgs[0] === "-h")
64
74
  return { action: "help" };
75
+ if (shellArgs.length === 0 || shellArgs[0] === "-i" || shellArgs[0] === "-l" || shellArgs[0] === "--login" || looksLikeLoginShell) {
76
+ return { action: "interactive" };
77
+ }
65
78
  return { action: "error" };
66
79
  }
67
80
 
@@ -72,9 +85,74 @@ import { defineCommand as defineCommand31, runMain } from "citty";
72
85
  import { Buffer } from "buffer";
73
86
  import { execFile } from "child_process";
74
87
  import { createServer } from "http";
88
+ import { homedir as homedir2 } from "os";
89
+ import { resolve as resolvePath } from "path";
75
90
  import { defineCommand } from "citty";
76
91
  import { generateCodeChallenge, generateCodeVerifier } from "@openape/core";
92
+ import consola2 from "consola";
93
+
94
+ // src/commands/auth/resolve-login.ts
95
+ import { existsSync } from "fs";
96
+ import { homedir } from "os";
97
+ import { join } from "path";
98
+ import { resolveDDISA } from "@openape/core";
77
99
  import consola from "consola";
100
+ var DEFAULT_KEY = join(homedir(), ".ssh", "id_ed25519");
101
+ async function resolveLoginInputs(flags) {
102
+ const config = loadConfig();
103
+ let keyPath;
104
+ if (!flags.browser) {
105
+ if (flags.key) {
106
+ keyPath = flags.key;
107
+ } else if (process.env.APES_KEY) {
108
+ keyPath = process.env.APES_KEY;
109
+ consola.info(`Using key from APES_KEY: ${keyPath}`);
110
+ } else if (config.agent?.key) {
111
+ keyPath = config.agent.key;
112
+ consola.info(`Using key from config: ${keyPath}`);
113
+ } else if (existsSync(DEFAULT_KEY)) {
114
+ keyPath = DEFAULT_KEY;
115
+ consola.info(`Using default key: ${keyPath}`);
116
+ }
117
+ }
118
+ let email;
119
+ if (flags.email) {
120
+ email = flags.email;
121
+ } else if (process.env.APES_EMAIL) {
122
+ email = process.env.APES_EMAIL;
123
+ } else if (config.agent?.email) {
124
+ email = config.agent.email;
125
+ } else if (keyPath) {
126
+ const comment = readPublicKeyComment(`${keyPath}.pub`);
127
+ if (comment && comment.includes("@")) {
128
+ email = comment;
129
+ consola.info(`Using email from ${keyPath}.pub comment: ${email}`);
130
+ }
131
+ }
132
+ let idp;
133
+ if (flags.idp) {
134
+ idp = flags.idp;
135
+ } else if (process.env.APES_IDP) {
136
+ idp = process.env.APES_IDP;
137
+ } else if (process.env.GRAPES_IDP) {
138
+ idp = process.env.GRAPES_IDP;
139
+ } else if (config.defaults?.idp) {
140
+ idp = config.defaults.idp;
141
+ } else if (email && email.includes("@")) {
142
+ const domain = email.split("@")[1];
143
+ try {
144
+ const record = await resolveDDISA(domain);
145
+ if (record?.idp) {
146
+ idp = record.idp;
147
+ consola.info(`Discovered IdP via DDISA (_ddisa.${domain}): ${idp}`);
148
+ }
149
+ } catch {
150
+ }
151
+ }
152
+ return { keyPath, email, idp };
153
+ }
154
+
155
+ // src/commands/auth/login.ts
78
156
  var CALLBACK_PORT = 9876;
79
157
  var CLIENT_ID = "grapes-cli";
80
158
  var loginCommand = defineCommand({
@@ -85,27 +163,56 @@ var loginCommand = defineCommand({
85
163
  args: {
86
164
  idp: {
87
165
  type: "string",
88
- description: "IdP URL (e.g. https://id.openape.at)"
166
+ description: "IdP URL (e.g. https://id.openape.at). Auto-discovered via DDISA DNS if omitted."
89
167
  },
90
168
  key: {
91
169
  type: "string",
92
- description: "Path to agent private key (agent mode)"
170
+ description: "Path to agent private key. Defaults to ~/.ssh/id_ed25519 if present."
93
171
  },
94
172
  email: {
95
173
  type: "string",
96
- description: "Agent email (for DNS discovery)"
174
+ description: "Agent email. Extracted from <key>.pub comment if omitted."
175
+ },
176
+ browser: {
177
+ type: "boolean",
178
+ description: "Force browser (PKCE) login even if an SSH key exists"
97
179
  }
98
180
  },
99
181
  async run({ args }) {
100
- const config = loadConfig();
101
- const idp = args.idp || process.env.APES_IDP || process.env.GRAPES_IDP || config.defaults?.idp;
102
- if (!idp) {
103
- throw new CliError("IdP URL required. Use --idp <url> or set APES_IDP.");
104
- }
105
- if (args.key) {
106
- await loginWithKey(idp, args.key, args.email);
182
+ const resolved = await resolveLoginInputs({
183
+ key: args.key,
184
+ idp: args.idp,
185
+ email: args.email,
186
+ browser: args.browser
187
+ });
188
+ if (resolved.keyPath) {
189
+ if (!resolved.email) {
190
+ throw new CliError(
191
+ `Agent email required for key-based login. Add an email comment to ${resolved.keyPath}.pub (ssh-keygen -C <email>) or pass --email <agent-email>.`
192
+ );
193
+ }
194
+ if (!resolved.idp) {
195
+ const domain = resolved.email.split("@")[1];
196
+ throw new CliError(
197
+ `No IdP found for ${resolved.email}.
198
+
199
+ There is no DDISA TXT record for ${domain} and no --idp was provided.
200
+
201
+ Options:
202
+ \u2022 Run your own IdP (recommended for production)
203
+ See: https://docs.openape.at
204
+ \u2022 Publish a DDISA TXT record for your domain:
205
+ _ddisa.${domain} TXT "v=ddisa1 idp=https://your-idp.example"
206
+ \u2022 Use OpenApe's free hosted IdP for testing:
207
+ apes login --idp https://id.openape.at`
208
+ );
209
+ }
210
+ await loginWithKey(resolved.idp, resolved.keyPath, resolved.email);
107
211
  } else {
108
- await loginWithPKCE(idp);
212
+ if (!resolved.idp) {
213
+ throw new CliError("IdP URL required for browser login. Use --idp <url> or set APES_IDP.");
214
+ }
215
+ await loginWithPKCE(resolved.idp);
109
216
  }
110
217
  }
111
218
  });
@@ -157,7 +264,12 @@ async function loginWithPKCE(idp) {
157
264
  }
158
265
  });
159
266
  server.listen(CALLBACK_PORT, () => {
160
- consola.info(`Opening browser for login at ${idp}...`);
267
+ console.log("");
268
+ console.log("Visit the following URL in a browser to authenticate:");
269
+ console.log("");
270
+ console.log(` ${authUrl.toString()}`);
271
+ console.log("");
272
+ consola2.info(`Waiting for authentication callback on ${redirectUri} ...`);
161
273
  openBrowser(authUrl.toString());
162
274
  });
163
275
  const timeout = setTimeout(() => {
@@ -194,16 +306,12 @@ async function loginWithPKCE(idp) {
194
306
  email: payload.email || payload.sub,
195
307
  expires_at: Math.floor(Date.now() / 1e3) + (tokens.expires_in || 3600)
196
308
  });
197
- consola.success(`Logged in as ${payload.email || payload.sub}`);
309
+ consola2.success(`Logged in as ${payload.email || payload.sub}`);
198
310
  }
199
- async function loginWithKey(idp, keyPath, email) {
311
+ async function loginWithKey(idp, keyPath, agentEmail) {
200
312
  const { readFileSync: readFileSync4 } = await import("fs");
201
313
  const { sign: sign2 } = await import("crypto");
202
- const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-Q7KG4K25.js");
203
- const agentEmail = email;
204
- if (!agentEmail) {
205
- throw new CliError("Agent email required for key-based login. Use --email <agent-email>");
206
- }
314
+ const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
207
315
  const challengeUrl = await getAgentChallengeEndpoint(idp);
208
316
  const challengeResp = await fetch(challengeUrl, {
209
317
  method: "POST",
@@ -237,12 +345,23 @@ async function loginWithKey(idp, keyPath, email) {
237
345
  email: agentEmail,
238
346
  expires_at: Math.floor(Date.now() / 1e3) + (expires_in || 3600)
239
347
  });
240
- consola.success(`Logged in as ${agentEmail}`);
348
+ const absoluteKeyPath = resolvePath(keyPath.replace(/^~/, homedir2()));
349
+ const existingConfig = loadConfig();
350
+ saveConfig({
351
+ ...existingConfig,
352
+ agent: {
353
+ ...existingConfig.agent,
354
+ key: absoluteKeyPath,
355
+ email: agentEmail
356
+ }
357
+ });
358
+ consola2.success(`Logged in as ${agentEmail}`);
359
+ consola2.info(`Auto-refresh enabled (key path saved to ~/.config/apes/config.toml)`);
241
360
  }
242
361
 
243
362
  // src/commands/auth/logout.ts
244
363
  import { defineCommand as defineCommand2 } from "citty";
245
- import consola2 from "consola";
364
+ import consola3 from "consola";
246
365
  var logoutCommand = defineCommand2({
247
366
  meta: {
248
367
  name: "logout",
@@ -250,13 +369,13 @@ var logoutCommand = defineCommand2({
250
369
  },
251
370
  run() {
252
371
  clearAuth();
253
- consola2.success("Logged out.");
372
+ consola3.success("Logged out.");
254
373
  }
255
374
  });
256
375
 
257
376
  // src/commands/auth/whoami.ts
258
377
  import { defineCommand as defineCommand3 } from "citty";
259
- import consola3 from "consola";
378
+ import consola4 from "consola";
260
379
  var whoamiCommand = defineCommand3({
261
380
  meta: {
262
381
  name: "whoami",
@@ -275,14 +394,14 @@ var whoamiCommand = defineCommand3({
275
394
  console.log(`IdP: ${auth.idp}`);
276
395
  console.log(`Token: ${isExpired ? "\u26A0 EXPIRED" : "valid"} (until ${expiresAt})`);
277
396
  if (isExpired) {
278
- consola3.warn("Token is expired. Run `apes login` to re-authenticate.");
397
+ consola4.warn("Token is expired. Run `apes login` to re-authenticate.");
279
398
  }
280
399
  }
281
400
  });
282
401
 
283
402
  // src/commands/grants/list.ts
284
403
  import { defineCommand as defineCommand4 } from "citty";
285
- import consola4 from "consola";
404
+ import consola5 from "consola";
286
405
  var listCommand = defineCommand4({
287
406
  meta: {
288
407
  name: "list",
@@ -331,7 +450,7 @@ var listCommand = defineCommand4({
331
450
  return;
332
451
  }
333
452
  if (grants.length === 0) {
334
- consola4.info(args.all ? "No grants found." : "No grants found. Use --all to see all visible grants.");
453
+ consola5.info(args.all ? "No grants found." : "No grants found. Use --all to see all visible grants.");
335
454
  return;
336
455
  }
337
456
  for (const grant of grants) {
@@ -343,14 +462,14 @@ var listCommand = defineCommand4({
343
462
  }
344
463
  }
345
464
  if (response.pagination.has_more) {
346
- consola4.info("More results available. Use --limit or pagination cursor.");
465
+ consola5.info("More results available. Use --limit or pagination cursor.");
347
466
  }
348
467
  }
349
468
  });
350
469
 
351
470
  // src/commands/grants/inbox.ts
352
471
  import { defineCommand as defineCommand5 } from "citty";
353
- import consola5 from "consola";
472
+ import consola6 from "consola";
354
473
  var inboxCommand = defineCommand5({
355
474
  meta: {
356
475
  name: "inbox",
@@ -389,10 +508,10 @@ var inboxCommand = defineCommand5({
389
508
  return;
390
509
  }
391
510
  if (grants.length === 0) {
392
- consola5.info("No pending grants to approve.");
511
+ consola6.info("No pending grants to approve.");
393
512
  return;
394
513
  }
395
- consola5.info(`${grants.length} grant(s) awaiting approval:
514
+ consola6.info(`${grants.length} grant(s) awaiting approval:
396
515
  `);
397
516
  for (const grant of grants) {
398
517
  const cmd = grant.request?.command?.join(" ") || "(no command)";
@@ -407,7 +526,7 @@ var inboxCommand = defineCommand5({
407
526
  }
408
527
  console.log();
409
528
  }
410
- consola5.info("Use `apes grants approve <id>` or `apes grants deny <id>` to respond.");
529
+ consola6.info("Use `apes grants approve <id>` or `apes grants deny <id>` to respond.");
411
530
  }
412
531
  });
413
532
 
@@ -463,7 +582,7 @@ var statusCommand = defineCommand6({
463
582
  // src/commands/grants/request.ts
464
583
  import { hostname } from "os";
465
584
  import { defineCommand as defineCommand7 } from "citty";
466
- import consola6 from "consola";
585
+ import consola7 from "consola";
467
586
  var requestCommand = defineCommand7({
468
587
  meta: {
469
588
  name: "request",
@@ -530,9 +649,9 @@ var requestCommand = defineCommand7({
530
649
  ...args["run-as"] ? { run_as: args["run-as"] } : {}
531
650
  }
532
651
  });
533
- consola6.success(`Grant requested: ${grant.id} (status: ${grant.status})`);
652
+ consola7.success(`Grant requested: ${grant.id} (status: ${grant.status})`);
534
653
  if (args.wait) {
535
- consola6.info("Waiting for approval...");
654
+ consola7.info("Waiting for approval...");
536
655
  await waitForApproval(grantsUrl, grant.id);
537
656
  }
538
657
  }
@@ -544,7 +663,7 @@ async function waitForApproval(grantsUrl, grantId) {
544
663
  while (Date.now() - start < maxWait) {
545
664
  const grant = await apiFetch(`${grantsUrl}/${grantId}`);
546
665
  if (grant.status === "approved") {
547
- consola6.success("Grant approved!");
666
+ consola7.success("Grant approved!");
548
667
  return;
549
668
  }
550
669
  if (grant.status === "denied") {
@@ -561,7 +680,7 @@ async function waitForApproval(grantsUrl, grantId) {
561
680
  // src/commands/grants/request-capability.ts
562
681
  import { hostname as hostname2 } from "os";
563
682
  import { defineCommand as defineCommand8 } from "citty";
564
- import consola7 from "consola";
683
+ import consola8 from "consola";
565
684
  function parseCapabilityArgs(rawArgs) {
566
685
  const tokens = [...rawArgs];
567
686
  if (tokens[0] === "request-capability") {
@@ -668,7 +787,7 @@ async function waitForApproval2(grantsUrl, grantId) {
668
787
  while (Date.now() - start < maxWait) {
669
788
  const grant = await apiFetch(`${grantsUrl}/${grantId}`);
670
789
  if (grant.status === "approved") {
671
- consola7.success("Grant approved!");
790
+ consola8.success("Grant approved!");
672
791
  return;
673
792
  }
674
793
  if (grant.status === "denied") {
@@ -769,9 +888,9 @@ var requestCapabilityCommand = defineCommand8({
769
888
  idp,
770
889
  body: request
771
890
  });
772
- consola7.success(`Grant requested: ${grant.id} (status: ${grant.status})`);
891
+ consola8.success(`Grant requested: ${grant.id} (status: ${grant.status})`);
773
892
  if (parsed.wait) {
774
- consola7.info("Waiting for approval...");
893
+ consola8.info("Waiting for approval...");
775
894
  await waitForApproval2(grantsUrl, grant.id);
776
895
  }
777
896
  }
@@ -779,7 +898,7 @@ var requestCapabilityCommand = defineCommand8({
779
898
 
780
899
  // src/commands/grants/approve.ts
781
900
  import { defineCommand as defineCommand9 } from "citty";
782
- import consola8 from "consola";
901
+ import consola9 from "consola";
783
902
  var approveCommand = defineCommand9({
784
903
  meta: {
785
904
  name: "approve",
@@ -798,13 +917,13 @@ var approveCommand = defineCommand9({
798
917
  await apiFetch(`${grantsUrl}/${args.id}/approve`, {
799
918
  method: "POST"
800
919
  });
801
- consola8.success(`Grant ${args.id} approved.`);
920
+ consola9.success(`Grant ${args.id} approved.`);
802
921
  }
803
922
  });
804
923
 
805
924
  // src/commands/grants/deny.ts
806
925
  import { defineCommand as defineCommand10 } from "citty";
807
- import consola9 from "consola";
926
+ import consola10 from "consola";
808
927
  var denyCommand = defineCommand10({
809
928
  meta: {
810
929
  name: "deny",
@@ -823,13 +942,13 @@ var denyCommand = defineCommand10({
823
942
  await apiFetch(`${grantsUrl}/${args.id}/deny`, {
824
943
  method: "POST"
825
944
  });
826
- consola9.success(`Grant ${args.id} denied.`);
945
+ consola10.success(`Grant ${args.id} denied.`);
827
946
  }
828
947
  });
829
948
 
830
949
  // src/commands/grants/revoke.ts
831
950
  import { defineCommand as defineCommand11 } from "citty";
832
- import consola10 from "consola";
951
+ import consola11 from "consola";
833
952
  var revokeCommand = defineCommand11({
834
953
  meta: {
835
954
  name: "revoke",
@@ -858,11 +977,11 @@ var revokeCommand = defineCommand11({
858
977
  const idp = getIdpUrl();
859
978
  const grantsUrl = await getGrantsEndpoint(idp);
860
979
  if (args.debug) {
861
- consola10.debug(`idp: ${idp}`);
862
- consola10.debug(`grantsUrl: ${grantsUrl}`);
863
- consola10.debug(`auth.email: ${auth?.email}`);
864
- consola10.debug(`auth.expires_at: ${auth?.expires_at} (now: ${Math.floor(Date.now() / 1e3)})`);
865
- consola10.debug(`getAuthToken(): ${token ? `${token.substring(0, 20)}...` : "NULL"}`);
980
+ consola11.debug(`idp: ${idp}`);
981
+ consola11.debug(`grantsUrl: ${grantsUrl}`);
982
+ consola11.debug(`auth.email: ${auth?.email}`);
983
+ consola11.debug(`auth.expires_at: ${auth?.expires_at} (now: ${Math.floor(Date.now() / 1e3)})`);
984
+ consola11.debug(`getAuthToken(): ${token ? `${token.substring(0, 20)}...` : "NULL"}`);
866
985
  }
867
986
  if (!auth || !token) {
868
987
  throw new CliError("Authentication required. Run `apes login` and try again.");
@@ -880,11 +999,11 @@ var revokeCommand = defineCommand11({
880
999
  );
881
1000
  const ownPending = auth2?.email ? response.data.filter((g) => g.request?.requester === auth2.email) : response.data;
882
1001
  if (ownPending.length === 0) {
883
- consola10.info("No pending grants to revoke.");
1002
+ consola11.info("No pending grants to revoke.");
884
1003
  return;
885
1004
  }
886
1005
  ids = ownPending.map((g) => g.id);
887
- consola10.info(`Found ${ids.length} pending grant(s) to revoke.`);
1006
+ consola11.info(`Found ${ids.length} pending grant(s) to revoke.`);
888
1007
  } else if (explicitIds.length > 0) {
889
1008
  ids = explicitIds;
890
1009
  } else {
@@ -892,7 +1011,7 @@ var revokeCommand = defineCommand11({
892
1011
  }
893
1012
  if (ids.length === 1) {
894
1013
  await apiFetch(`${grantsUrl}/${ids[0]}/revoke`, { method: "POST", token });
895
- consola10.success(`Grant ${ids[0]} revoked.`);
1014
+ consola11.success(`Grant ${ids[0]} revoked.`);
896
1015
  return;
897
1016
  }
898
1017
  const operations = ids.map((id) => ({ id, action: "revoke" }));
@@ -903,16 +1022,16 @@ var revokeCommand = defineCommand11({
903
1022
  let succeeded = 0;
904
1023
  for (const r of results) {
905
1024
  if (r.success) {
906
- consola10.success(`Grant ${r.id} revoked.`);
1025
+ consola11.success(`Grant ${r.id} revoked.`);
907
1026
  succeeded++;
908
1027
  } else {
909
- consola10.error(`Grant ${r.id}: ${r.error?.title || "Failed"}`);
1028
+ consola11.error(`Grant ${r.id}: ${r.error?.title || "Failed"}`);
910
1029
  }
911
1030
  }
912
1031
  if (succeeded < results.length) {
913
1032
  throw new CliError(`Revoked ${succeeded} of ${results.length} grants.`);
914
1033
  } else {
915
- consola10.success(`All ${succeeded} grants revoked.`);
1034
+ consola11.success(`All ${succeeded} grants revoked.`);
916
1035
  }
917
1036
  }
918
1037
  });
@@ -946,7 +1065,7 @@ var tokenCommand = defineCommand12({
946
1065
 
947
1066
  // src/commands/grants/delegate.ts
948
1067
  import { defineCommand as defineCommand13 } from "citty";
949
- import consola11 from "consola";
1068
+ import consola12 from "consola";
950
1069
  var delegateCommand = defineCommand13({
951
1070
  meta: {
952
1071
  name: "delegate",
@@ -999,7 +1118,7 @@ var delegateCommand = defineCommand13({
999
1118
  method: "POST",
1000
1119
  body
1001
1120
  });
1002
- consola11.success(`Delegation created: ${result.id}`);
1121
+ consola12.success(`Delegation created: ${result.id}`);
1003
1122
  console.log(` Delegate: ${args.to}`);
1004
1123
  console.log(` Audience: ${args.at}`);
1005
1124
  if (args.scopes)
@@ -1012,7 +1131,7 @@ var delegateCommand = defineCommand13({
1012
1131
 
1013
1132
  // src/commands/grants/delegations.ts
1014
1133
  import { defineCommand as defineCommand14 } from "citty";
1015
- import consola12 from "consola";
1134
+ import consola13 from "consola";
1016
1135
  var delegationsCommand = defineCommand14({
1017
1136
  meta: {
1018
1137
  name: "delegations",
@@ -1035,7 +1154,7 @@ var delegationsCommand = defineCommand14({
1035
1154
  return;
1036
1155
  }
1037
1156
  if (delegations.length === 0) {
1038
- consola12.info("No delegations found.");
1157
+ consola13.info("No delegations found.");
1039
1158
  return;
1040
1159
  }
1041
1160
  for (const d of delegations) {
@@ -1048,7 +1167,7 @@ var delegationsCommand = defineCommand14({
1048
1167
 
1049
1168
  // src/commands/grants/delegation-revoke.ts
1050
1169
  import { defineCommand as defineCommand15 } from "citty";
1051
- import consola13 from "consola";
1170
+ import consola14 from "consola";
1052
1171
  var delegationRevokeCommand = defineCommand15({
1053
1172
  meta: {
1054
1173
  name: "delegation-revoke",
@@ -1072,7 +1191,7 @@ var delegationRevokeCommand = defineCommand15({
1072
1191
  `${delegationsUrl}/${id}`,
1073
1192
  { method: "DELETE" }
1074
1193
  );
1075
- consola13.success(`Delegation ${result.id} revoked.`);
1194
+ consola14.success(`Delegation ${result.id} revoked.`);
1076
1195
  }
1077
1196
  });
1078
1197
 
@@ -1081,7 +1200,7 @@ import { defineCommand as defineCommand18 } from "citty";
1081
1200
 
1082
1201
  // src/commands/admin/users.ts
1083
1202
  import { defineCommand as defineCommand16 } from "citty";
1084
- import consola14 from "consola";
1203
+ import consola15 from "consola";
1085
1204
  function getManagementToken() {
1086
1205
  const token = process.env.APES_MANAGEMENT_TOKEN;
1087
1206
  if (!token) {
@@ -1131,7 +1250,7 @@ var usersListCommand = defineCommand16({
1131
1250
  return;
1132
1251
  }
1133
1252
  if (result.data.length === 0) {
1134
- consola14.info("No users found.");
1253
+ consola15.info("No users found.");
1135
1254
  return;
1136
1255
  }
1137
1256
  for (const u of result.data) {
@@ -1140,7 +1259,7 @@ var usersListCommand = defineCommand16({
1140
1259
  console.log(`${u.email} ${u.name}${owner}${active}`);
1141
1260
  }
1142
1261
  if (result.pagination.has_more) {
1143
- consola14.info(`More results available. Use --cursor="${result.pagination.cursor}" to see next page.`);
1262
+ consola15.info(`More results available. Use --cursor="${result.pagination.cursor}" to see next page.`);
1144
1263
  }
1145
1264
  }
1146
1265
  });
@@ -1175,7 +1294,7 @@ var usersCreateCommand = defineCommand16({
1175
1294
  token
1176
1295
  }
1177
1296
  );
1178
- consola14.success(`User created: ${result.email} (${result.name})`);
1297
+ consola15.success(`User created: ${result.email} (${result.name})`);
1179
1298
  }
1180
1299
  });
1181
1300
  var usersDeleteCommand = defineCommand16({
@@ -1201,16 +1320,16 @@ var usersDeleteCommand = defineCommand16({
1201
1320
  method: "DELETE",
1202
1321
  token
1203
1322
  });
1204
- consola14.success(`User deleted: ${email}`);
1323
+ consola15.success(`User deleted: ${email}`);
1205
1324
  }
1206
1325
  });
1207
1326
 
1208
1327
  // src/commands/admin/ssh-keys.ts
1209
- import { existsSync, readFileSync } from "fs";
1328
+ import { existsSync as existsSync2, readFileSync } from "fs";
1210
1329
  import { resolve } from "path";
1211
- import { homedir } from "os";
1330
+ import { homedir as homedir3 } from "os";
1212
1331
  import { defineCommand as defineCommand17 } from "citty";
1213
- import consola15 from "consola";
1332
+ import consola16 from "consola";
1214
1333
  function getManagementToken2() {
1215
1334
  const token = process.env.APES_MANAGEMENT_TOKEN;
1216
1335
  if (!token) {
@@ -1251,7 +1370,7 @@ var sshKeysListCommand = defineCommand17({
1251
1370
  return;
1252
1371
  }
1253
1372
  if (keys.length === 0) {
1254
- consola15.info(`No SSH keys found for ${email}.`);
1373
+ consola16.info(`No SSH keys found for ${email}.`);
1255
1374
  return;
1256
1375
  }
1257
1376
  for (const k of keys) {
@@ -1287,8 +1406,8 @@ var sshKeysAddCommand = defineCommand17({
1287
1406
  }
1288
1407
  const token = getManagementToken2();
1289
1408
  let publicKey = args.key;
1290
- const resolved = resolve(args.key.replace(/^~/, homedir()));
1291
- if (existsSync(resolved)) {
1409
+ const resolved = resolve(args.key.replace(/^~/, homedir3()));
1410
+ if (existsSync2(resolved)) {
1292
1411
  publicKey = readFileSync(resolved, "utf-8").trim();
1293
1412
  }
1294
1413
  const body = { publicKey };
@@ -1303,7 +1422,7 @@ var sshKeysAddCommand = defineCommand17({
1303
1422
  token
1304
1423
  }
1305
1424
  );
1306
- consola15.success(`SSH key added: ${result.keyId} (${result.name})`);
1425
+ consola16.success(`SSH key added: ${result.keyId} (${result.name})`);
1307
1426
  }
1308
1427
  });
1309
1428
  var sshKeysDeleteCommand = defineCommand17({
@@ -1337,7 +1456,7 @@ var sshKeysDeleteCommand = defineCommand17({
1337
1456
  token
1338
1457
  }
1339
1458
  );
1340
- consola15.success(`SSH key deleted: ${keyId}`);
1459
+ consola16.success(`SSH key deleted: ${keyId}`);
1341
1460
  }
1342
1461
  });
1343
1462
 
@@ -1377,7 +1496,7 @@ var adminCommand = defineCommand18({
1377
1496
 
1378
1497
  // src/commands/adapter/index.ts
1379
1498
  import { defineCommand as defineCommand19 } from "citty";
1380
- import consola16 from "consola";
1499
+ import consola17 from "consola";
1381
1500
  var adapterCommand = defineCommand19({
1382
1501
  meta: {
1383
1502
  name: "adapter",
@@ -1415,7 +1534,7 @@ var adapterCommand = defineCommand19({
1415
1534
  `);
1416
1535
  return;
1417
1536
  }
1418
- consola16.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
1537
+ consola17.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
1419
1538
  for (const a of index2.adapters) {
1420
1539
  const installed = isInstalled(a.id, false) ? " [installed]" : "";
1421
1540
  console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
@@ -1437,7 +1556,7 @@ var adapterCommand = defineCommand19({
1437
1556
  return;
1438
1557
  }
1439
1558
  if (local.length === 0) {
1440
- consola16.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
1559
+ consola17.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
1441
1560
  return;
1442
1561
  }
1443
1562
  for (const a of local) {
@@ -1474,20 +1593,20 @@ var adapterCommand = defineCommand19({
1474
1593
  for (const id of ids) {
1475
1594
  const entry = findAdapter(index, id);
1476
1595
  if (!entry) {
1477
- consola16.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
1596
+ consola17.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
1478
1597
  continue;
1479
1598
  }
1480
1599
  const conflicts = findConflictingAdapters(entry.executable, id);
1481
1600
  if (conflicts.length > 0) {
1482
1601
  for (const c of conflicts) {
1483
- consola16.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
1484
- consola16.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
1602
+ consola17.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
1603
+ consola17.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
1485
1604
  }
1486
1605
  }
1487
1606
  const result = await installAdapter(entry, { local });
1488
1607
  const verb = result.updated ? "Updated" : "Installed";
1489
- consola16.success(`${verb} ${result.id} \u2192 ${result.path}`);
1490
- consola16.info(`Digest: ${result.digest}`);
1608
+ consola17.success(`${verb} ${result.id} \u2192 ${result.path}`);
1609
+ consola17.info(`Digest: ${result.digest}`);
1491
1610
  }
1492
1611
  }
1493
1612
  }),
@@ -1514,9 +1633,9 @@ var adapterCommand = defineCommand19({
1514
1633
  let failed = false;
1515
1634
  for (const id of ids) {
1516
1635
  if (removeAdapter(id, local)) {
1517
- consola16.success(`Removed adapter: ${id}`);
1636
+ consola17.success(`Removed adapter: ${id}`);
1518
1637
  } else {
1519
- consola16.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
1638
+ consola17.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
1520
1639
  failed = true;
1521
1640
  }
1522
1641
  }
@@ -1598,7 +1717,7 @@ var adapterCommand = defineCommand19({
1598
1717
  return;
1599
1718
  }
1600
1719
  if (results.length === 0) {
1601
- consola16.info(`No adapters matching "${query}"`);
1720
+ consola17.info(`No adapters matching "${query}"`);
1602
1721
  return;
1603
1722
  }
1604
1723
  for (const a of results) {
@@ -1633,29 +1752,29 @@ var adapterCommand = defineCommand19({
1633
1752
  const targetId = args.id ? String(args.id) : void 0;
1634
1753
  const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
1635
1754
  if (targets.length === 0) {
1636
- consola16.info("No adapters installed to update.");
1755
+ consola17.info("No adapters installed to update.");
1637
1756
  return;
1638
1757
  }
1639
1758
  for (const id of targets) {
1640
1759
  const entry = findAdapter(index, id);
1641
1760
  if (!entry) {
1642
- consola16.warn(`${id}: not found in registry, skipping`);
1761
+ consola17.warn(`${id}: not found in registry, skipping`);
1643
1762
  continue;
1644
1763
  }
1645
1764
  const localDigest = getInstalledDigest(id, false);
1646
1765
  if (localDigest === entry.digest) {
1647
- consola16.info(`${id}: already up to date`);
1766
+ consola17.info(`${id}: already up to date`);
1648
1767
  continue;
1649
1768
  }
1650
1769
  if (localDigest && !args.yes) {
1651
- consola16.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
1652
- consola16.info(` Old: ${localDigest}`);
1653
- consola16.info(` New: ${entry.digest}`);
1654
- consola16.info(" Use --yes to confirm");
1770
+ consola17.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
1771
+ consola17.info(` Old: ${localDigest}`);
1772
+ consola17.info(` New: ${entry.digest}`);
1773
+ consola17.info(" Use --yes to confirm");
1655
1774
  continue;
1656
1775
  }
1657
1776
  const result = await installAdapter(entry);
1658
- consola16.success(`Updated ${result.id} \u2192 ${result.path}`);
1777
+ consola17.success(`Updated ${result.id} \u2192 ${result.path}`);
1659
1778
  }
1660
1779
  }
1661
1780
  }),
@@ -1692,7 +1811,7 @@ var adapterCommand = defineCommand19({
1692
1811
  if (!localDigest)
1693
1812
  throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
1694
1813
  if (localDigest === entry.digest) {
1695
- consola16.success(`${id}: digest matches registry`);
1814
+ consola17.success(`${id}: digest matches registry`);
1696
1815
  } else {
1697
1816
  console.log(` Local: ${localDigest}`);
1698
1817
  console.log(` Registry: ${entry.digest}`);
@@ -1706,8 +1825,9 @@ var adapterCommand = defineCommand19({
1706
1825
  // src/commands/run.ts
1707
1826
  import { execFileSync } from "child_process";
1708
1827
  import { hostname as hostname3 } from "os";
1828
+ import { basename } from "path";
1709
1829
  import { defineCommand as defineCommand20 } from "citty";
1710
- import consola17 from "consola";
1830
+ import consola18 from "consola";
1711
1831
  var runCommand = defineCommand20({
1712
1832
  meta: {
1713
1833
  name: "run",
@@ -1795,7 +1915,7 @@ async function runShellMode(command, args) {
1795
1915
  }
1796
1916
  } catch {
1797
1917
  }
1798
- consola17.info(`Requesting ape-shell session grant on ${targetHost}`);
1918
+ consola18.info(`Requesting ape-shell session grant on ${targetHost}`);
1799
1919
  const grant = await apiFetch(grantsUrl, {
1800
1920
  method: "POST",
1801
1921
  body: {
@@ -1807,8 +1927,8 @@ async function runShellMode(command, args) {
1807
1927
  reason: `Shell session: ${command.join(" ").slice(0, 100)}`
1808
1928
  }
1809
1929
  });
1810
- consola17.info(`Grant requested: ${grant.id}`);
1811
- consola17.info("Waiting for approval...");
1930
+ consola18.info(`Grant requested: ${grant.id}`);
1931
+ consola18.info("Waiting for approval...");
1812
1932
  const maxWait = 3e5;
1813
1933
  const interval = 3e3;
1814
1934
  const start = Date.now();
@@ -1830,17 +1950,18 @@ async function tryAdapterModeFromShell(command, idp, args) {
1830
1950
  if (parsed.isCompound) return false;
1831
1951
  const loaded = await loadOrInstallAdapter(parsed.executable);
1832
1952
  if (!loaded) return false;
1953
+ const normalizedExecutable = basename(parsed.executable);
1833
1954
  let resolved;
1834
1955
  try {
1835
- resolved = await resolveCommand(loaded, [parsed.executable, ...parsed.argv]);
1956
+ resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
1836
1957
  } catch (err) {
1837
- consola17.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
1958
+ consola18.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
1838
1959
  return false;
1839
1960
  }
1840
1961
  try {
1841
1962
  const existingGrantId = await findExistingGrant(resolved, idp);
1842
1963
  if (existingGrantId) {
1843
- consola17.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
1964
+ consola18.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
1844
1965
  const token2 = await fetchGrantToken(idp, existingGrantId);
1845
1966
  await verifyAndExecute(token2, resolved);
1846
1967
  return true;
@@ -1848,18 +1969,18 @@ async function tryAdapterModeFromShell(command, idp, args) {
1848
1969
  } catch {
1849
1970
  }
1850
1971
  const approval = args.approval ?? "once";
1851
- consola17.info(`Requesting grant for: ${resolved.detail.display}`);
1972
+ consola18.info(`Requesting grant for: ${resolved.detail.display}`);
1852
1973
  const grant = await createShapesGrant(resolved, {
1853
1974
  idp,
1854
1975
  approval,
1855
1976
  reason: args.reason || `ape-shell: ${resolved.detail.display}`
1856
1977
  });
1857
- consola17.info(`Grant requested: ${grant.id}`);
1858
- consola17.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
1978
+ consola18.info(`Grant requested: ${grant.id}`);
1979
+ consola18.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
1859
1980
  if (grant.similar_grants?.similar_grants?.length) {
1860
1981
  const n = grant.similar_grants.similar_grants.length;
1861
- consola17.info("");
1862
- consola17.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
1982
+ consola18.info("");
1983
+ consola18.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
1863
1984
  }
1864
1985
  const status = await waitForGrantStatus(idp, grant.id);
1865
1986
  if (status !== "approved")
@@ -1909,7 +2030,7 @@ async function runAdapterMode(command, rawArgs, args) {
1909
2030
  try {
1910
2031
  const existingGrantId = await findExistingGrant(resolved, idp);
1911
2032
  if (existingGrantId) {
1912
- consola17.info(`Reusing existing grant: ${existingGrantId}`);
2033
+ consola18.info(`Reusing existing grant: ${existingGrantId}`);
1913
2034
  const token2 = await fetchGrantToken(idp, existingGrantId);
1914
2035
  await verifyAndExecute(token2, resolved);
1915
2036
  return;
@@ -1921,17 +2042,17 @@ async function runAdapterMode(command, rawArgs, args) {
1921
2042
  approval,
1922
2043
  ...args.reason ? { reason: args.reason } : {}
1923
2044
  });
1924
- consola17.info(`Grant requested: ${grant.id}`);
1925
- consola17.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
2045
+ consola18.info(`Grant requested: ${grant.id}`);
2046
+ consola18.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
1926
2047
  if (grant.similar_grants?.similar_grants?.length) {
1927
2048
  const n = grant.similar_grants.similar_grants.length;
1928
- consola17.info("");
1929
- consola17.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
2049
+ consola18.info("");
2050
+ consola18.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
1930
2051
  if (grant.similar_grants.widened_details?.length) {
1931
2052
  const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
1932
- consola17.info(` Broader scope: ${wider}`);
2053
+ consola18.info(` Broader scope: ${wider}`);
1933
2054
  }
1934
- consola17.info("");
2055
+ consola18.info("");
1935
2056
  }
1936
2057
  const status = await waitForGrantStatus(idp, grant.id);
1937
2058
  if (status !== "approved")
@@ -1948,7 +2069,7 @@ async function runAudienceMode(audience, action, args) {
1948
2069
  const grantsUrl = await getGrantsEndpoint(idp);
1949
2070
  const command = action.split(" ");
1950
2071
  const targetHost = args.host || hostname3();
1951
- consola17.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
2072
+ consola18.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
1952
2073
  const grant = await apiFetch(grantsUrl, {
1953
2074
  method: "POST",
1954
2075
  body: {
@@ -1961,15 +2082,15 @@ async function runAudienceMode(audience, action, args) {
1961
2082
  ...args.as ? { run_as: args.as } : {}
1962
2083
  }
1963
2084
  });
1964
- consola17.success(`Grant requested: ${grant.id}`);
1965
- consola17.info("Waiting for approval...");
2085
+ consola18.success(`Grant requested: ${grant.id}`);
2086
+ consola18.info("Waiting for approval...");
1966
2087
  const maxWait = 3e5;
1967
2088
  const interval = 3e3;
1968
2089
  const start = Date.now();
1969
2090
  while (Date.now() - start < maxWait) {
1970
2091
  const status = await apiFetch(`${grantsUrl}/${grant.id}`);
1971
2092
  if (status.status === "approved") {
1972
- consola17.success("Grant approved!");
2093
+ consola18.success("Grant approved!");
1973
2094
  break;
1974
2095
  }
1975
2096
  if (status.status === "denied" || status.status === "revoked") {
@@ -1977,12 +2098,12 @@ async function runAudienceMode(audience, action, args) {
1977
2098
  }
1978
2099
  await new Promise((r) => setTimeout(r, interval));
1979
2100
  }
1980
- consola17.info("Fetching grant token...");
2101
+ consola18.info("Fetching grant token...");
1981
2102
  const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
1982
2103
  method: "POST"
1983
2104
  });
1984
2105
  if (audience === "escapes") {
1985
- consola17.info(`Executing: ${command.join(" ")}`);
2106
+ consola18.info(`Executing: ${command.join(" ")}`);
1986
2107
  try {
1987
2108
  execFileSync(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
1988
2109
  stdio: "inherit"
@@ -2037,7 +2158,7 @@ var explainCommand = defineCommand21({
2037
2158
 
2038
2159
  // src/commands/config/get.ts
2039
2160
  import { defineCommand as defineCommand22 } from "citty";
2040
- import consola18 from "consola";
2161
+ import consola19 from "consola";
2041
2162
  var configGetCommand = defineCommand22({
2042
2163
  meta: {
2043
2164
  name: "get",
@@ -2058,7 +2179,7 @@ var configGetCommand = defineCommand22({
2058
2179
  if (idp)
2059
2180
  console.log(idp);
2060
2181
  else
2061
- consola18.info("No IdP configured.");
2182
+ consola19.info("No IdP configured.");
2062
2183
  break;
2063
2184
  }
2064
2185
  case "email": {
@@ -2066,7 +2187,7 @@ var configGetCommand = defineCommand22({
2066
2187
  if (auth?.email)
2067
2188
  console.log(auth.email);
2068
2189
  else
2069
- consola18.info("Not logged in.");
2190
+ consola19.info("Not logged in.");
2070
2191
  break;
2071
2192
  }
2072
2193
  default: {
@@ -2079,7 +2200,7 @@ var configGetCommand = defineCommand22({
2079
2200
  if (sectionObj && field in sectionObj) {
2080
2201
  console.log(sectionObj[field]);
2081
2202
  } else {
2082
- consola18.info(`Key "${key}" not set.`);
2203
+ consola19.info(`Key "${key}" not set.`);
2083
2204
  }
2084
2205
  } else {
2085
2206
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -2091,7 +2212,7 @@ var configGetCommand = defineCommand22({
2091
2212
 
2092
2213
  // src/commands/config/set.ts
2093
2214
  import { defineCommand as defineCommand23 } from "citty";
2094
- import consola19 from "consola";
2215
+ import consola20 from "consola";
2095
2216
  var configSetCommand = defineCommand23({
2096
2217
  meta: {
2097
2218
  name: "set",
@@ -2128,7 +2249,7 @@ var configSetCommand = defineCommand23({
2128
2249
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
2129
2250
  }
2130
2251
  saveConfig(config);
2131
- consola19.success(`Set ${key} = ${value}`);
2252
+ consola20.success(`Set ${key} = ${value}`);
2132
2253
  }
2133
2254
  });
2134
2255
 
@@ -2264,25 +2385,25 @@ var mcpCommand = defineCommand25({
2264
2385
  if (transport !== "stdio" && transport !== "sse") {
2265
2386
  throw new Error('Transport must be "stdio" or "sse"');
2266
2387
  }
2267
- const { startMcpServer } = await import("./server-FR6GFS3S.js");
2388
+ const { startMcpServer } = await import("./server-UTCZSPCU.js");
2268
2389
  await startMcpServer(transport, port);
2269
2390
  }
2270
2391
  });
2271
2392
 
2272
2393
  // src/commands/init/index.ts
2273
- import { existsSync as existsSync2, copyFileSync, writeFileSync } from "fs";
2394
+ import { existsSync as existsSync3, copyFileSync, writeFileSync } from "fs";
2274
2395
  import { randomBytes } from "crypto";
2275
2396
  import { execFileSync as execFileSync2 } from "child_process";
2276
- import { join } from "path";
2397
+ import { join as join2 } from "path";
2277
2398
  import { defineCommand as defineCommand26 } from "citty";
2278
- import consola20 from "consola";
2399
+ import consola21 from "consola";
2279
2400
  var DEFAULT_IDP_URL = "https://id.openape.at";
2280
2401
  async function downloadTemplate(repo, targetDir) {
2281
2402
  const { downloadTemplate: gigetDownload } = await import("giget");
2282
2403
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
2283
2404
  }
2284
2405
  function installDeps(dir) {
2285
- const hasLockFile = (name) => existsSync2(join(dir, name));
2406
+ const hasLockFile = (name) => existsSync3(join2(dir, name));
2286
2407
  if (hasLockFile("pnpm-lock.yaml")) {
2287
2408
  execFileSync2("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
2288
2409
  } else if (hasLockFile("bun.lockb")) {
@@ -2292,14 +2413,14 @@ function installDeps(dir) {
2292
2413
  }
2293
2414
  }
2294
2415
  async function promptChoice(message, choices) {
2295
- const result = await consola20.prompt(message, { type: "select", options: choices });
2416
+ const result = await consola21.prompt(message, { type: "select", options: choices });
2296
2417
  if (typeof result === "symbol") {
2297
2418
  throw new CliExit(0);
2298
2419
  }
2299
2420
  return result;
2300
2421
  }
2301
2422
  async function promptText(message, defaultValue) {
2302
- const result = await consola20.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
2423
+ const result = await consola21.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
2303
2424
  if (typeof result === "symbol") {
2304
2425
  throw new CliExit(0);
2305
2426
  }
@@ -2347,23 +2468,23 @@ var initCommand = defineCommand26({
2347
2468
  });
2348
2469
  async function initSP(targetDir) {
2349
2470
  const dir = targetDir || "my-app";
2350
- if (existsSync2(join(dir, "package.json"))) {
2471
+ if (existsSync3(join2(dir, "package.json"))) {
2351
2472
  throw new CliError(`Directory "${dir}" already contains a project.`);
2352
2473
  }
2353
- consola20.start("Scaffolding SP starter...");
2474
+ consola21.start("Scaffolding SP starter...");
2354
2475
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
2355
- consola20.success("Scaffolded from openape-sp-starter");
2356
- consola20.start("Installing dependencies...");
2476
+ consola21.success("Scaffolded from openape-sp-starter");
2477
+ consola21.start("Installing dependencies...");
2357
2478
  installDeps(dir);
2358
- consola20.success("Dependencies installed");
2359
- const envExample = join(dir, ".env.example");
2360
- const envFile = join(dir, ".env");
2361
- if (existsSync2(envExample) && !existsSync2(envFile)) {
2479
+ consola21.success("Dependencies installed");
2480
+ const envExample = join2(dir, ".env.example");
2481
+ const envFile = join2(dir, ".env");
2482
+ if (existsSync3(envExample) && !existsSync3(envFile)) {
2362
2483
  copyFileSync(envExample, envFile);
2363
- consola20.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
2484
+ consola21.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
2364
2485
  }
2365
2486
  console.log("");
2366
- consola20.box([
2487
+ consola21.box([
2367
2488
  `cd ${dir}`,
2368
2489
  "npm run dev",
2369
2490
  "",
@@ -2372,7 +2493,7 @@ async function initSP(targetDir) {
2372
2493
  }
2373
2494
  async function initIdP(targetDir) {
2374
2495
  const dir = targetDir || "my-idp";
2375
- if (existsSync2(join(dir, "package.json"))) {
2496
+ if (existsSync3(join2(dir, "package.json"))) {
2376
2497
  throw new CliError(`Directory "${dir}" already contains a project.`);
2377
2498
  }
2378
2499
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -2382,15 +2503,15 @@ async function initIdP(targetDir) {
2382
2503
  "s3 (S3-compatible)"
2383
2504
  ]);
2384
2505
  const adminEmail = await promptText("Admin email");
2385
- consola20.start("Scaffolding IdP starter...");
2506
+ consola21.start("Scaffolding IdP starter...");
2386
2507
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
2387
- consola20.success("Scaffolded from openape-idp-starter");
2388
- consola20.start("Installing dependencies...");
2508
+ consola21.success("Scaffolded from openape-idp-starter");
2509
+ consola21.start("Installing dependencies...");
2389
2510
  installDeps(dir);
2390
- consola20.success("Dependencies installed");
2511
+ consola21.success("Dependencies installed");
2391
2512
  const sessionSecret = randomBytes(32).toString("hex");
2392
2513
  const managementToken = randomBytes(32).toString("hex");
2393
- consola20.success("Secrets generated");
2514
+ consola21.success("Secrets generated");
2394
2515
  const isLocalhost = domain === "localhost";
2395
2516
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
2396
2517
  const envContent = [
@@ -2404,11 +2525,11 @@ async function initIdP(targetDir) {
2404
2525
  `NUXT_OPENAPE_RP_ID=${domain}`,
2405
2526
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
2406
2527
  ].join("\n");
2407
- writeFileSync(join(dir, ".env"), `${envContent}
2528
+ writeFileSync(join2(dir, ".env"), `${envContent}
2408
2529
  `, { mode: 384 });
2409
- consola20.success(".env created");
2530
+ consola21.success(".env created");
2410
2531
  console.log("");
2411
- consola20.box([
2532
+ consola21.box([
2412
2533
  `cd ${dir}`,
2413
2534
  "npm run dev",
2414
2535
  "",
@@ -2425,19 +2546,19 @@ async function initIdP(targetDir) {
2425
2546
 
2426
2547
  // src/commands/enroll.ts
2427
2548
  import { Buffer as Buffer2 } from "buffer";
2428
- import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
2549
+ import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
2429
2550
  import { execFile as execFile2 } from "child_process";
2430
2551
  import { generateKeyPairSync, sign } from "crypto";
2431
2552
  import { dirname, resolve as resolve2 } from "path";
2432
- import { homedir as homedir2 } from "os";
2553
+ import { homedir as homedir4 } from "os";
2433
2554
  import { defineCommand as defineCommand27 } from "citty";
2434
- import consola21 from "consola";
2555
+ import consola22 from "consola";
2435
2556
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
2436
2557
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
2437
2558
  var POLL_INTERVAL = 3e3;
2438
2559
  var POLL_TIMEOUT = 3e5;
2439
- function resolvePath(p) {
2440
- return resolve2(p.replace(/^~/, homedir2()));
2560
+ function resolvePath2(p) {
2561
+ return resolve2(p.replace(/^~/, homedir4()));
2441
2562
  }
2442
2563
  function openBrowser2(url) {
2443
2564
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -2446,7 +2567,7 @@ function openBrowser2(url) {
2446
2567
  }
2447
2568
  function readPublicKey(keyPath) {
2448
2569
  const pubPath = `${keyPath}.pub`;
2449
- if (existsSync3(pubPath)) {
2570
+ if (existsSync4(pubPath)) {
2450
2571
  return readFileSync2(pubPath, "utf-8").trim();
2451
2572
  }
2452
2573
  const keyContent = readFileSync2(keyPath, "utf-8");
@@ -2462,9 +2583,9 @@ function readPublicKey(keyPath) {
2462
2583
  return `ssh-ed25519 ${blob.toString("base64")}`;
2463
2584
  }
2464
2585
  function generateAndSaveKey(keyPath) {
2465
- const resolved = resolvePath(keyPath);
2586
+ const resolved = resolvePath2(keyPath);
2466
2587
  const dir = dirname(resolved);
2467
- if (!existsSync3(dir)) {
2588
+ if (!existsSync4(dir)) {
2468
2589
  mkdirSync(dir, { recursive: true });
2469
2590
  }
2470
2591
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
@@ -2484,7 +2605,7 @@ function generateAndSaveKey(keyPath) {
2484
2605
  return pubKeyStr;
2485
2606
  }
2486
2607
  async function pollForEnrollment(idp, agentEmail, keyPath) {
2487
- const resolvedKey = resolvePath(keyPath);
2608
+ const resolvedKey = resolvePath2(keyPath);
2488
2609
  const keyContent = readFileSync2(resolvedKey, "utf-8");
2489
2610
  const privateKey = loadEd25519PrivateKey(keyContent);
2490
2611
  const challengeUrl = await getAgentChallengeEndpoint(idp);
@@ -2536,38 +2657,38 @@ var enrollCommand = defineCommand27({
2536
2657
  }
2537
2658
  },
2538
2659
  async run({ args }) {
2539
- const idp = args.idp || await consola21.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
2660
+ const idp = args.idp || await consola22.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
2540
2661
  if (typeof r === "symbol") throw new CliExit(0);
2541
2662
  return r;
2542
2663
  }) || DEFAULT_IDP_URL2;
2543
- const agentName = args.name || await consola21.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
2664
+ const agentName = args.name || await consola22.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
2544
2665
  if (typeof r === "symbol") throw new CliExit(0);
2545
2666
  return r;
2546
2667
  });
2547
2668
  if (!agentName) {
2548
2669
  throw new CliError("Agent name is required.");
2549
2670
  }
2550
- const keyPath = args.key || await consola21.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
2671
+ const keyPath = args.key || await consola22.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
2551
2672
  if (typeof r === "symbol") throw new CliExit(0);
2552
2673
  return r;
2553
2674
  }) || DEFAULT_KEY_PATH;
2554
- const resolvedKey = resolvePath(keyPath);
2675
+ const resolvedKey = resolvePath2(keyPath);
2555
2676
  let publicKey;
2556
- if (existsSync3(resolvedKey)) {
2677
+ if (existsSync4(resolvedKey)) {
2557
2678
  publicKey = readPublicKey(resolvedKey);
2558
- consola21.success(`Using existing key ${keyPath}`);
2679
+ consola22.success(`Using existing key ${keyPath}`);
2559
2680
  } else {
2560
- consola21.start(`Generating Ed25519 key pair at ${keyPath}...`);
2681
+ consola22.start(`Generating Ed25519 key pair at ${keyPath}...`);
2561
2682
  publicKey = generateAndSaveKey(keyPath);
2562
- consola21.success(`Key pair generated at ${keyPath}`);
2683
+ consola22.success(`Key pair generated at ${keyPath}`);
2563
2684
  }
2564
2685
  const encodedKey = encodeURIComponent(publicKey);
2565
2686
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
2566
- consola21.info("Opening browser for enrollment...");
2567
- consola21.info(`\u2192 ${idp}/enroll`);
2687
+ consola22.info("Opening browser for enrollment...");
2688
+ consola22.info(`\u2192 ${idp}/enroll`);
2568
2689
  openBrowser2(enrollUrl);
2569
2690
  console.log("");
2570
- const agentEmail = await consola21.prompt(
2691
+ const agentEmail = await consola22.prompt(
2571
2692
  "Agent email (shown in browser after enrollment)",
2572
2693
  { type: "text", placeholder: `agent+${agentName}@...` }
2573
2694
  ).then((r) => {
@@ -2577,7 +2698,7 @@ var enrollCommand = defineCommand27({
2577
2698
  if (!agentEmail) {
2578
2699
  throw new CliError("Agent email is required to verify enrollment.");
2579
2700
  }
2580
- consola21.start("Verifying enrollment...");
2701
+ consola22.start("Verifying enrollment...");
2581
2702
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
2582
2703
  saveAuth({
2583
2704
  idp,
@@ -2589,17 +2710,17 @@ var enrollCommand = defineCommand27({
2589
2710
  config.defaults = { ...config.defaults, idp };
2590
2711
  config.agent = { key: keyPath, email: agentEmail };
2591
2712
  saveConfig(config);
2592
- consola21.success(`Agent enrolled as ${agentEmail}`);
2593
- consola21.success("Config saved to ~/.config/apes/");
2713
+ consola22.success(`Agent enrolled as ${agentEmail}`);
2714
+ consola22.success("Config saved to ~/.config/apes/");
2594
2715
  console.log("");
2595
- consola21.info("Verify with: apes whoami");
2716
+ consola22.info("Verify with: apes whoami");
2596
2717
  }
2597
2718
  });
2598
2719
 
2599
2720
  // src/commands/register-user.ts
2600
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
2721
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2601
2722
  import { defineCommand as defineCommand28 } from "citty";
2602
- import consola22 from "consola";
2723
+ import consola23 from "consola";
2603
2724
  var registerUserCommand = defineCommand28({
2604
2725
  meta: {
2605
2726
  name: "register-user",
@@ -2636,7 +2757,7 @@ var registerUserCommand = defineCommand28({
2636
2757
  throw new CliError("No IdP URL configured. Run `apes login` first.");
2637
2758
  }
2638
2759
  let publicKey = args.key;
2639
- if (existsSync4(args.key)) {
2760
+ if (existsSync5(args.key)) {
2640
2761
  publicKey = readFileSync3(args.key, "utf-8").trim();
2641
2762
  }
2642
2763
  if (!publicKey.startsWith("ssh-ed25519 ")) {
@@ -2655,14 +2776,14 @@ var registerUserCommand = defineCommand28({
2655
2776
  ...userType ? { type: userType } : {}
2656
2777
  }
2657
2778
  });
2658
- consola22.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
2779
+ consola23.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
2659
2780
  }
2660
2781
  });
2661
2782
 
2662
2783
  // src/commands/dns-check.ts
2663
2784
  import { defineCommand as defineCommand29 } from "citty";
2664
- import consola23 from "consola";
2665
- import { resolveDDISA } from "@openape/core";
2785
+ import consola24 from "consola";
2786
+ import { resolveDDISA as resolveDDISA2 } from "@openape/core";
2666
2787
  var dnsCheckCommand = defineCommand29({
2667
2788
  meta: {
2668
2789
  name: "dns-check",
@@ -2677,16 +2798,16 @@ var dnsCheckCommand = defineCommand29({
2677
2798
  },
2678
2799
  async run({ args }) {
2679
2800
  const domain = args.domain;
2680
- consola23.start(`Checking _ddisa.${domain}...`);
2801
+ consola24.start(`Checking _ddisa.${domain}...`);
2681
2802
  try {
2682
- const result = await resolveDDISA(domain);
2803
+ const result = await resolveDDISA2(domain);
2683
2804
  if (!result) {
2684
2805
  console.log("");
2685
2806
  console.log("To set up DDISA, add a DNS TXT record:");
2686
2807
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
2687
2808
  throw new CliError(`No DDISA record found for ${domain}`);
2688
2809
  }
2689
- consola23.success(`_ddisa.${domain} \u2192 ${result.idp}`);
2810
+ consola24.success(`_ddisa.${domain} \u2192 ${result.idp}`);
2690
2811
  console.log("");
2691
2812
  console.log(` Version: ${result.version || "ddisa1"}`);
2692
2813
  console.log(` IdP URL: ${result.idp}`);
@@ -2695,14 +2816,14 @@ var dnsCheckCommand = defineCommand29({
2695
2816
  if (result.priority !== void 0)
2696
2817
  console.log(` Priority: ${result.priority}`);
2697
2818
  console.log("");
2698
- consola23.start(`Verifying IdP at ${result.idp}...`);
2819
+ consola24.start(`Verifying IdP at ${result.idp}...`);
2699
2820
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
2700
2821
  if (!discoResp.ok) {
2701
- consola23.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
2822
+ consola24.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
2702
2823
  return;
2703
2824
  }
2704
2825
  const disco = await discoResp.json();
2705
- consola23.success(`IdP is reachable`);
2826
+ consola24.success(`IdP is reachable`);
2706
2827
  console.log(` Issuer: ${disco.issuer}`);
2707
2828
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
2708
2829
  if (disco.ddisa_auth_methods_supported) {
@@ -2719,7 +2840,7 @@ var dnsCheckCommand = defineCommand29({
2719
2840
 
2720
2841
  // src/commands/workflows.ts
2721
2842
  import { defineCommand as defineCommand30 } from "citty";
2722
- import consola24 from "consola";
2843
+ import consola25 from "consola";
2723
2844
 
2724
2845
  // src/guides/index.ts
2725
2846
  var guides = [
@@ -2790,7 +2911,7 @@ var workflowsCommand = defineCommand30({
2790
2911
  if (args.id) {
2791
2912
  const guide = guides.find((g) => g.id === String(args.id));
2792
2913
  if (!guide) {
2793
- consola24.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
2914
+ consola25.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
2794
2915
  throw new CliError(`Guide not found: ${args.id}`);
2795
2916
  }
2796
2917
  if (args.json) {
@@ -2834,19 +2955,34 @@ process.stdout.on("error", (err) => {
2834
2955
  if (err.code === "EPIPE") process.exit(0);
2835
2956
  throw err;
2836
2957
  });
2837
- var shellRewrite = rewriteApeShellArgs(process.argv);
2958
+ var shellRewrite = rewriteApeShellArgs(process.argv, process.argv0);
2838
2959
  if (shellRewrite) {
2839
2960
  if (shellRewrite.action === "rewrite") {
2840
2961
  process.argv = shellRewrite.argv;
2841
2962
  } else if (shellRewrite.action === "version") {
2842
- console.log(`ape-shell (OpenApe DDISA shell wrapper)`);
2963
+ console.log(`ape-shell ${"0.7.1"} (OpenApe DDISA shell wrapper)`);
2843
2964
  process.exit(0);
2844
2965
  } else if (shellRewrite.action === "help") {
2845
- console.log("Usage: ape-shell -c <command>");
2846
- console.log("Routes all commands through apes run for grant-based authorization.");
2966
+ console.log(`ape-shell ${"0.7.1"} \u2014 OpenApe DDISA shell wrapper`);
2967
+ console.log("");
2968
+ console.log("Usage:");
2969
+ console.log(" ape-shell Start interactive grant-mediated REPL");
2970
+ console.log(" ape-shell -c <command> Run a single command through the grant flow");
2971
+ console.log(" ape-shell -i | -l Force interactive mode");
2972
+ console.log("");
2973
+ console.log("Options:");
2974
+ console.log(" -c <command> Execute <command> via the apes grant flow and exit");
2975
+ console.log(" -i Interactive REPL (default when no args are given)");
2976
+ console.log(" -l, --login Login shell semantics \u2014 currently same as -i");
2977
+ console.log(" --version, -v Show ape-shell version");
2978
+ console.log(" --help, -h Show this help message");
2979
+ process.exit(0);
2980
+ } else if (shellRewrite.action === "interactive") {
2981
+ const { runInteractiveShell } = await import("./orchestrator-JAMWD6DD.js");
2982
+ await runInteractiveShell();
2847
2983
  process.exit(0);
2848
2984
  } else {
2849
- console.error("ape-shell: only -c <command> mode is supported");
2985
+ console.error("ape-shell: unsupported invocation. Try `ape-shell --help`.");
2850
2986
  process.exit(1);
2851
2987
  }
2852
2988
  }
@@ -2884,7 +3020,7 @@ var configCommand = defineCommand31({
2884
3020
  var main = defineCommand31({
2885
3021
  meta: {
2886
3022
  name: "apes",
2887
- version: "0.6.1",
3023
+ version: "0.7.1",
2888
3024
  description: "Unified CLI for OpenApe"
2889
3025
  },
2890
3026
  subCommands: {
@@ -2911,13 +3047,13 @@ runMain(main).catch((err) => {
2911
3047
  process.exit(err.exitCode);
2912
3048
  }
2913
3049
  if (err instanceof CliError) {
2914
- consola25.error(err.message);
3050
+ consola26.error(err.message);
2915
3051
  process.exit(err.exitCode);
2916
3052
  }
2917
3053
  if (debug) {
2918
- consola25.error(err);
3054
+ consola26.error(err);
2919
3055
  } else {
2920
- consola25.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
3056
+ consola26.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
2921
3057
  }
2922
3058
  process.exit(1);
2923
3059
  });