@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/README.md +144 -0
- package/dist/chunk-G3Q2TMAI.js +1331 -0
- package/dist/chunk-G3Q2TMAI.js.map +1 -0
- package/dist/cli.js +719 -174
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +207 -2
- package/dist/index.js +28 -22
- package/dist/index.js.map +1 -1
- package/dist/{server-IYR5LM63.js → server-FR6GFS3S.js} +11 -14
- package/dist/server-FR6GFS3S.js.map +1 -0
- package/package.json +12 -8
- package/dist/chunk-KXESKY4X.js +0 -278
- package/dist/chunk-KXESKY4X.js.map +0 -1
- package/dist/server-IYR5LM63.js.map +0 -1
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
|
-
|
|
42
|
+
saveConfig,
|
|
43
|
+
searchAdapters,
|
|
44
|
+
verifyAndExecute,
|
|
45
|
+
waitForGrantStatus
|
|
46
|
+
} from "./chunk-G3Q2TMAI.js";
|
|
25
47
|
|
|
26
48
|
// src/cli.ts
|
|
27
|
-
import
|
|
28
|
-
|
|
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((
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
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}
|
|
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((
|
|
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
|
|
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/
|
|
1049
|
+
// src/commands/grants/delegation-revoke.ts
|
|
1009
1050
|
import { defineCommand as defineCommand15 } from "citty";
|
|
1010
1051
|
import consola13 from "consola";
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1022
|
-
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
1125
|
-
|
|
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
|
-
|
|
1131
|
-
|
|
1489
|
+
consola16.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
1490
|
+
consola16.info(`Digest: ${result.digest}`);
|
|
1132
1491
|
}
|
|
1133
1492
|
}
|
|
1134
1493
|
}),
|
|
1135
|
-
remove:
|
|
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
|
-
|
|
1517
|
+
consola16.success(`Removed adapter: ${id}`);
|
|
1159
1518
|
} else {
|
|
1160
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1647
|
+
consola16.info(`${id}: already up to date`);
|
|
1289
1648
|
continue;
|
|
1290
1649
|
}
|
|
1291
1650
|
if (localDigest && !args.yes) {
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
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
|
-
|
|
1658
|
+
consola16.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
1300
1659
|
}
|
|
1301
1660
|
}
|
|
1302
1661
|
}),
|
|
1303
|
-
verify:
|
|
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
|
-
|
|
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
|
|
1351
|
-
import
|
|
1352
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1461
|
-
|
|
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
|
-
|
|
1465
|
-
|
|
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
|
-
|
|
1932
|
+
consola17.info(` Broader scope: ${wider}`);
|
|
1469
1933
|
}
|
|
1470
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1501
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1537
|
-
|
|
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 =
|
|
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 =
|
|
1559
|
-
const loaded =
|
|
1560
|
-
const resolved = await
|
|
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
|
|
1577
|
-
import
|
|
1578
|
-
var configGetCommand =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1631
|
-
import
|
|
1632
|
-
var configSetCommand =
|
|
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
|
-
|
|
2131
|
+
consola19.success(`Set ${key} = ${value}`);
|
|
1669
2132
|
}
|
|
1670
2133
|
});
|
|
1671
2134
|
|
|
1672
2135
|
// src/commands/fetch/index.ts
|
|
1673
|
-
import { defineCommand as
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
1781
|
-
var mcpCommand =
|
|
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-
|
|
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
|
|
1815
|
-
import
|
|
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) =>
|
|
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
|
|
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
|
|
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 =
|
|
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 (
|
|
2350
|
+
if (existsSync2(join(dir, "package.json"))) {
|
|
1888
2351
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
1889
2352
|
}
|
|
1890
|
-
|
|
2353
|
+
consola20.start("Scaffolding SP starter...");
|
|
1891
2354
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
1892
|
-
|
|
1893
|
-
|
|
2355
|
+
consola20.success("Scaffolded from openape-sp-starter");
|
|
2356
|
+
consola20.start("Installing dependencies...");
|
|
1894
2357
|
installDeps(dir);
|
|
1895
|
-
|
|
2358
|
+
consola20.success("Dependencies installed");
|
|
1896
2359
|
const envExample = join(dir, ".env.example");
|
|
1897
2360
|
const envFile = join(dir, ".env");
|
|
1898
|
-
if (
|
|
2361
|
+
if (existsSync2(envExample) && !existsSync2(envFile)) {
|
|
1899
2362
|
copyFileSync(envExample, envFile);
|
|
1900
|
-
|
|
2363
|
+
consola20.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
1901
2364
|
}
|
|
1902
2365
|
console.log("");
|
|
1903
|
-
|
|
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 (
|
|
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
|
-
|
|
2385
|
+
consola20.start("Scaffolding IdP starter...");
|
|
1923
2386
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
1924
|
-
|
|
1925
|
-
|
|
2387
|
+
consola20.success("Scaffolded from openape-idp-starter");
|
|
2388
|
+
consola20.start("Installing dependencies...");
|
|
1926
2389
|
installDeps(dir);
|
|
1927
|
-
|
|
2390
|
+
consola20.success("Dependencies installed");
|
|
1928
2391
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
1929
2392
|
const managementToken = randomBytes(32).toString("hex");
|
|
1930
|
-
|
|
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
|
-
|
|
2409
|
+
consola20.success(".env created");
|
|
1947
2410
|
console.log("");
|
|
1948
|
-
|
|
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
|
|
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
|
|
1971
|
-
import
|
|
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
|
|
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 (
|
|
1987
|
-
return
|
|
2449
|
+
if (existsSync3(pubPath)) {
|
|
2450
|
+
return readFileSync2(pubPath, "utf-8").trim();
|
|
1988
2451
|
}
|
|
1989
|
-
const keyContent =
|
|
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 (!
|
|
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 =
|
|
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((
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
2556
|
+
if (existsSync3(resolvedKey)) {
|
|
2094
2557
|
publicKey = readPublicKey(resolvedKey);
|
|
2095
|
-
|
|
2558
|
+
consola21.success(`Using existing key ${keyPath}`);
|
|
2096
2559
|
} else {
|
|
2097
|
-
|
|
2560
|
+
consola21.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
2098
2561
|
publicKey = generateAndSaveKey(keyPath);
|
|
2099
|
-
|
|
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
|
-
|
|
2104
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
2130
|
-
|
|
2592
|
+
consola21.success(`Agent enrolled as ${agentEmail}`);
|
|
2593
|
+
consola21.success("Config saved to ~/.config/apes/");
|
|
2131
2594
|
console.log("");
|
|
2132
|
-
|
|
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
|
|
2138
|
-
import
|
|
2663
|
+
import { defineCommand as defineCommand29 } from "citty";
|
|
2664
|
+
import consola23 from "consola";
|
|
2139
2665
|
import { resolveDDISA } from "@openape/core";
|
|
2140
|
-
var dnsCheckCommand =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2196
|
-
import
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2884
|
+
var main = defineCommand31({
|
|
2342
2885
|
meta: {
|
|
2343
2886
|
name: "apes",
|
|
2344
|
-
version: "0.6.
|
|
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
|
-
|
|
2914
|
+
consola25.error(err.message);
|
|
2370
2915
|
process.exit(err.exitCode);
|
|
2371
2916
|
}
|
|
2372
2917
|
if (debug) {
|
|
2373
|
-
|
|
2918
|
+
consola25.error(err);
|
|
2374
2919
|
} else {
|
|
2375
|
-
|
|
2920
|
+
consola25.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
2376
2921
|
}
|
|
2377
2922
|
process.exit(1);
|
|
2378
2923
|
});
|