@openape/apes 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -10,22 +10,63 @@ import {
10
10
  import {
11
11
  ApiError,
12
12
  apiFetch,
13
+ buildStructuredCliGrantRequest,
13
14
  clearAuth,
15
+ createShapesGrant,
16
+ extractOption,
17
+ extractShellCommandString,
18
+ extractWrappedCommand,
19
+ fetchGrantToken,
20
+ fetchRegistry,
21
+ findAdapter,
22
+ findConflictingAdapters,
23
+ findExistingGrant,
14
24
  getAgentAuthenticateEndpoint,
15
25
  getAgentChallengeEndpoint,
16
26
  getAuthToken,
17
27
  getDelegationsEndpoint,
18
28
  getGrantsEndpoint,
19
29
  getIdpUrl,
30
+ getInstalledDigest,
31
+ installAdapter,
32
+ isInstalled,
33
+ loadAdapter,
20
34
  loadAuth,
21
35
  loadConfig,
36
+ loadOrInstallAdapter,
37
+ parseShellCommand,
38
+ removeAdapter,
39
+ resolveCapabilityRequest,
40
+ resolveCommand,
22
41
  saveAuth,
23
- saveConfig
24
- } from "./chunk-KXESKY4X.js";
42
+ saveConfig,
43
+ searchAdapters,
44
+ verifyAndExecute,
45
+ waitForGrantStatus
46
+ } from "./chunk-G3Q2TMAI.js";
25
47
 
26
48
  // src/cli.ts
27
- import consola21 from "consola";
28
- import { defineCommand as defineCommand26, runMain } from "citty";
49
+ import consola25 from "consola";
50
+
51
+ // src/ape-shell.ts
52
+ 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
+ return null;
57
+ const shellArgs = argv.slice(2);
58
+ if (shellArgs[0] === "-c" && shellArgs.length > 1) {
59
+ return { action: "rewrite", argv: [argv[0], argv[1], "run", "--shell", "--", "bash", "-c", ...shellArgs.slice(1)] };
60
+ }
61
+ if (shellArgs[0] === "--version")
62
+ return { action: "version" };
63
+ if (shellArgs[0] === "--help" || shellArgs[0] === "-h")
64
+ return { action: "help" };
65
+ return { action: "error" };
66
+ }
67
+
68
+ // src/cli.ts
69
+ import { defineCommand as defineCommand31, runMain } from "citty";
29
70
 
30
71
  // src/commands/auth/login.ts
31
72
  import { Buffer } from "buffer";
@@ -88,7 +129,7 @@ async function loginWithPKCE(idp) {
88
129
  authUrl.searchParams.set("state", state);
89
130
  authUrl.searchParams.set("nonce", nonce);
90
131
  authUrl.searchParams.set("scope", "openid email profile offline_access");
91
- const code = await new Promise((resolve2, reject) => {
132
+ const code = await new Promise((resolve3, reject) => {
92
133
  const server = createServer((req, res) => {
93
134
  const url = new URL(req.url, `http://localhost:${CALLBACK_PORT}`);
94
135
  if (url.pathname === "/callback") {
@@ -105,7 +146,7 @@ async function loginWithPKCE(idp) {
105
146
  res.writeHead(200, { "Content-Type": "text/html" });
106
147
  res.end("<h1>Login successful!</h1><p>You can close this window.</p>");
107
148
  server.close();
108
- resolve2(authCode);
149
+ resolve3(authCode);
109
150
  return;
110
151
  }
111
152
  res.writeHead(400);
@@ -156,7 +197,7 @@ async function loginWithPKCE(idp) {
156
197
  consola.success(`Logged in as ${payload.email || payload.sub}`);
157
198
  }
158
199
  async function loginWithKey(idp, keyPath, email) {
159
- const { readFileSync: readFileSync2 } = await import("fs");
200
+ const { readFileSync: readFileSync4 } = await import("fs");
160
201
  const { sign: sign2 } = await import("crypto");
161
202
  const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-Q7KG4K25.js");
162
203
  const agentEmail = email;
@@ -173,7 +214,7 @@ async function loginWithKey(idp, keyPath, email) {
173
214
  throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
174
215
  }
175
216
  const { challenge } = await challengeResp.json();
176
- const keyContent = readFileSync2(keyPath, "utf-8");
217
+ const keyContent = readFileSync4(keyPath, "utf-8");
177
218
  const privateKey = loadEd25519PrivateKey2(keyContent);
178
219
  const signature = sign2(null, Buffer.from(challenge), privateKey).toString("base64");
179
220
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -196,7 +237,7 @@ async function loginWithKey(idp, keyPath, email) {
196
237
  email: agentEmail,
197
238
  expires_at: Math.floor(Date.now() / 1e3) + (expires_in || 3600)
198
239
  });
199
- consola.success(`Logged in as ${agentEmail} (agent)`);
240
+ consola.success(`Logged in as ${agentEmail}`);
200
241
  }
201
242
 
202
243
  // src/commands/auth/logout.ts
@@ -519,7 +560,6 @@ async function waitForApproval(grantsUrl, grantId) {
519
560
 
520
561
  // src/commands/grants/request-capability.ts
521
562
  import { hostname as hostname2 } from "os";
522
- import { buildStructuredCliGrantRequest, loadAdapter, resolveCapabilityRequest } from "@openape/shapes";
523
563
  import { defineCommand as defineCommand8 } from "citty";
524
564
  import consola7 from "consola";
525
565
  function parseCapabilityArgs(rawArgs) {
@@ -637,7 +677,7 @@ async function waitForApproval2(grantsUrl, grantId) {
637
677
  if (grant.status === "revoked") {
638
678
  throw new CliError("Grant revoked.");
639
679
  }
640
- await new Promise((resolve2) => setTimeout(resolve2, interval));
680
+ await new Promise((resolve3) => setTimeout(resolve3, interval));
641
681
  }
642
682
  throw new CliError("Timed out waiting for approval.");
643
683
  }
@@ -988,7 +1028,8 @@ var delegationsCommand = defineCommand14({
988
1028
  async run({ args }) {
989
1029
  const idp = getIdpUrl();
990
1030
  const delegationsUrl = await getDelegationsEndpoint(idp);
991
- const delegations = await apiFetch(delegationsUrl);
1031
+ const response = await apiFetch(delegationsUrl);
1032
+ const delegations = Array.isArray(response) ? response : response.data;
992
1033
  if (args.json) {
993
1034
  console.log(JSON.stringify(delegations, null, 2));
994
1035
  return;
@@ -1005,27 +1046,345 @@ var delegationsCommand = defineCommand14({
1005
1046
  }
1006
1047
  });
1007
1048
 
1008
- // src/commands/adapter/index.ts
1049
+ // src/commands/grants/delegation-revoke.ts
1009
1050
  import { defineCommand as defineCommand15 } from "citty";
1010
1051
  import consola13 from "consola";
1011
- import {
1012
- fetchRegistry,
1013
- findAdapter,
1014
- findConflictingAdapters,
1015
- getInstalledDigest,
1016
- installAdapter,
1017
- isInstalled,
1018
- loadAdapter as loadAdapter2,
1019
- removeAdapter,
1020
- searchAdapters
1021
- } from "@openape/shapes";
1022
- var adapterCommand = defineCommand15({
1052
+ var delegationRevokeCommand = defineCommand15({
1053
+ meta: {
1054
+ name: "delegation-revoke",
1055
+ description: "Revoke a delegation"
1056
+ },
1057
+ args: {
1058
+ id: {
1059
+ type: "positional",
1060
+ description: "Delegation ID to revoke",
1061
+ required: true
1062
+ }
1063
+ },
1064
+ async run({ args }) {
1065
+ const idp = getIdpUrl();
1066
+ if (!idp) {
1067
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1068
+ }
1069
+ const delegationsUrl = await getDelegationsEndpoint(idp);
1070
+ const id = String(args.id);
1071
+ const result = await apiFetch(
1072
+ `${delegationsUrl}/${id}`,
1073
+ { method: "DELETE" }
1074
+ );
1075
+ consola13.success(`Delegation ${result.id} revoked.`);
1076
+ }
1077
+ });
1078
+
1079
+ // src/commands/admin/index.ts
1080
+ import { defineCommand as defineCommand18 } from "citty";
1081
+
1082
+ // src/commands/admin/users.ts
1083
+ import { defineCommand as defineCommand16 } from "citty";
1084
+ import consola14 from "consola";
1085
+ function getManagementToken() {
1086
+ const token = process.env.APES_MANAGEMENT_TOKEN;
1087
+ if (!token) {
1088
+ throw new CliError("Management token required. Set APES_MANAGEMENT_TOKEN environment variable.");
1089
+ }
1090
+ return token;
1091
+ }
1092
+ var usersListCommand = defineCommand16({
1093
+ meta: {
1094
+ name: "list",
1095
+ description: "List all users"
1096
+ },
1097
+ args: {
1098
+ json: {
1099
+ type: "boolean",
1100
+ description: "Output as JSON",
1101
+ default: false
1102
+ },
1103
+ limit: {
1104
+ type: "string",
1105
+ description: "Max number of users to return (1-100, default 50)"
1106
+ },
1107
+ cursor: {
1108
+ type: "string",
1109
+ description: "Pagination cursor (email of last item from previous page)"
1110
+ },
1111
+ search: {
1112
+ type: "string",
1113
+ description: "Filter by email or name (case-insensitive)"
1114
+ }
1115
+ },
1116
+ async run({ args }) {
1117
+ const idp = getIdpUrl();
1118
+ if (!idp) {
1119
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1120
+ }
1121
+ const token = getManagementToken();
1122
+ const params = new URLSearchParams();
1123
+ if (args.limit) params.set("limit", args.limit);
1124
+ if (args.cursor) params.set("cursor", args.cursor);
1125
+ if (args.search) params.set("search", args.search);
1126
+ const qs = params.toString();
1127
+ const url = qs ? `${idp}/api/admin/users?${qs}` : `${idp}/api/admin/users`;
1128
+ const result = await apiFetch(url, { token });
1129
+ if (args.json) {
1130
+ console.log(JSON.stringify(result, null, 2));
1131
+ return;
1132
+ }
1133
+ if (result.data.length === 0) {
1134
+ consola14.info("No users found.");
1135
+ return;
1136
+ }
1137
+ for (const u of result.data) {
1138
+ const owner = u.owner ? ` (agent of ${u.owner})` : "";
1139
+ const active = u.isActive ? "" : " [inactive]";
1140
+ console.log(`${u.email} ${u.name}${owner}${active}`);
1141
+ }
1142
+ if (result.pagination.has_more) {
1143
+ consola14.info(`More results available. Use --cursor="${result.pagination.cursor}" to see next page.`);
1144
+ }
1145
+ }
1146
+ });
1147
+ var usersCreateCommand = defineCommand16({
1148
+ meta: {
1149
+ name: "create",
1150
+ description: "Create a user"
1151
+ },
1152
+ args: {
1153
+ email: {
1154
+ type: "string",
1155
+ description: "User email",
1156
+ required: true
1157
+ },
1158
+ name: {
1159
+ type: "string",
1160
+ description: "User name",
1161
+ required: true
1162
+ }
1163
+ },
1164
+ async run({ args }) {
1165
+ const idp = getIdpUrl();
1166
+ if (!idp) {
1167
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1168
+ }
1169
+ const token = getManagementToken();
1170
+ const result = await apiFetch(
1171
+ `${idp}/api/admin/users`,
1172
+ {
1173
+ method: "POST",
1174
+ body: { email: args.email, name: args.name },
1175
+ token
1176
+ }
1177
+ );
1178
+ consola14.success(`User created: ${result.email} (${result.name})`);
1179
+ }
1180
+ });
1181
+ var usersDeleteCommand = defineCommand16({
1182
+ meta: {
1183
+ name: "delete",
1184
+ description: "Delete a user"
1185
+ },
1186
+ args: {
1187
+ email: {
1188
+ type: "positional",
1189
+ description: "User email",
1190
+ required: true
1191
+ }
1192
+ },
1193
+ async run({ args }) {
1194
+ const idp = getIdpUrl();
1195
+ if (!idp) {
1196
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1197
+ }
1198
+ const token = getManagementToken();
1199
+ const email = String(args.email);
1200
+ await apiFetch(`${idp}/api/admin/users/${encodeURIComponent(email)}`, {
1201
+ method: "DELETE",
1202
+ token
1203
+ });
1204
+ consola14.success(`User deleted: ${email}`);
1205
+ }
1206
+ });
1207
+
1208
+ // src/commands/admin/ssh-keys.ts
1209
+ import { existsSync, readFileSync } from "fs";
1210
+ import { resolve } from "path";
1211
+ import { homedir } from "os";
1212
+ import { defineCommand as defineCommand17 } from "citty";
1213
+ import consola15 from "consola";
1214
+ function getManagementToken2() {
1215
+ const token = process.env.APES_MANAGEMENT_TOKEN;
1216
+ if (!token) {
1217
+ throw new CliError("Management token required. Set APES_MANAGEMENT_TOKEN environment variable.");
1218
+ }
1219
+ return token;
1220
+ }
1221
+ var sshKeysListCommand = defineCommand17({
1222
+ meta: {
1223
+ name: "list",
1224
+ description: "List SSH keys for a user"
1225
+ },
1226
+ args: {
1227
+ email: {
1228
+ type: "positional",
1229
+ description: "User email",
1230
+ required: true
1231
+ },
1232
+ json: {
1233
+ type: "boolean",
1234
+ description: "Output as JSON",
1235
+ default: false
1236
+ }
1237
+ },
1238
+ async run({ args }) {
1239
+ const idp = getIdpUrl();
1240
+ if (!idp) {
1241
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1242
+ }
1243
+ const token = getManagementToken2();
1244
+ const email = String(args.email);
1245
+ const keys = await apiFetch(
1246
+ `${idp}/api/admin/users/${encodeURIComponent(email)}/ssh-keys`,
1247
+ { token }
1248
+ );
1249
+ if (args.json) {
1250
+ console.log(JSON.stringify(keys, null, 2));
1251
+ return;
1252
+ }
1253
+ if (keys.length === 0) {
1254
+ consola15.info(`No SSH keys found for ${email}.`);
1255
+ return;
1256
+ }
1257
+ for (const k of keys) {
1258
+ console.log(`${k.keyId} ${k.name} ${k.publicKey.substring(0, 40)}...`);
1259
+ }
1260
+ }
1261
+ });
1262
+ var sshKeysAddCommand = defineCommand17({
1263
+ meta: {
1264
+ name: "add",
1265
+ description: "Add an SSH key for a user"
1266
+ },
1267
+ args: {
1268
+ email: {
1269
+ type: "string",
1270
+ description: "User email",
1271
+ required: true
1272
+ },
1273
+ key: {
1274
+ type: "string",
1275
+ description: "Path to public key file or key string",
1276
+ required: true
1277
+ },
1278
+ name: {
1279
+ type: "string",
1280
+ description: "Key name/label"
1281
+ }
1282
+ },
1283
+ async run({ args }) {
1284
+ const idp = getIdpUrl();
1285
+ if (!idp) {
1286
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1287
+ }
1288
+ const token = getManagementToken2();
1289
+ let publicKey = args.key;
1290
+ const resolved = resolve(args.key.replace(/^~/, homedir()));
1291
+ if (existsSync(resolved)) {
1292
+ publicKey = readFileSync(resolved, "utf-8").trim();
1293
+ }
1294
+ const body = { publicKey };
1295
+ if (args.name) {
1296
+ body.name = args.name;
1297
+ }
1298
+ const result = await apiFetch(
1299
+ `${idp}/api/admin/users/${encodeURIComponent(args.email)}/ssh-keys`,
1300
+ {
1301
+ method: "POST",
1302
+ body,
1303
+ token
1304
+ }
1305
+ );
1306
+ consola15.success(`SSH key added: ${result.keyId} (${result.name})`);
1307
+ }
1308
+ });
1309
+ var sshKeysDeleteCommand = defineCommand17({
1310
+ meta: {
1311
+ name: "delete",
1312
+ description: "Delete an SSH key"
1313
+ },
1314
+ args: {
1315
+ email: {
1316
+ type: "string",
1317
+ description: "User email",
1318
+ required: true
1319
+ },
1320
+ keyId: {
1321
+ type: "positional",
1322
+ description: "Key ID",
1323
+ required: true
1324
+ }
1325
+ },
1326
+ async run({ args }) {
1327
+ const idp = getIdpUrl();
1328
+ if (!idp) {
1329
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1330
+ }
1331
+ const token = getManagementToken2();
1332
+ const keyId = String(args.keyId);
1333
+ await apiFetch(
1334
+ `${idp}/api/admin/users/${encodeURIComponent(args.email)}/ssh-keys/${keyId}`,
1335
+ {
1336
+ method: "DELETE",
1337
+ token
1338
+ }
1339
+ );
1340
+ consola15.success(`SSH key deleted: ${keyId}`);
1341
+ }
1342
+ });
1343
+
1344
+ // src/commands/admin/index.ts
1345
+ var usersCommand = defineCommand18({
1346
+ meta: {
1347
+ name: "users",
1348
+ description: "Manage users"
1349
+ },
1350
+ subCommands: {
1351
+ list: usersListCommand,
1352
+ create: usersCreateCommand,
1353
+ delete: usersDeleteCommand
1354
+ }
1355
+ });
1356
+ var sshKeysCommand = defineCommand18({
1357
+ meta: {
1358
+ name: "ssh-keys",
1359
+ description: "Manage SSH keys"
1360
+ },
1361
+ subCommands: {
1362
+ list: sshKeysListCommand,
1363
+ add: sshKeysAddCommand,
1364
+ delete: sshKeysDeleteCommand
1365
+ }
1366
+ });
1367
+ var adminCommand = defineCommand18({
1368
+ meta: {
1369
+ name: "admin",
1370
+ description: "Admin commands (requires APES_MANAGEMENT_TOKEN)"
1371
+ },
1372
+ subCommands: {
1373
+ users: usersCommand,
1374
+ "ssh-keys": sshKeysCommand
1375
+ }
1376
+ });
1377
+
1378
+ // src/commands/adapter/index.ts
1379
+ import { defineCommand as defineCommand19 } from "citty";
1380
+ import consola16 from "consola";
1381
+ var adapterCommand = defineCommand19({
1023
1382
  meta: {
1024
1383
  name: "adapter",
1025
1384
  description: "Manage CLI adapters"
1026
1385
  },
1027
1386
  subCommands: {
1028
- list: defineCommand15({
1387
+ list: defineCommand19({
1029
1388
  meta: {
1030
1389
  name: "list",
1031
1390
  description: "List available adapters"
@@ -1056,7 +1415,7 @@ var adapterCommand = defineCommand15({
1056
1415
  `);
1057
1416
  return;
1058
1417
  }
1059
- consola13.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
1418
+ consola16.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
1060
1419
  for (const a of index2.adapters) {
1061
1420
  const installed = isInstalled(a.id, false) ? " [installed]" : "";
1062
1421
  console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
@@ -1067,7 +1426,7 @@ var adapterCommand = defineCommand15({
1067
1426
  const local = [];
1068
1427
  for (const a of index.adapters) {
1069
1428
  try {
1070
- const loaded = loadAdapter2(a.id);
1429
+ const loaded = loadAdapter(a.id);
1071
1430
  local.push({ id: a.id, source: loaded.source, digest: loaded.digest });
1072
1431
  } catch {
1073
1432
  }
@@ -1078,7 +1437,7 @@ var adapterCommand = defineCommand15({
1078
1437
  return;
1079
1438
  }
1080
1439
  if (local.length === 0) {
1081
- consola13.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
1440
+ consola16.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
1082
1441
  return;
1083
1442
  }
1084
1443
  for (const a of local) {
@@ -1086,7 +1445,7 @@ var adapterCommand = defineCommand15({
1086
1445
  }
1087
1446
  }
1088
1447
  }),
1089
- install: defineCommand15({
1448
+ install: defineCommand19({
1090
1449
  meta: {
1091
1450
  name: "install",
1092
1451
  description: "Install an adapter from the registry"
@@ -1115,24 +1474,24 @@ var adapterCommand = defineCommand15({
1115
1474
  for (const id of ids) {
1116
1475
  const entry = findAdapter(index, id);
1117
1476
  if (!entry) {
1118
- consola13.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
1477
+ consola16.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
1119
1478
  continue;
1120
1479
  }
1121
1480
  const conflicts = findConflictingAdapters(entry.executable, id);
1122
1481
  if (conflicts.length > 0) {
1123
1482
  for (const c of conflicts) {
1124
- consola13.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
1125
- consola13.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
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}`);
1126
1485
  }
1127
1486
  }
1128
1487
  const result = await installAdapter(entry, { local });
1129
1488
  const verb = result.updated ? "Updated" : "Installed";
1130
- consola13.success(`${verb} ${result.id} \u2192 ${result.path}`);
1131
- consola13.info(`Digest: ${result.digest}`);
1489
+ consola16.success(`${verb} ${result.id} \u2192 ${result.path}`);
1490
+ consola16.info(`Digest: ${result.digest}`);
1132
1491
  }
1133
1492
  }
1134
1493
  }),
1135
- remove: defineCommand15({
1494
+ remove: defineCommand19({
1136
1495
  meta: {
1137
1496
  name: "remove",
1138
1497
  description: "Remove an installed adapter"
@@ -1155,9 +1514,9 @@ var adapterCommand = defineCommand15({
1155
1514
  let failed = false;
1156
1515
  for (const id of ids) {
1157
1516
  if (removeAdapter(id, local)) {
1158
- consola13.success(`Removed adapter: ${id}`);
1517
+ consola16.success(`Removed adapter: ${id}`);
1159
1518
  } else {
1160
- consola13.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
1519
+ consola16.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
1161
1520
  failed = true;
1162
1521
  }
1163
1522
  }
@@ -1165,7 +1524,7 @@ var adapterCommand = defineCommand15({
1165
1524
  throw new CliError("Some adapters could not be removed");
1166
1525
  }
1167
1526
  }),
1168
- info: defineCommand15({
1527
+ info: defineCommand19({
1169
1528
  meta: {
1170
1529
  name: "info",
1171
1530
  description: "Show detailed adapter information"
@@ -1207,7 +1566,7 @@ var adapterCommand = defineCommand15({
1207
1566
  }
1208
1567
  }
1209
1568
  }),
1210
- search: defineCommand15({
1569
+ search: defineCommand19({
1211
1570
  meta: {
1212
1571
  name: "search",
1213
1572
  description: "Search adapters in the registry"
@@ -1239,7 +1598,7 @@ var adapterCommand = defineCommand15({
1239
1598
  return;
1240
1599
  }
1241
1600
  if (results.length === 0) {
1242
- consola13.info(`No adapters matching "${query}"`);
1601
+ consola16.info(`No adapters matching "${query}"`);
1243
1602
  return;
1244
1603
  }
1245
1604
  for (const a of results) {
@@ -1248,7 +1607,7 @@ var adapterCommand = defineCommand15({
1248
1607
  }
1249
1608
  }
1250
1609
  }),
1251
- update: defineCommand15({
1610
+ update: defineCommand19({
1252
1611
  meta: {
1253
1612
  name: "update",
1254
1613
  description: "Update installed adapters"
@@ -1274,33 +1633,33 @@ var adapterCommand = defineCommand15({
1274
1633
  const targetId = args.id ? String(args.id) : void 0;
1275
1634
  const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
1276
1635
  if (targets.length === 0) {
1277
- consola13.info("No adapters installed to update.");
1636
+ consola16.info("No adapters installed to update.");
1278
1637
  return;
1279
1638
  }
1280
1639
  for (const id of targets) {
1281
1640
  const entry = findAdapter(index, id);
1282
1641
  if (!entry) {
1283
- consola13.warn(`${id}: not found in registry, skipping`);
1642
+ consola16.warn(`${id}: not found in registry, skipping`);
1284
1643
  continue;
1285
1644
  }
1286
1645
  const localDigest = getInstalledDigest(id, false);
1287
1646
  if (localDigest === entry.digest) {
1288
- consola13.info(`${id}: already up to date`);
1647
+ consola16.info(`${id}: already up to date`);
1289
1648
  continue;
1290
1649
  }
1291
1650
  if (localDigest && !args.yes) {
1292
- consola13.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
1293
- consola13.info(` Old: ${localDigest}`);
1294
- consola13.info(` New: ${entry.digest}`);
1295
- consola13.info(" Use --yes to confirm");
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");
1296
1655
  continue;
1297
1656
  }
1298
1657
  const result = await installAdapter(entry);
1299
- consola13.success(`Updated ${result.id} \u2192 ${result.path}`);
1658
+ consola16.success(`Updated ${result.id} \u2192 ${result.path}`);
1300
1659
  }
1301
1660
  }
1302
1661
  }),
1303
- verify: defineCommand15({
1662
+ verify: defineCommand19({
1304
1663
  meta: {
1305
1664
  name: "verify",
1306
1665
  description: "Verify installed adapter against registry digest"
@@ -1333,7 +1692,7 @@ var adapterCommand = defineCommand15({
1333
1692
  if (!localDigest)
1334
1693
  throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
1335
1694
  if (localDigest === entry.digest) {
1336
- consola13.success(`${id}: digest matches registry`);
1695
+ consola16.success(`${id}: digest matches registry`);
1337
1696
  } else {
1338
1697
  console.log(` Local: ${localDigest}`);
1339
1698
  console.log(` Registry: ${entry.digest}`);
@@ -1347,20 +1706,9 @@ var adapterCommand = defineCommand15({
1347
1706
  // src/commands/run.ts
1348
1707
  import { execFileSync } from "child_process";
1349
1708
  import { hostname as hostname3 } from "os";
1350
- import { defineCommand as defineCommand16 } from "citty";
1351
- import {
1352
- createShapesGrant,
1353
- extractOption,
1354
- extractWrappedCommand,
1355
- fetchGrantToken,
1356
- findExistingGrant,
1357
- loadAdapter as loadAdapter3,
1358
- resolveCommand,
1359
- verifyAndExecute,
1360
- waitForGrantStatus
1361
- } from "@openape/shapes";
1362
- import consola14 from "consola";
1363
- var runCommand = defineCommand16({
1709
+ import { defineCommand as defineCommand20 } from "citty";
1710
+ import consola17 from "consola";
1711
+ var runCommand = defineCommand20({
1364
1712
  meta: {
1365
1713
  name: "run",
1366
1714
  description: "Execute a grant-secured command"
@@ -1396,6 +1744,11 @@ var runCommand = defineCommand16({
1396
1744
  type: "string",
1397
1745
  description: "IdP URL"
1398
1746
  },
1747
+ "shell": {
1748
+ type: "boolean",
1749
+ description: "Shell mode: use session grant with audience ape-shell",
1750
+ default: false
1751
+ },
1399
1752
  "_": {
1400
1753
  type: "positional",
1401
1754
  description: "Command to execute (after --)",
@@ -1404,6 +1757,10 @@ var runCommand = defineCommand16({
1404
1757
  },
1405
1758
  async run({ rawArgs, args }) {
1406
1759
  const wrappedCommand = extractWrappedCommand(rawArgs ?? []);
1760
+ if (args.shell && wrappedCommand.length > 0) {
1761
+ await runShellMode(wrappedCommand, args);
1762
+ return;
1763
+ }
1407
1764
  if (wrappedCommand.length > 0) {
1408
1765
  await runAdapterMode(wrappedCommand, rawArgs ?? [], args);
1409
1766
  } else {
@@ -1414,6 +1771,113 @@ var runCommand = defineCommand16({
1414
1771
  }
1415
1772
  }
1416
1773
  });
1774
+ async function runShellMode(command, args) {
1775
+ const auth = loadAuth();
1776
+ if (!auth)
1777
+ throw new CliError("Not logged in. Run `apes login` first.");
1778
+ const idp = getIdpUrl(args.idp);
1779
+ if (!idp)
1780
+ throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1781
+ const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
1782
+ if (adapterHandled) return;
1783
+ const grantsUrl = await getGrantsEndpoint(idp);
1784
+ const targetHost = args.host || hostname3();
1785
+ try {
1786
+ const grants = await apiFetch(
1787
+ `${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
1788
+ );
1789
+ const sessionGrant = grants.data.find(
1790
+ (g) => g.request.audience === "ape-shell" && g.request.target_host === targetHost && g.request.grant_type !== "once"
1791
+ );
1792
+ if (sessionGrant) {
1793
+ execShellCommand(command);
1794
+ return;
1795
+ }
1796
+ } catch {
1797
+ }
1798
+ consola17.info(`Requesting ape-shell session grant on ${targetHost}`);
1799
+ const grant = await apiFetch(grantsUrl, {
1800
+ method: "POST",
1801
+ body: {
1802
+ requester: auth.email,
1803
+ target_host: targetHost,
1804
+ audience: "ape-shell",
1805
+ grant_type: "once",
1806
+ command: command.slice(0, 3),
1807
+ reason: `Shell session: ${command.join(" ").slice(0, 100)}`
1808
+ }
1809
+ });
1810
+ consola17.info(`Grant requested: ${grant.id}`);
1811
+ consola17.info("Waiting for approval...");
1812
+ const maxWait = 3e5;
1813
+ const interval = 3e3;
1814
+ const start = Date.now();
1815
+ while (Date.now() - start < maxWait) {
1816
+ const status = await apiFetch(`${grantsUrl}/${grant.id}`);
1817
+ if (status.status === "approved")
1818
+ break;
1819
+ if (status.status === "denied" || status.status === "revoked")
1820
+ throw new CliError(`Grant ${status.status}.`);
1821
+ await new Promise((r) => setTimeout(r, interval));
1822
+ }
1823
+ execShellCommand(command);
1824
+ }
1825
+ async function tryAdapterModeFromShell(command, idp, args) {
1826
+ const cmdString = extractShellCommandString(command);
1827
+ if (!cmdString) return false;
1828
+ const parsed = parseShellCommand(cmdString);
1829
+ if (!parsed) return false;
1830
+ if (parsed.isCompound) return false;
1831
+ const loaded = await loadOrInstallAdapter(parsed.executable);
1832
+ if (!loaded) return false;
1833
+ let resolved;
1834
+ try {
1835
+ resolved = await resolveCommand(loaded, [parsed.executable, ...parsed.argv]);
1836
+ } catch (err) {
1837
+ consola17.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
1838
+ return false;
1839
+ }
1840
+ try {
1841
+ const existingGrantId = await findExistingGrant(resolved, idp);
1842
+ if (existingGrantId) {
1843
+ consola17.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
1844
+ const token2 = await fetchGrantToken(idp, existingGrantId);
1845
+ await verifyAndExecute(token2, resolved);
1846
+ return true;
1847
+ }
1848
+ } catch {
1849
+ }
1850
+ const approval = args.approval ?? "once";
1851
+ consola17.info(`Requesting grant for: ${resolved.detail.display}`);
1852
+ const grant = await createShapesGrant(resolved, {
1853
+ idp,
1854
+ approval,
1855
+ reason: args.reason || `ape-shell: ${resolved.detail.display}`
1856
+ });
1857
+ consola17.info(`Grant requested: ${grant.id}`);
1858
+ consola17.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
1859
+ if (grant.similar_grants?.similar_grants?.length) {
1860
+ 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.`);
1863
+ }
1864
+ const status = await waitForGrantStatus(idp, grant.id);
1865
+ if (status !== "approved")
1866
+ throw new CliError(`Grant ${status}`);
1867
+ const token = await fetchGrantToken(idp, grant.id);
1868
+ await verifyAndExecute(token, resolved);
1869
+ return true;
1870
+ }
1871
+ function execShellCommand(command) {
1872
+ if (command.length === 0)
1873
+ throw new CliError("No command to execute");
1874
+ try {
1875
+ execFileSync(command[0], command.slice(1), { stdio: "inherit" });
1876
+ } catch (err) {
1877
+ const exitCode = err.status || 1;
1878
+ throw new CliExit(exitCode);
1879
+ }
1880
+ }
1417
1881
  function extractPositionals(rawArgs) {
1418
1882
  const positionals = [];
1419
1883
  const delimiter = rawArgs.indexOf("--");
@@ -1439,13 +1903,13 @@ async function runAdapterMode(command, rawArgs, args) {
1439
1903
  return;
1440
1904
  }
1441
1905
  const adapterOpt = extractOption(rawArgs, "adapter");
1442
- const loaded = loadAdapter3(command[0], adapterOpt);
1906
+ const loaded = loadAdapter(command[0], adapterOpt);
1443
1907
  const resolved = await resolveCommand(loaded, command);
1444
1908
  const approval = args.approval ?? "once";
1445
1909
  try {
1446
1910
  const existingGrantId = await findExistingGrant(resolved, idp);
1447
1911
  if (existingGrantId) {
1448
- consola14.info(`Reusing existing grant: ${existingGrantId}`);
1912
+ consola17.info(`Reusing existing grant: ${existingGrantId}`);
1449
1913
  const token2 = await fetchGrantToken(idp, existingGrantId);
1450
1914
  await verifyAndExecute(token2, resolved);
1451
1915
  return;
@@ -1457,17 +1921,17 @@ async function runAdapterMode(command, rawArgs, args) {
1457
1921
  approval,
1458
1922
  ...args.reason ? { reason: args.reason } : {}
1459
1923
  });
1460
- consola14.info(`Grant requested: ${grant.id}`);
1461
- consola14.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
1924
+ consola17.info(`Grant requested: ${grant.id}`);
1925
+ consola17.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
1462
1926
  if (grant.similar_grants?.similar_grants?.length) {
1463
1927
  const n = grant.similar_grants.similar_grants.length;
1464
- consola14.info("");
1465
- consola14.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
1928
+ consola17.info("");
1929
+ consola17.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
1466
1930
  if (grant.similar_grants.widened_details?.length) {
1467
1931
  const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
1468
- consola14.info(` Broader scope: ${wider}`);
1932
+ consola17.info(` Broader scope: ${wider}`);
1469
1933
  }
1470
- consola14.info("");
1934
+ consola17.info("");
1471
1935
  }
1472
1936
  const status = await waitForGrantStatus(idp, grant.id);
1473
1937
  if (status !== "approved")
@@ -1484,7 +1948,7 @@ async function runAudienceMode(audience, action, args) {
1484
1948
  const grantsUrl = await getGrantsEndpoint(idp);
1485
1949
  const command = action.split(" ");
1486
1950
  const targetHost = args.host || hostname3();
1487
- consola14.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
1951
+ consola17.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
1488
1952
  const grant = await apiFetch(grantsUrl, {
1489
1953
  method: "POST",
1490
1954
  body: {
@@ -1497,15 +1961,15 @@ async function runAudienceMode(audience, action, args) {
1497
1961
  ...args.as ? { run_as: args.as } : {}
1498
1962
  }
1499
1963
  });
1500
- consola14.success(`Grant requested: ${grant.id}`);
1501
- consola14.info("Waiting for approval...");
1964
+ consola17.success(`Grant requested: ${grant.id}`);
1965
+ consola17.info("Waiting for approval...");
1502
1966
  const maxWait = 3e5;
1503
1967
  const interval = 3e3;
1504
1968
  const start = Date.now();
1505
1969
  while (Date.now() - start < maxWait) {
1506
1970
  const status = await apiFetch(`${grantsUrl}/${grant.id}`);
1507
1971
  if (status.status === "approved") {
1508
- consola14.success("Grant approved!");
1972
+ consola17.success("Grant approved!");
1509
1973
  break;
1510
1974
  }
1511
1975
  if (status.status === "denied" || status.status === "revoked") {
@@ -1513,12 +1977,12 @@ async function runAudienceMode(audience, action, args) {
1513
1977
  }
1514
1978
  await new Promise((r) => setTimeout(r, interval));
1515
1979
  }
1516
- consola14.info("Fetching grant token...");
1980
+ consola17.info("Fetching grant token...");
1517
1981
  const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
1518
1982
  method: "POST"
1519
1983
  });
1520
1984
  if (audience === "escapes") {
1521
- consola14.info(`Executing: ${command.join(" ")}`);
1985
+ consola17.info(`Executing: ${command.join(" ")}`);
1522
1986
  try {
1523
1987
  execFileSync(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
1524
1988
  stdio: "inherit"
@@ -1533,9 +1997,8 @@ async function runAudienceMode(audience, action, args) {
1533
1997
  }
1534
1998
 
1535
1999
  // src/commands/explain.ts
1536
- import { defineCommand as defineCommand17 } from "citty";
1537
- import { extractOption as extractOption2, extractWrappedCommand as extractWrappedCommand2, loadAdapter as loadAdapter4, resolveCommand as resolveCommand2 } from "@openape/shapes";
1538
- var explainCommand = defineCommand17({
2000
+ import { defineCommand as defineCommand21 } from "citty";
2001
+ var explainCommand = defineCommand21({
1539
2002
  meta: {
1540
2003
  name: "explain",
1541
2004
  description: "Show what permission a command would need"
@@ -1552,12 +2015,12 @@ var explainCommand = defineCommand17({
1552
2015
  }
1553
2016
  },
1554
2017
  async run({ rawArgs }) {
1555
- const command = extractWrappedCommand2(rawArgs ?? []);
2018
+ const command = extractWrappedCommand(rawArgs ?? []);
1556
2019
  if (command.length === 0)
1557
2020
  throw new Error("Missing wrapped command. Usage: apes explain [--adapter <file>] -- <cli> ...");
1558
- const adapterOpt = extractOption2(rawArgs ?? [], "adapter");
1559
- const loaded = loadAdapter4(command[0], adapterOpt);
1560
- const resolved = await resolveCommand2(loaded, command);
2021
+ const adapterOpt = extractOption(rawArgs ?? [], "adapter");
2022
+ const loaded = loadAdapter(command[0], adapterOpt);
2023
+ const resolved = await resolveCommand(loaded, command);
1561
2024
  process.stdout.write(`${JSON.stringify({
1562
2025
  adapter: resolved.adapter.cli.id,
1563
2026
  source: resolved.source,
@@ -1573,9 +2036,9 @@ var explainCommand = defineCommand17({
1573
2036
  });
1574
2037
 
1575
2038
  // src/commands/config/get.ts
1576
- import { defineCommand as defineCommand18 } from "citty";
1577
- import consola15 from "consola";
1578
- var configGetCommand = defineCommand18({
2039
+ import { defineCommand as defineCommand22 } from "citty";
2040
+ import consola18 from "consola";
2041
+ var configGetCommand = defineCommand22({
1579
2042
  meta: {
1580
2043
  name: "get",
1581
2044
  description: "Get a configuration value"
@@ -1595,7 +2058,7 @@ var configGetCommand = defineCommand18({
1595
2058
  if (idp)
1596
2059
  console.log(idp);
1597
2060
  else
1598
- consola15.info("No IdP configured.");
2061
+ consola18.info("No IdP configured.");
1599
2062
  break;
1600
2063
  }
1601
2064
  case "email": {
@@ -1603,7 +2066,7 @@ var configGetCommand = defineCommand18({
1603
2066
  if (auth?.email)
1604
2067
  console.log(auth.email);
1605
2068
  else
1606
- consola15.info("Not logged in.");
2069
+ consola18.info("Not logged in.");
1607
2070
  break;
1608
2071
  }
1609
2072
  default: {
@@ -1616,7 +2079,7 @@ var configGetCommand = defineCommand18({
1616
2079
  if (sectionObj && field in sectionObj) {
1617
2080
  console.log(sectionObj[field]);
1618
2081
  } else {
1619
- consola15.info(`Key "${key}" not set.`);
2082
+ consola18.info(`Key "${key}" not set.`);
1620
2083
  }
1621
2084
  } else {
1622
2085
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -1627,9 +2090,9 @@ var configGetCommand = defineCommand18({
1627
2090
  });
1628
2091
 
1629
2092
  // src/commands/config/set.ts
1630
- import { defineCommand as defineCommand19 } from "citty";
1631
- import consola16 from "consola";
1632
- var configSetCommand = defineCommand19({
2093
+ import { defineCommand as defineCommand23 } from "citty";
2094
+ import consola19 from "consola";
2095
+ var configSetCommand = defineCommand23({
1633
2096
  meta: {
1634
2097
  name: "set",
1635
2098
  description: "Set a configuration value"
@@ -1665,12 +2128,12 @@ var configSetCommand = defineCommand19({
1665
2128
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
1666
2129
  }
1667
2130
  saveConfig(config);
1668
- consola16.success(`Set ${key} = ${value}`);
2131
+ consola19.success(`Set ${key} = ${value}`);
1669
2132
  }
1670
2133
  });
1671
2134
 
1672
2135
  // src/commands/fetch/index.ts
1673
- import { defineCommand as defineCommand20 } from "citty";
2136
+ import { defineCommand as defineCommand24 } from "citty";
1674
2137
  async function doRequest(method, url, body, contentType, raw, showHeaders) {
1675
2138
  const token = getAuthToken();
1676
2139
  if (!token) {
@@ -1706,13 +2169,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
1706
2169
  throw new CliError(`HTTP ${response.status} ${response.statusText}`);
1707
2170
  }
1708
2171
  }
1709
- var fetchCommand = defineCommand20({
2172
+ var fetchCommand = defineCommand24({
1710
2173
  meta: {
1711
2174
  name: "fetch",
1712
2175
  description: "Make authenticated HTTP requests"
1713
2176
  },
1714
2177
  subCommands: {
1715
- get: defineCommand20({
2178
+ get: defineCommand24({
1716
2179
  meta: {
1717
2180
  name: "get",
1718
2181
  description: "GET request with auth token"
@@ -1738,7 +2201,7 @@ var fetchCommand = defineCommand20({
1738
2201
  await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
1739
2202
  }
1740
2203
  }),
1741
- post: defineCommand20({
2204
+ post: defineCommand24({
1742
2205
  meta: {
1743
2206
  name: "post",
1744
2207
  description: "POST request with auth token"
@@ -1777,8 +2240,8 @@ var fetchCommand = defineCommand20({
1777
2240
  });
1778
2241
 
1779
2242
  // src/commands/mcp/index.ts
1780
- import { defineCommand as defineCommand21 } from "citty";
1781
- var mcpCommand = defineCommand21({
2243
+ import { defineCommand as defineCommand25 } from "citty";
2244
+ var mcpCommand = defineCommand25({
1782
2245
  meta: {
1783
2246
  name: "mcp",
1784
2247
  description: "Start MCP server for AI agents"
@@ -1801,25 +2264,25 @@ var mcpCommand = defineCommand21({
1801
2264
  if (transport !== "stdio" && transport !== "sse") {
1802
2265
  throw new Error('Transport must be "stdio" or "sse"');
1803
2266
  }
1804
- const { startMcpServer } = await import("./server-IYR5LM63.js");
2267
+ const { startMcpServer } = await import("./server-FR6GFS3S.js");
1805
2268
  await startMcpServer(transport, port);
1806
2269
  }
1807
2270
  });
1808
2271
 
1809
2272
  // src/commands/init/index.ts
1810
- import { existsSync, copyFileSync, writeFileSync } from "fs";
2273
+ import { existsSync as existsSync2, copyFileSync, writeFileSync } from "fs";
1811
2274
  import { randomBytes } from "crypto";
1812
2275
  import { execFileSync as execFileSync2 } from "child_process";
1813
2276
  import { join } from "path";
1814
- import { defineCommand as defineCommand22 } from "citty";
1815
- import consola17 from "consola";
2277
+ import { defineCommand as defineCommand26 } from "citty";
2278
+ import consola20 from "consola";
1816
2279
  var DEFAULT_IDP_URL = "https://id.openape.at";
1817
2280
  async function downloadTemplate(repo, targetDir) {
1818
2281
  const { downloadTemplate: gigetDownload } = await import("giget");
1819
2282
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
1820
2283
  }
1821
2284
  function installDeps(dir) {
1822
- const hasLockFile = (name) => existsSync(join(dir, name));
2285
+ const hasLockFile = (name) => existsSync2(join(dir, name));
1823
2286
  if (hasLockFile("pnpm-lock.yaml")) {
1824
2287
  execFileSync2("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
1825
2288
  } else if (hasLockFile("bun.lockb")) {
@@ -1829,20 +2292,20 @@ function installDeps(dir) {
1829
2292
  }
1830
2293
  }
1831
2294
  async function promptChoice(message, choices) {
1832
- const result = await consola17.prompt(message, { type: "select", options: choices });
2295
+ const result = await consola20.prompt(message, { type: "select", options: choices });
1833
2296
  if (typeof result === "symbol") {
1834
2297
  throw new CliExit(0);
1835
2298
  }
1836
2299
  return result;
1837
2300
  }
1838
2301
  async function promptText(message, defaultValue) {
1839
- const result = await consola17.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
2302
+ const result = await consola20.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
1840
2303
  if (typeof result === "symbol") {
1841
2304
  throw new CliExit(0);
1842
2305
  }
1843
2306
  return result || defaultValue || "";
1844
2307
  }
1845
- var initCommand = defineCommand22({
2308
+ var initCommand = defineCommand26({
1846
2309
  meta: {
1847
2310
  name: "init",
1848
2311
  description: "Scaffold a new OpenApe project"
@@ -1884,23 +2347,23 @@ var initCommand = defineCommand22({
1884
2347
  });
1885
2348
  async function initSP(targetDir) {
1886
2349
  const dir = targetDir || "my-app";
1887
- if (existsSync(join(dir, "package.json"))) {
2350
+ if (existsSync2(join(dir, "package.json"))) {
1888
2351
  throw new CliError(`Directory "${dir}" already contains a project.`);
1889
2352
  }
1890
- consola17.start("Scaffolding SP starter...");
2353
+ consola20.start("Scaffolding SP starter...");
1891
2354
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
1892
- consola17.success("Scaffolded from openape-sp-starter");
1893
- consola17.start("Installing dependencies...");
2355
+ consola20.success("Scaffolded from openape-sp-starter");
2356
+ consola20.start("Installing dependencies...");
1894
2357
  installDeps(dir);
1895
- consola17.success("Dependencies installed");
2358
+ consola20.success("Dependencies installed");
1896
2359
  const envExample = join(dir, ".env.example");
1897
2360
  const envFile = join(dir, ".env");
1898
- if (existsSync(envExample) && !existsSync(envFile)) {
2361
+ if (existsSync2(envExample) && !existsSync2(envFile)) {
1899
2362
  copyFileSync(envExample, envFile);
1900
- consola17.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
2363
+ consola20.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
1901
2364
  }
1902
2365
  console.log("");
1903
- consola17.box([
2366
+ consola20.box([
1904
2367
  `cd ${dir}`,
1905
2368
  "npm run dev",
1906
2369
  "",
@@ -1909,7 +2372,7 @@ async function initSP(targetDir) {
1909
2372
  }
1910
2373
  async function initIdP(targetDir) {
1911
2374
  const dir = targetDir || "my-idp";
1912
- if (existsSync(join(dir, "package.json"))) {
2375
+ if (existsSync2(join(dir, "package.json"))) {
1913
2376
  throw new CliError(`Directory "${dir}" already contains a project.`);
1914
2377
  }
1915
2378
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -1919,15 +2382,15 @@ async function initIdP(targetDir) {
1919
2382
  "s3 (S3-compatible)"
1920
2383
  ]);
1921
2384
  const adminEmail = await promptText("Admin email");
1922
- consola17.start("Scaffolding IdP starter...");
2385
+ consola20.start("Scaffolding IdP starter...");
1923
2386
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
1924
- consola17.success("Scaffolded from openape-idp-starter");
1925
- consola17.start("Installing dependencies...");
2387
+ consola20.success("Scaffolded from openape-idp-starter");
2388
+ consola20.start("Installing dependencies...");
1926
2389
  installDeps(dir);
1927
- consola17.success("Dependencies installed");
2390
+ consola20.success("Dependencies installed");
1928
2391
  const sessionSecret = randomBytes(32).toString("hex");
1929
2392
  const managementToken = randomBytes(32).toString("hex");
1930
- consola17.success("Secrets generated");
2393
+ consola20.success("Secrets generated");
1931
2394
  const isLocalhost = domain === "localhost";
1932
2395
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
1933
2396
  const envContent = [
@@ -1943,9 +2406,9 @@ async function initIdP(targetDir) {
1943
2406
  ].join("\n");
1944
2407
  writeFileSync(join(dir, ".env"), `${envContent}
1945
2408
  `, { mode: 384 });
1946
- consola17.success(".env created");
2409
+ consola20.success(".env created");
1947
2410
  console.log("");
1948
- consola17.box([
2411
+ consola20.box([
1949
2412
  `cd ${dir}`,
1950
2413
  "npm run dev",
1951
2414
  "",
@@ -1962,19 +2425,19 @@ async function initIdP(targetDir) {
1962
2425
 
1963
2426
  // src/commands/enroll.ts
1964
2427
  import { Buffer as Buffer2 } from "buffer";
1965
- import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2, mkdirSync } from "fs";
2428
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
1966
2429
  import { execFile as execFile2 } from "child_process";
1967
2430
  import { generateKeyPairSync, sign } from "crypto";
1968
- import { dirname, resolve } from "path";
1969
- import { homedir } from "os";
1970
- import { defineCommand as defineCommand23 } from "citty";
1971
- import consola18 from "consola";
2431
+ import { dirname, resolve as resolve2 } from "path";
2432
+ import { homedir as homedir2 } from "os";
2433
+ import { defineCommand as defineCommand27 } from "citty";
2434
+ import consola21 from "consola";
1972
2435
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
1973
2436
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
1974
2437
  var POLL_INTERVAL = 3e3;
1975
2438
  var POLL_TIMEOUT = 3e5;
1976
2439
  function resolvePath(p) {
1977
- return resolve(p.replace(/^~/, homedir()));
2440
+ return resolve2(p.replace(/^~/, homedir2()));
1978
2441
  }
1979
2442
  function openBrowser2(url) {
1980
2443
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -1983,10 +2446,10 @@ function openBrowser2(url) {
1983
2446
  }
1984
2447
  function readPublicKey(keyPath) {
1985
2448
  const pubPath = `${keyPath}.pub`;
1986
- if (existsSync2(pubPath)) {
1987
- return readFileSync(pubPath, "utf-8").trim();
2449
+ if (existsSync3(pubPath)) {
2450
+ return readFileSync2(pubPath, "utf-8").trim();
1988
2451
  }
1989
- const keyContent = readFileSync(keyPath, "utf-8");
2452
+ const keyContent = readFileSync2(keyPath, "utf-8");
1990
2453
  const privateKey = loadEd25519PrivateKey(keyContent);
1991
2454
  const jwk = privateKey.export({ format: "jwk" });
1992
2455
  const pubBytes = Buffer2.from(jwk.x, "base64url");
@@ -2001,7 +2464,7 @@ function readPublicKey(keyPath) {
2001
2464
  function generateAndSaveKey(keyPath) {
2002
2465
  const resolved = resolvePath(keyPath);
2003
2466
  const dir = dirname(resolved);
2004
- if (!existsSync2(dir)) {
2467
+ if (!existsSync3(dir)) {
2005
2468
  mkdirSync(dir, { recursive: true });
2006
2469
  }
2007
2470
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
@@ -2022,7 +2485,7 @@ function generateAndSaveKey(keyPath) {
2022
2485
  }
2023
2486
  async function pollForEnrollment(idp, agentEmail, keyPath) {
2024
2487
  const resolvedKey = resolvePath(keyPath);
2025
- const keyContent = readFileSync(resolvedKey, "utf-8");
2488
+ const keyContent = readFileSync2(resolvedKey, "utf-8");
2026
2489
  const privateKey = loadEd25519PrivateKey(keyContent);
2027
2490
  const challengeUrl = await getAgentChallengeEndpoint(idp);
2028
2491
  const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
@@ -2049,11 +2512,11 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
2049
2512
  }
2050
2513
  } catch {
2051
2514
  }
2052
- await new Promise((resolve2) => setTimeout(resolve2, POLL_INTERVAL));
2515
+ await new Promise((resolve3) => setTimeout(resolve3, POLL_INTERVAL));
2053
2516
  }
2054
2517
  throw new Error("Enrollment timed out. Please check the browser and try again.");
2055
2518
  }
2056
- var enrollCommand = defineCommand23({
2519
+ var enrollCommand = defineCommand27({
2057
2520
  meta: {
2058
2521
  name: "enroll",
2059
2522
  description: "Enroll an agent with an Identity Provider"
@@ -2073,38 +2536,38 @@ var enrollCommand = defineCommand23({
2073
2536
  }
2074
2537
  },
2075
2538
  async run({ args }) {
2076
- const idp = args.idp || await consola18.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
2539
+ const idp = args.idp || await consola21.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
2077
2540
  if (typeof r === "symbol") throw new CliExit(0);
2078
2541
  return r;
2079
2542
  }) || DEFAULT_IDP_URL2;
2080
- const agentName = args.name || await consola18.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
2543
+ const agentName = args.name || await consola21.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
2081
2544
  if (typeof r === "symbol") throw new CliExit(0);
2082
2545
  return r;
2083
2546
  });
2084
2547
  if (!agentName) {
2085
2548
  throw new CliError("Agent name is required.");
2086
2549
  }
2087
- const keyPath = args.key || await consola18.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
2550
+ const keyPath = args.key || await consola21.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
2088
2551
  if (typeof r === "symbol") throw new CliExit(0);
2089
2552
  return r;
2090
2553
  }) || DEFAULT_KEY_PATH;
2091
2554
  const resolvedKey = resolvePath(keyPath);
2092
2555
  let publicKey;
2093
- if (existsSync2(resolvedKey)) {
2556
+ if (existsSync3(resolvedKey)) {
2094
2557
  publicKey = readPublicKey(resolvedKey);
2095
- consola18.success(`Using existing key ${keyPath}`);
2558
+ consola21.success(`Using existing key ${keyPath}`);
2096
2559
  } else {
2097
- consola18.start(`Generating Ed25519 key pair at ${keyPath}...`);
2560
+ consola21.start(`Generating Ed25519 key pair at ${keyPath}...`);
2098
2561
  publicKey = generateAndSaveKey(keyPath);
2099
- consola18.success(`Key pair generated at ${keyPath}`);
2562
+ consola21.success(`Key pair generated at ${keyPath}`);
2100
2563
  }
2101
2564
  const encodedKey = encodeURIComponent(publicKey);
2102
2565
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
2103
- consola18.info("Opening browser for enrollment...");
2104
- consola18.info(`\u2192 ${idp}/enroll`);
2566
+ consola21.info("Opening browser for enrollment...");
2567
+ consola21.info(`\u2192 ${idp}/enroll`);
2105
2568
  openBrowser2(enrollUrl);
2106
2569
  console.log("");
2107
- const agentEmail = await consola18.prompt(
2570
+ const agentEmail = await consola21.prompt(
2108
2571
  "Agent email (shown in browser after enrollment)",
2109
2572
  { type: "text", placeholder: `agent+${agentName}@...` }
2110
2573
  ).then((r) => {
@@ -2114,7 +2577,7 @@ var enrollCommand = defineCommand23({
2114
2577
  if (!agentEmail) {
2115
2578
  throw new CliError("Agent email is required to verify enrollment.");
2116
2579
  }
2117
- consola18.start("Verifying enrollment...");
2580
+ consola21.start("Verifying enrollment...");
2118
2581
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
2119
2582
  saveAuth({
2120
2583
  idp,
@@ -2126,18 +2589,81 @@ var enrollCommand = defineCommand23({
2126
2589
  config.defaults = { ...config.defaults, idp };
2127
2590
  config.agent = { key: keyPath, email: agentEmail };
2128
2591
  saveConfig(config);
2129
- consola18.success(`Agent enrolled as ${agentEmail}`);
2130
- consola18.success("Config saved to ~/.config/apes/");
2592
+ consola21.success(`Agent enrolled as ${agentEmail}`);
2593
+ consola21.success("Config saved to ~/.config/apes/");
2131
2594
  console.log("");
2132
- consola18.info("Verify with: apes whoami");
2595
+ consola21.info("Verify with: apes whoami");
2596
+ }
2597
+ });
2598
+
2599
+ // src/commands/register-user.ts
2600
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
2601
+ import { defineCommand as defineCommand28 } from "citty";
2602
+ import consola22 from "consola";
2603
+ var registerUserCommand = defineCommand28({
2604
+ meta: {
2605
+ name: "register-user",
2606
+ description: "Register a sub-user with SSH key"
2607
+ },
2608
+ args: {
2609
+ email: {
2610
+ type: "string",
2611
+ description: "Email for the new user",
2612
+ required: true
2613
+ },
2614
+ name: {
2615
+ type: "string",
2616
+ description: "Name for the new user",
2617
+ required: true
2618
+ },
2619
+ key: {
2620
+ type: "string",
2621
+ description: "Path to SSH public key file or key string",
2622
+ required: true
2623
+ },
2624
+ type: {
2625
+ type: "string",
2626
+ description: "User type: human or agent (default: agent)"
2627
+ }
2628
+ },
2629
+ async run({ args }) {
2630
+ const auth = loadAuth();
2631
+ if (!auth) {
2632
+ throw new CliError("Not authenticated. Run `apes login` first.");
2633
+ }
2634
+ const idp = getIdpUrl();
2635
+ if (!idp) {
2636
+ throw new CliError("No IdP URL configured. Run `apes login` first.");
2637
+ }
2638
+ let publicKey = args.key;
2639
+ if (existsSync4(args.key)) {
2640
+ publicKey = readFileSync3(args.key, "utf-8").trim();
2641
+ }
2642
+ if (!publicKey.startsWith("ssh-ed25519 ")) {
2643
+ throw new CliError("Public key must be in ssh-ed25519 format.");
2644
+ }
2645
+ const userType = args.type;
2646
+ if (userType && userType !== "human" && userType !== "agent") {
2647
+ throw new CliError('Type must be "human" or "agent".');
2648
+ }
2649
+ const result = await apiFetch(`${idp}/api/auth/enroll`, {
2650
+ method: "POST",
2651
+ body: {
2652
+ email: args.email,
2653
+ name: args.name,
2654
+ publicKey,
2655
+ ...userType ? { type: userType } : {}
2656
+ }
2657
+ });
2658
+ consola22.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
2133
2659
  }
2134
2660
  });
2135
2661
 
2136
2662
  // src/commands/dns-check.ts
2137
- import { defineCommand as defineCommand24 } from "citty";
2138
- import consola19 from "consola";
2663
+ import { defineCommand as defineCommand29 } from "citty";
2664
+ import consola23 from "consola";
2139
2665
  import { resolveDDISA } from "@openape/core";
2140
- var dnsCheckCommand = defineCommand24({
2666
+ var dnsCheckCommand = defineCommand29({
2141
2667
  meta: {
2142
2668
  name: "dns-check",
2143
2669
  description: "Validate DDISA DNS TXT records for a domain"
@@ -2151,7 +2677,7 @@ var dnsCheckCommand = defineCommand24({
2151
2677
  },
2152
2678
  async run({ args }) {
2153
2679
  const domain = args.domain;
2154
- consola19.start(`Checking _ddisa.${domain}...`);
2680
+ consola23.start(`Checking _ddisa.${domain}...`);
2155
2681
  try {
2156
2682
  const result = await resolveDDISA(domain);
2157
2683
  if (!result) {
@@ -2160,7 +2686,7 @@ var dnsCheckCommand = defineCommand24({
2160
2686
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
2161
2687
  throw new CliError(`No DDISA record found for ${domain}`);
2162
2688
  }
2163
- consola19.success(`_ddisa.${domain} \u2192 ${result.idp}`);
2689
+ consola23.success(`_ddisa.${domain} \u2192 ${result.idp}`);
2164
2690
  console.log("");
2165
2691
  console.log(` Version: ${result.version || "ddisa1"}`);
2166
2692
  console.log(` IdP URL: ${result.idp}`);
@@ -2169,14 +2695,14 @@ var dnsCheckCommand = defineCommand24({
2169
2695
  if (result.priority !== void 0)
2170
2696
  console.log(` Priority: ${result.priority}`);
2171
2697
  console.log("");
2172
- consola19.start(`Verifying IdP at ${result.idp}...`);
2698
+ consola23.start(`Verifying IdP at ${result.idp}...`);
2173
2699
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
2174
2700
  if (!discoResp.ok) {
2175
- consola19.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
2701
+ consola23.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
2176
2702
  return;
2177
2703
  }
2178
2704
  const disco = await discoResp.json();
2179
- consola19.success(`IdP is reachable`);
2705
+ consola23.success(`IdP is reachable`);
2180
2706
  console.log(` Issuer: ${disco.issuer}`);
2181
2707
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
2182
2708
  if (disco.ddisa_auth_methods_supported) {
@@ -2192,8 +2718,8 @@ var dnsCheckCommand = defineCommand24({
2192
2718
  });
2193
2719
 
2194
2720
  // src/commands/workflows.ts
2195
- import { defineCommand as defineCommand25 } from "citty";
2196
- import consola20 from "consola";
2721
+ import { defineCommand as defineCommand30 } from "citty";
2722
+ import consola24 from "consola";
2197
2723
 
2198
2724
  // src/guides/index.ts
2199
2725
  var guides = [
@@ -2243,7 +2769,7 @@ var guides = [
2243
2769
  ];
2244
2770
 
2245
2771
  // src/commands/workflows.ts
2246
- var workflowsCommand = defineCommand25({
2772
+ var workflowsCommand = defineCommand30({
2247
2773
  meta: {
2248
2774
  name: "workflows",
2249
2775
  description: "Discover workflow guides"
@@ -2264,7 +2790,7 @@ var workflowsCommand = defineCommand25({
2264
2790
  if (args.id) {
2265
2791
  const guide = guides.find((g) => g.id === String(args.id));
2266
2792
  if (!guide) {
2267
- consola20.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
2793
+ consola24.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
2268
2794
  throw new CliError(`Guide not found: ${args.id}`);
2269
2795
  }
2270
2796
  if (args.json) {
@@ -2308,8 +2834,24 @@ process.stdout.on("error", (err) => {
2308
2834
  if (err.code === "EPIPE") process.exit(0);
2309
2835
  throw err;
2310
2836
  });
2837
+ var shellRewrite = rewriteApeShellArgs(process.argv);
2838
+ if (shellRewrite) {
2839
+ if (shellRewrite.action === "rewrite") {
2840
+ process.argv = shellRewrite.argv;
2841
+ } else if (shellRewrite.action === "version") {
2842
+ console.log(`ape-shell (OpenApe DDISA shell wrapper)`);
2843
+ process.exit(0);
2844
+ } 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.");
2847
+ process.exit(0);
2848
+ } else {
2849
+ console.error("ape-shell: only -c <command> mode is supported");
2850
+ process.exit(1);
2851
+ }
2852
+ }
2311
2853
  var debug = process.argv.includes("--debug");
2312
- var grantsCommand = defineCommand26({
2854
+ var grantsCommand = defineCommand31({
2313
2855
  meta: {
2314
2856
  name: "grants",
2315
2857
  description: "Grant management"
@@ -2325,10 +2867,11 @@ var grantsCommand = defineCommand26({
2325
2867
  revoke: revokeCommand,
2326
2868
  token: tokenCommand,
2327
2869
  delegate: delegateCommand,
2328
- delegations: delegationsCommand
2870
+ delegations: delegationsCommand,
2871
+ "delegation-revoke": delegationRevokeCommand
2329
2872
  }
2330
2873
  });
2331
- var configCommand = defineCommand26({
2874
+ var configCommand = defineCommand31({
2332
2875
  meta: {
2333
2876
  name: "config",
2334
2877
  description: "Configuration management"
@@ -2338,20 +2881,22 @@ var configCommand = defineCommand26({
2338
2881
  set: configSetCommand
2339
2882
  }
2340
2883
  });
2341
- var main = defineCommand26({
2884
+ var main = defineCommand31({
2342
2885
  meta: {
2343
2886
  name: "apes",
2344
- version: "0.6.0",
2887
+ version: "0.6.1",
2345
2888
  description: "Unified CLI for OpenApe"
2346
2889
  },
2347
2890
  subCommands: {
2348
2891
  init: initCommand,
2349
2892
  enroll: enrollCommand,
2893
+ "register-user": registerUserCommand,
2350
2894
  "dns-check": dnsCheckCommand,
2351
2895
  login: loginCommand,
2352
2896
  logout: logoutCommand,
2353
2897
  whoami: whoamiCommand,
2354
2898
  grants: grantsCommand,
2899
+ admin: adminCommand,
2355
2900
  run: runCommand,
2356
2901
  explain: explainCommand,
2357
2902
  adapter: adapterCommand,
@@ -2366,13 +2911,13 @@ runMain(main).catch((err) => {
2366
2911
  process.exit(err.exitCode);
2367
2912
  }
2368
2913
  if (err instanceof CliError) {
2369
- consola21.error(err.message);
2914
+ consola25.error(err.message);
2370
2915
  process.exit(err.exitCode);
2371
2916
  }
2372
2917
  if (debug) {
2373
- consola21.error(err);
2918
+ consola25.error(err);
2374
2919
  } else {
2375
- consola21.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
2920
+ consola25.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
2376
2921
  }
2377
2922
  process.exit(1);
2378
2923
  });