@openape/apes 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-AGHP6MNV.js → chunk-ZSJU7IXE.js} +19 -1
- package/dist/chunk-ZSJU7IXE.js.map +1 -0
- package/dist/cli.js +301 -179
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/{server-5ZRR26S4.js → server-IYR5LM63.js} +2 -2
- package/package.json +10 -6
- package/dist/chunk-AGHP6MNV.js.map +0 -1
- /package/dist/{server-5ZRR26S4.js.map → server-IYR5LM63.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
CliError,
|
|
4
|
+
CliExit,
|
|
3
5
|
parseDuration
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZSJU7IXE.js";
|
|
5
7
|
import {
|
|
6
8
|
loadEd25519PrivateKey
|
|
7
9
|
} from "./chunk-KVBHBOED.js";
|
|
@@ -22,8 +24,8 @@ import {
|
|
|
22
24
|
} from "./chunk-KXESKY4X.js";
|
|
23
25
|
|
|
24
26
|
// src/cli.ts
|
|
25
|
-
import
|
|
26
|
-
import { defineCommand as
|
|
27
|
+
import consola21 from "consola";
|
|
28
|
+
import { defineCommand as defineCommand26, runMain } from "citty";
|
|
27
29
|
|
|
28
30
|
// src/commands/auth/login.ts
|
|
29
31
|
import { Buffer } from "buffer";
|
|
@@ -57,8 +59,7 @@ var loginCommand = defineCommand({
|
|
|
57
59
|
const config = loadConfig();
|
|
58
60
|
const idp = args.idp || process.env.APES_IDP || process.env.GRAPES_IDP || config.defaults?.idp;
|
|
59
61
|
if (!idp) {
|
|
60
|
-
|
|
61
|
-
return process.exit(1);
|
|
62
|
+
throw new CliError("IdP URL required. Use --idp <url> or set APES_IDP.");
|
|
62
63
|
}
|
|
63
64
|
if (args.key) {
|
|
64
65
|
await loginWithKey(idp, args.key, args.email);
|
|
@@ -137,14 +138,12 @@ async function loginWithPKCE(idp) {
|
|
|
137
138
|
});
|
|
138
139
|
if (!tokenResponse.ok) {
|
|
139
140
|
const text = await tokenResponse.text();
|
|
140
|
-
|
|
141
|
-
return process.exit(1);
|
|
141
|
+
throw new CliError(`Token exchange failed: ${text}`);
|
|
142
142
|
}
|
|
143
143
|
const tokens = await tokenResponse.json();
|
|
144
144
|
const accessToken = tokens.access_token || tokens.id_token || tokens.assertion;
|
|
145
145
|
if (!accessToken) {
|
|
146
|
-
|
|
147
|
-
return process.exit(1);
|
|
146
|
+
throw new CliError("No access token received");
|
|
148
147
|
}
|
|
149
148
|
const payload = JSON.parse(atob(accessToken.split(".")[1]));
|
|
150
149
|
saveAuth({
|
|
@@ -162,8 +161,7 @@ async function loginWithKey(idp, keyPath, email) {
|
|
|
162
161
|
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-Q7KG4K25.js");
|
|
163
162
|
const agentEmail = email;
|
|
164
163
|
if (!agentEmail) {
|
|
165
|
-
|
|
166
|
-
return process.exit(1);
|
|
164
|
+
throw new CliError("Agent email required for key-based login. Use --email <agent-email>");
|
|
167
165
|
}
|
|
168
166
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
169
167
|
const challengeResp = await fetch(challengeUrl, {
|
|
@@ -172,8 +170,7 @@ async function loginWithKey(idp, keyPath, email) {
|
|
|
172
170
|
body: JSON.stringify({ agent_id: agentEmail })
|
|
173
171
|
});
|
|
174
172
|
if (!challengeResp.ok) {
|
|
175
|
-
|
|
176
|
-
return process.exit(1);
|
|
173
|
+
throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
|
|
177
174
|
}
|
|
178
175
|
const { challenge } = await challengeResp.json();
|
|
179
176
|
const keyContent = readFileSync2(keyPath, "utf-8");
|
|
@@ -190,8 +187,7 @@ async function loginWithKey(idp, keyPath, email) {
|
|
|
190
187
|
})
|
|
191
188
|
});
|
|
192
189
|
if (!authResp.ok) {
|
|
193
|
-
|
|
194
|
-
return process.exit(1);
|
|
190
|
+
throw new CliError(`Authentication failed: ${await authResp.text()}`);
|
|
195
191
|
}
|
|
196
192
|
const { token, expires_in } = await authResp.json();
|
|
197
193
|
saveAuth({
|
|
@@ -228,8 +224,7 @@ var whoamiCommand = defineCommand3({
|
|
|
228
224
|
run() {
|
|
229
225
|
const auth = loadAuth();
|
|
230
226
|
if (!auth) {
|
|
231
|
-
|
|
232
|
-
return process.exit(1);
|
|
227
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
233
228
|
}
|
|
234
229
|
const isAgent = auth.email.includes("agent+");
|
|
235
230
|
const expiresAt = new Date(auth.expires_at * 1e3).toISOString();
|
|
@@ -275,8 +270,7 @@ var listCommand = defineCommand4({
|
|
|
275
270
|
async run({ args }) {
|
|
276
271
|
const idp = getIdpUrl();
|
|
277
272
|
if (!idp) {
|
|
278
|
-
|
|
279
|
-
return process.exit(1);
|
|
273
|
+
throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
|
|
280
274
|
}
|
|
281
275
|
const auth = loadAuth();
|
|
282
276
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
@@ -335,13 +329,11 @@ var inboxCommand = defineCommand5({
|
|
|
335
329
|
async run({ args }) {
|
|
336
330
|
const idp = getIdpUrl();
|
|
337
331
|
if (!idp) {
|
|
338
|
-
|
|
339
|
-
return process.exit(1);
|
|
332
|
+
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
340
333
|
}
|
|
341
334
|
const auth = loadAuth();
|
|
342
335
|
if (!auth) {
|
|
343
|
-
|
|
344
|
-
return process.exit(1);
|
|
336
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
345
337
|
}
|
|
346
338
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
347
339
|
const params = new URLSearchParams();
|
|
@@ -477,8 +469,7 @@ var requestCommand = defineCommand7({
|
|
|
477
469
|
async run({ args }) {
|
|
478
470
|
const auth = loadAuth();
|
|
479
471
|
if (!auth) {
|
|
480
|
-
|
|
481
|
-
return process.exit(1);
|
|
472
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
482
473
|
}
|
|
483
474
|
const idp = getIdpUrl();
|
|
484
475
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
@@ -516,17 +507,14 @@ async function waitForApproval(grantsUrl, grantId) {
|
|
|
516
507
|
return;
|
|
517
508
|
}
|
|
518
509
|
if (grant.status === "denied") {
|
|
519
|
-
|
|
520
|
-
return process.exit(1);
|
|
510
|
+
throw new CliError("Grant denied.");
|
|
521
511
|
}
|
|
522
512
|
if (grant.status === "revoked") {
|
|
523
|
-
|
|
524
|
-
return process.exit(1);
|
|
513
|
+
throw new CliError("Grant revoked.");
|
|
525
514
|
}
|
|
526
515
|
await new Promise((r) => setTimeout(r, interval));
|
|
527
516
|
}
|
|
528
|
-
|
|
529
|
-
return process.exit(1);
|
|
517
|
+
throw new CliError("Timed out waiting for approval.");
|
|
530
518
|
}
|
|
531
519
|
|
|
532
520
|
// src/commands/grants/request-capability.ts
|
|
@@ -644,17 +632,14 @@ async function waitForApproval2(grantsUrl, grantId) {
|
|
|
644
632
|
return;
|
|
645
633
|
}
|
|
646
634
|
if (grant.status === "denied") {
|
|
647
|
-
|
|
648
|
-
process.exit(1);
|
|
635
|
+
throw new CliError("Grant denied.");
|
|
649
636
|
}
|
|
650
637
|
if (grant.status === "revoked") {
|
|
651
|
-
|
|
652
|
-
process.exit(1);
|
|
638
|
+
throw new CliError("Grant revoked.");
|
|
653
639
|
}
|
|
654
640
|
await new Promise((resolve2) => setTimeout(resolve2, interval));
|
|
655
641
|
}
|
|
656
|
-
|
|
657
|
-
process.exit(1);
|
|
642
|
+
throw new CliError("Timed out waiting for approval.");
|
|
658
643
|
}
|
|
659
644
|
var requestCapabilityCommand = defineCommand8({
|
|
660
645
|
meta: {
|
|
@@ -713,14 +698,12 @@ var requestCapabilityCommand = defineCommand8({
|
|
|
713
698
|
async run({ rawArgs }) {
|
|
714
699
|
const auth = loadAuth();
|
|
715
700
|
if (!auth) {
|
|
716
|
-
|
|
717
|
-
return process.exit(1);
|
|
701
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
718
702
|
}
|
|
719
703
|
const parsed = parseCapabilityArgs(rawArgs);
|
|
720
704
|
const idp = getIdpUrl(parsed.idp);
|
|
721
705
|
if (!idp) {
|
|
722
|
-
|
|
723
|
-
return process.exit(1);
|
|
706
|
+
throw new CliError("No IdP URL configured. Use --idp or log in first.");
|
|
724
707
|
}
|
|
725
708
|
const loaded = loadAdapter(parsed.cliId, parsed.adapter);
|
|
726
709
|
const resolved = resolveCapabilityRequest(loaded, {
|
|
@@ -822,23 +805,40 @@ var revokeCommand = defineCommand11({
|
|
|
822
805
|
type: "boolean",
|
|
823
806
|
description: "Revoke all own pending grants",
|
|
824
807
|
default: false
|
|
808
|
+
},
|
|
809
|
+
debug: {
|
|
810
|
+
type: "boolean",
|
|
811
|
+
description: "Print debug information (does not include full tokens)",
|
|
812
|
+
default: false
|
|
825
813
|
}
|
|
826
814
|
},
|
|
827
815
|
async run({ args }) {
|
|
816
|
+
const auth = loadAuth();
|
|
817
|
+
const token = getAuthToken();
|
|
828
818
|
const idp = getIdpUrl();
|
|
829
819
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
820
|
+
if (args.debug) {
|
|
821
|
+
consola10.debug(`idp: ${idp}`);
|
|
822
|
+
consola10.debug(`grantsUrl: ${grantsUrl}`);
|
|
823
|
+
consola10.debug(`auth.email: ${auth?.email}`);
|
|
824
|
+
consola10.debug(`auth.expires_at: ${auth?.expires_at} (now: ${Math.floor(Date.now() / 1e3)})`);
|
|
825
|
+
consola10.debug(`getAuthToken(): ${token ? `${token.substring(0, 20)}...` : "NULL"}`);
|
|
826
|
+
}
|
|
827
|
+
if (!auth || !token) {
|
|
828
|
+
throw new CliError("Authentication required. Run `apes login` and try again.");
|
|
829
|
+
}
|
|
830
830
|
const explicitIds = args.id ? [String(args.id), ...args._].filter(Boolean) : [];
|
|
831
831
|
if (args.allPending && explicitIds.length > 0) {
|
|
832
|
-
|
|
833
|
-
return process.exit(1);
|
|
832
|
+
throw new CliError("Use either --all-pending or grant IDs, not both.");
|
|
834
833
|
}
|
|
835
834
|
let ids;
|
|
836
835
|
if (args.allPending) {
|
|
837
|
-
const
|
|
836
|
+
const auth2 = loadAuth();
|
|
838
837
|
const response = await apiFetch(
|
|
839
|
-
`${grantsUrl}?status=pending&limit=100
|
|
838
|
+
`${grantsUrl}?status=pending&limit=100`,
|
|
839
|
+
{ token }
|
|
840
840
|
);
|
|
841
|
-
const ownPending =
|
|
841
|
+
const ownPending = auth2?.email ? response.data.filter((g) => g.request?.requester === auth2.email) : response.data;
|
|
842
842
|
if (ownPending.length === 0) {
|
|
843
843
|
consola10.info("No pending grants to revoke.");
|
|
844
844
|
return;
|
|
@@ -848,18 +848,17 @@ var revokeCommand = defineCommand11({
|
|
|
848
848
|
} else if (explicitIds.length > 0) {
|
|
849
849
|
ids = explicitIds;
|
|
850
850
|
} else {
|
|
851
|
-
|
|
852
|
-
return process.exit(1);
|
|
851
|
+
throw new CliError("Provide grant ID(s) or use --all-pending.");
|
|
853
852
|
}
|
|
854
853
|
if (ids.length === 1) {
|
|
855
|
-
await apiFetch(`${grantsUrl}/${ids[0]}/revoke`, { method: "POST" });
|
|
854
|
+
await apiFetch(`${grantsUrl}/${ids[0]}/revoke`, { method: "POST", token });
|
|
856
855
|
consola10.success(`Grant ${ids[0]} revoked.`);
|
|
857
856
|
return;
|
|
858
857
|
}
|
|
859
858
|
const operations = ids.map((id) => ({ id, action: "revoke" }));
|
|
860
859
|
const { results } = await apiFetch(
|
|
861
860
|
`${grantsUrl}/batch`,
|
|
862
|
-
{ method: "POST", body: { operations } }
|
|
861
|
+
{ method: "POST", body: { operations }, token }
|
|
863
862
|
);
|
|
864
863
|
let succeeded = 0;
|
|
865
864
|
for (const r of results) {
|
|
@@ -871,8 +870,7 @@ var revokeCommand = defineCommand11({
|
|
|
871
870
|
}
|
|
872
871
|
}
|
|
873
872
|
if (succeeded < results.length) {
|
|
874
|
-
|
|
875
|
-
process.exit(1);
|
|
873
|
+
throw new CliError(`Revoked ${succeeded} of ${results.length} grants.`);
|
|
876
874
|
} else {
|
|
877
875
|
consola10.success(`All ${succeeded} grants revoked.`);
|
|
878
876
|
}
|
|
@@ -881,7 +879,6 @@ var revokeCommand = defineCommand11({
|
|
|
881
879
|
|
|
882
880
|
// src/commands/grants/token.ts
|
|
883
881
|
import { defineCommand as defineCommand12 } from "citty";
|
|
884
|
-
import consola11 from "consola";
|
|
885
882
|
var tokenCommand = defineCommand12({
|
|
886
883
|
meta: {
|
|
887
884
|
name: "token",
|
|
@@ -901,8 +898,7 @@ var tokenCommand = defineCommand12({
|
|
|
901
898
|
method: "POST"
|
|
902
899
|
});
|
|
903
900
|
if (!result.authz_jwt) {
|
|
904
|
-
|
|
905
|
-
return process.exit(1);
|
|
901
|
+
throw new CliError("No token received. Grant may not be approved.");
|
|
906
902
|
}
|
|
907
903
|
process.stdout.write(result.authz_jwt);
|
|
908
904
|
}
|
|
@@ -910,7 +906,7 @@ var tokenCommand = defineCommand12({
|
|
|
910
906
|
|
|
911
907
|
// src/commands/grants/delegate.ts
|
|
912
908
|
import { defineCommand as defineCommand13 } from "citty";
|
|
913
|
-
import
|
|
909
|
+
import consola11 from "consola";
|
|
914
910
|
var delegateCommand = defineCommand13({
|
|
915
911
|
meta: {
|
|
916
912
|
name: "delegate",
|
|
@@ -944,8 +940,7 @@ var delegateCommand = defineCommand13({
|
|
|
944
940
|
async run({ args }) {
|
|
945
941
|
const auth = loadAuth();
|
|
946
942
|
if (!auth) {
|
|
947
|
-
|
|
948
|
-
return process.exit(1);
|
|
943
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
949
944
|
}
|
|
950
945
|
const idp = getIdpUrl();
|
|
951
946
|
const delegationsUrl = await getDelegationsEndpoint(idp);
|
|
@@ -964,7 +959,7 @@ var delegateCommand = defineCommand13({
|
|
|
964
959
|
method: "POST",
|
|
965
960
|
body
|
|
966
961
|
});
|
|
967
|
-
|
|
962
|
+
consola11.success(`Delegation created: ${result.id}`);
|
|
968
963
|
console.log(` Delegate: ${args.to}`);
|
|
969
964
|
console.log(` Audience: ${args.at}`);
|
|
970
965
|
if (args.scopes)
|
|
@@ -977,7 +972,7 @@ var delegateCommand = defineCommand13({
|
|
|
977
972
|
|
|
978
973
|
// src/commands/grants/delegations.ts
|
|
979
974
|
import { defineCommand as defineCommand14 } from "citty";
|
|
980
|
-
import
|
|
975
|
+
import consola12 from "consola";
|
|
981
976
|
var delegationsCommand = defineCommand14({
|
|
982
977
|
meta: {
|
|
983
978
|
name: "delegations",
|
|
@@ -999,7 +994,7 @@ var delegationsCommand = defineCommand14({
|
|
|
999
994
|
return;
|
|
1000
995
|
}
|
|
1001
996
|
if (delegations.length === 0) {
|
|
1002
|
-
|
|
997
|
+
consola12.info("No delegations found.");
|
|
1003
998
|
return;
|
|
1004
999
|
}
|
|
1005
1000
|
for (const d of delegations) {
|
|
@@ -1012,7 +1007,7 @@ var delegationsCommand = defineCommand14({
|
|
|
1012
1007
|
|
|
1013
1008
|
// src/commands/adapter/index.ts
|
|
1014
1009
|
import { defineCommand as defineCommand15 } from "citty";
|
|
1015
|
-
import
|
|
1010
|
+
import consola13 from "consola";
|
|
1016
1011
|
import {
|
|
1017
1012
|
fetchRegistry,
|
|
1018
1013
|
findAdapter,
|
|
@@ -1061,7 +1056,7 @@ var adapterCommand = defineCommand15({
|
|
|
1061
1056
|
`);
|
|
1062
1057
|
return;
|
|
1063
1058
|
}
|
|
1064
|
-
|
|
1059
|
+
consola13.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
1065
1060
|
for (const a of index2.adapters) {
|
|
1066
1061
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
1067
1062
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -1083,7 +1078,7 @@ var adapterCommand = defineCommand15({
|
|
|
1083
1078
|
return;
|
|
1084
1079
|
}
|
|
1085
1080
|
if (local.length === 0) {
|
|
1086
|
-
|
|
1081
|
+
consola13.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
1087
1082
|
return;
|
|
1088
1083
|
}
|
|
1089
1084
|
for (const a of local) {
|
|
@@ -1120,20 +1115,20 @@ var adapterCommand = defineCommand15({
|
|
|
1120
1115
|
for (const id of ids) {
|
|
1121
1116
|
const entry = findAdapter(index, id);
|
|
1122
1117
|
if (!entry) {
|
|
1123
|
-
|
|
1118
|
+
consola13.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
1124
1119
|
continue;
|
|
1125
1120
|
}
|
|
1126
1121
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
1127
1122
|
if (conflicts.length > 0) {
|
|
1128
1123
|
for (const c of conflicts) {
|
|
1129
|
-
|
|
1130
|
-
|
|
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}`);
|
|
1131
1126
|
}
|
|
1132
1127
|
}
|
|
1133
1128
|
const result = await installAdapter(entry, { local });
|
|
1134
1129
|
const verb = result.updated ? "Updated" : "Installed";
|
|
1135
|
-
|
|
1136
|
-
|
|
1130
|
+
consola13.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
1131
|
+
consola13.info(`Digest: ${result.digest}`);
|
|
1137
1132
|
}
|
|
1138
1133
|
}
|
|
1139
1134
|
}),
|
|
@@ -1160,14 +1155,14 @@ var adapterCommand = defineCommand15({
|
|
|
1160
1155
|
let failed = false;
|
|
1161
1156
|
for (const id of ids) {
|
|
1162
1157
|
if (removeAdapter(id, local)) {
|
|
1163
|
-
|
|
1158
|
+
consola13.success(`Removed adapter: ${id}`);
|
|
1164
1159
|
} else {
|
|
1165
|
-
|
|
1160
|
+
consola13.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
1166
1161
|
failed = true;
|
|
1167
1162
|
}
|
|
1168
1163
|
}
|
|
1169
1164
|
if (failed)
|
|
1170
|
-
|
|
1165
|
+
throw new CliError("Some adapters could not be removed");
|
|
1171
1166
|
}
|
|
1172
1167
|
}),
|
|
1173
1168
|
info: defineCommand15({
|
|
@@ -1244,7 +1239,7 @@ var adapterCommand = defineCommand15({
|
|
|
1244
1239
|
return;
|
|
1245
1240
|
}
|
|
1246
1241
|
if (results.length === 0) {
|
|
1247
|
-
|
|
1242
|
+
consola13.info(`No adapters matching "${query}"`);
|
|
1248
1243
|
return;
|
|
1249
1244
|
}
|
|
1250
1245
|
for (const a of results) {
|
|
@@ -1279,29 +1274,29 @@ var adapterCommand = defineCommand15({
|
|
|
1279
1274
|
const targetId = args.id ? String(args.id) : void 0;
|
|
1280
1275
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
1281
1276
|
if (targets.length === 0) {
|
|
1282
|
-
|
|
1277
|
+
consola13.info("No adapters installed to update.");
|
|
1283
1278
|
return;
|
|
1284
1279
|
}
|
|
1285
1280
|
for (const id of targets) {
|
|
1286
1281
|
const entry = findAdapter(index, id);
|
|
1287
1282
|
if (!entry) {
|
|
1288
|
-
|
|
1283
|
+
consola13.warn(`${id}: not found in registry, skipping`);
|
|
1289
1284
|
continue;
|
|
1290
1285
|
}
|
|
1291
1286
|
const localDigest = getInstalledDigest(id, false);
|
|
1292
1287
|
if (localDigest === entry.digest) {
|
|
1293
|
-
|
|
1288
|
+
consola13.info(`${id}: already up to date`);
|
|
1294
1289
|
continue;
|
|
1295
1290
|
}
|
|
1296
1291
|
if (localDigest && !args.yes) {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
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");
|
|
1301
1296
|
continue;
|
|
1302
1297
|
}
|
|
1303
1298
|
const result = await installAdapter(entry);
|
|
1304
|
-
|
|
1299
|
+
consola13.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
1305
1300
|
}
|
|
1306
1301
|
}
|
|
1307
1302
|
}),
|
|
@@ -1338,12 +1333,11 @@ var adapterCommand = defineCommand15({
|
|
|
1338
1333
|
if (!localDigest)
|
|
1339
1334
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
1340
1335
|
if (localDigest === entry.digest) {
|
|
1341
|
-
|
|
1336
|
+
consola13.success(`${id}: digest matches registry`);
|
|
1342
1337
|
} else {
|
|
1343
|
-
consola14.error(`${id}: digest mismatch`);
|
|
1344
1338
|
console.log(` Local: ${localDigest}`);
|
|
1345
1339
|
console.log(` Registry: ${entry.digest}`);
|
|
1346
|
-
|
|
1340
|
+
throw new CliError(`${id}: digest mismatch`);
|
|
1347
1341
|
}
|
|
1348
1342
|
}
|
|
1349
1343
|
})
|
|
@@ -1365,7 +1359,7 @@ import {
|
|
|
1365
1359
|
verifyAndExecute,
|
|
1366
1360
|
waitForGrantStatus
|
|
1367
1361
|
} from "@openape/shapes";
|
|
1368
|
-
import
|
|
1362
|
+
import consola14 from "consola";
|
|
1369
1363
|
var runCommand = defineCommand16({
|
|
1370
1364
|
meta: {
|
|
1371
1365
|
name: "run",
|
|
@@ -1440,6 +1434,10 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
1440
1434
|
const idp = getIdpUrl(args.idp);
|
|
1441
1435
|
if (!idp)
|
|
1442
1436
|
throw new Error("No IdP URL configured. Run `apes login` first or pass --idp.");
|
|
1437
|
+
if (args.as) {
|
|
1438
|
+
await runAudienceMode("escapes", command.join(" "), args);
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1443
1441
|
const adapterOpt = extractOption(rawArgs, "adapter");
|
|
1444
1442
|
const loaded = loadAdapter3(command[0], adapterOpt);
|
|
1445
1443
|
const resolved = await resolveCommand(loaded, command);
|
|
@@ -1447,7 +1445,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
1447
1445
|
try {
|
|
1448
1446
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
1449
1447
|
if (existingGrantId) {
|
|
1450
|
-
|
|
1448
|
+
consola14.info(`Reusing existing grant: ${existingGrantId}`);
|
|
1451
1449
|
const token2 = await fetchGrantToken(idp, existingGrantId);
|
|
1452
1450
|
await verifyAndExecute(token2, resolved);
|
|
1453
1451
|
return;
|
|
@@ -1459,17 +1457,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
1459
1457
|
approval,
|
|
1460
1458
|
...args.reason ? { reason: args.reason } : {}
|
|
1461
1459
|
});
|
|
1462
|
-
|
|
1463
|
-
|
|
1460
|
+
consola14.info(`Grant requested: ${grant.id}`);
|
|
1461
|
+
consola14.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
1464
1462
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
1465
1463
|
const n = grant.similar_grants.similar_grants.length;
|
|
1466
|
-
|
|
1467
|
-
|
|
1464
|
+
consola14.info("");
|
|
1465
|
+
consola14.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
1468
1466
|
if (grant.similar_grants.widened_details?.length) {
|
|
1469
1467
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
1470
|
-
|
|
1468
|
+
consola14.info(` Broader scope: ${wider}`);
|
|
1471
1469
|
}
|
|
1472
|
-
|
|
1470
|
+
consola14.info("");
|
|
1473
1471
|
}
|
|
1474
1472
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
1475
1473
|
if (status !== "approved")
|
|
@@ -1480,14 +1478,13 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
1480
1478
|
async function runAudienceMode(audience, action, args) {
|
|
1481
1479
|
const auth = loadAuth();
|
|
1482
1480
|
if (!auth) {
|
|
1483
|
-
|
|
1484
|
-
return process.exit(1);
|
|
1481
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
1485
1482
|
}
|
|
1486
1483
|
const idp = getIdpUrl(args.idp);
|
|
1487
1484
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
1488
1485
|
const command = action.split(" ");
|
|
1489
1486
|
const targetHost = args.host || hostname3();
|
|
1490
|
-
|
|
1487
|
+
consola14.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
1491
1488
|
const grant = await apiFetch(grantsUrl, {
|
|
1492
1489
|
method: "POST",
|
|
1493
1490
|
body: {
|
|
@@ -1500,36 +1497,35 @@ async function runAudienceMode(audience, action, args) {
|
|
|
1500
1497
|
...args.as ? { run_as: args.as } : {}
|
|
1501
1498
|
}
|
|
1502
1499
|
});
|
|
1503
|
-
|
|
1504
|
-
|
|
1500
|
+
consola14.success(`Grant requested: ${grant.id}`);
|
|
1501
|
+
consola14.info("Waiting for approval...");
|
|
1505
1502
|
const maxWait = 3e5;
|
|
1506
1503
|
const interval = 3e3;
|
|
1507
1504
|
const start = Date.now();
|
|
1508
1505
|
while (Date.now() - start < maxWait) {
|
|
1509
1506
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
1510
1507
|
if (status.status === "approved") {
|
|
1511
|
-
|
|
1508
|
+
consola14.success("Grant approved!");
|
|
1512
1509
|
break;
|
|
1513
1510
|
}
|
|
1514
1511
|
if (status.status === "denied" || status.status === "revoked") {
|
|
1515
|
-
|
|
1516
|
-
return process.exit(1);
|
|
1512
|
+
throw new CliError(`Grant ${status.status}.`);
|
|
1517
1513
|
}
|
|
1518
1514
|
await new Promise((r) => setTimeout(r, interval));
|
|
1519
1515
|
}
|
|
1520
|
-
|
|
1516
|
+
consola14.info("Fetching grant token...");
|
|
1521
1517
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
1522
1518
|
method: "POST"
|
|
1523
1519
|
});
|
|
1524
1520
|
if (audience === "escapes") {
|
|
1525
|
-
|
|
1521
|
+
consola14.info(`Executing: ${command.join(" ")}`);
|
|
1526
1522
|
try {
|
|
1527
1523
|
execFileSync(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
1528
1524
|
stdio: "inherit"
|
|
1529
1525
|
});
|
|
1530
1526
|
} catch (err) {
|
|
1531
1527
|
const exitCode = err.status || 1;
|
|
1532
|
-
|
|
1528
|
+
throw new CliExit(exitCode);
|
|
1533
1529
|
}
|
|
1534
1530
|
} else {
|
|
1535
1531
|
process.stdout.write(authz_jwt);
|
|
@@ -1578,7 +1574,7 @@ var explainCommand = defineCommand17({
|
|
|
1578
1574
|
|
|
1579
1575
|
// src/commands/config/get.ts
|
|
1580
1576
|
import { defineCommand as defineCommand18 } from "citty";
|
|
1581
|
-
import
|
|
1577
|
+
import consola15 from "consola";
|
|
1582
1578
|
var configGetCommand = defineCommand18({
|
|
1583
1579
|
meta: {
|
|
1584
1580
|
name: "get",
|
|
@@ -1599,7 +1595,7 @@ var configGetCommand = defineCommand18({
|
|
|
1599
1595
|
if (idp)
|
|
1600
1596
|
console.log(idp);
|
|
1601
1597
|
else
|
|
1602
|
-
|
|
1598
|
+
consola15.info("No IdP configured.");
|
|
1603
1599
|
break;
|
|
1604
1600
|
}
|
|
1605
1601
|
case "email": {
|
|
@@ -1607,7 +1603,7 @@ var configGetCommand = defineCommand18({
|
|
|
1607
1603
|
if (auth?.email)
|
|
1608
1604
|
console.log(auth.email);
|
|
1609
1605
|
else
|
|
1610
|
-
|
|
1606
|
+
consola15.info("Not logged in.");
|
|
1611
1607
|
break;
|
|
1612
1608
|
}
|
|
1613
1609
|
default: {
|
|
@@ -1620,11 +1616,10 @@ var configGetCommand = defineCommand18({
|
|
|
1620
1616
|
if (sectionObj && field in sectionObj) {
|
|
1621
1617
|
console.log(sectionObj[field]);
|
|
1622
1618
|
} else {
|
|
1623
|
-
|
|
1619
|
+
consola15.info(`Key "${key}" not set.`);
|
|
1624
1620
|
}
|
|
1625
1621
|
} else {
|
|
1626
|
-
|
|
1627
|
-
process.exit(1);
|
|
1622
|
+
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
1628
1623
|
}
|
|
1629
1624
|
}
|
|
1630
1625
|
}
|
|
@@ -1633,7 +1628,7 @@ var configGetCommand = defineCommand18({
|
|
|
1633
1628
|
|
|
1634
1629
|
// src/commands/config/set.ts
|
|
1635
1630
|
import { defineCommand as defineCommand19 } from "citty";
|
|
1636
|
-
import
|
|
1631
|
+
import consola16 from "consola";
|
|
1637
1632
|
var configSetCommand = defineCommand19({
|
|
1638
1633
|
meta: {
|
|
1639
1634
|
name: "set",
|
|
@@ -1657,8 +1652,7 @@ var configSetCommand = defineCommand19({
|
|
|
1657
1652
|
const config = loadConfig();
|
|
1658
1653
|
const parts = key.split(".");
|
|
1659
1654
|
if (parts.length !== 2) {
|
|
1660
|
-
|
|
1661
|
-
return process.exit(1);
|
|
1655
|
+
throw new CliError(`Invalid key: "${key}". Use: defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
1662
1656
|
}
|
|
1663
1657
|
const [section, field] = parts;
|
|
1664
1658
|
if (section === "defaults") {
|
|
@@ -1668,22 +1662,19 @@ var configSetCommand = defineCommand19({
|
|
|
1668
1662
|
config.agent = config.agent || {};
|
|
1669
1663
|
config.agent[field] = value;
|
|
1670
1664
|
} else {
|
|
1671
|
-
|
|
1672
|
-
return process.exit(1);
|
|
1665
|
+
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
1673
1666
|
}
|
|
1674
1667
|
saveConfig(config);
|
|
1675
|
-
|
|
1668
|
+
consola16.success(`Set ${key} = ${value}`);
|
|
1676
1669
|
}
|
|
1677
1670
|
});
|
|
1678
1671
|
|
|
1679
1672
|
// src/commands/fetch/index.ts
|
|
1680
1673
|
import { defineCommand as defineCommand20 } from "citty";
|
|
1681
|
-
import consola18 from "consola";
|
|
1682
1674
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
1683
1675
|
const token = getAuthToken();
|
|
1684
1676
|
if (!token) {
|
|
1685
|
-
|
|
1686
|
-
return process.exit(1);
|
|
1677
|
+
throw new CliError("Not authenticated. Run `apes login` first.");
|
|
1687
1678
|
}
|
|
1688
1679
|
const response = await fetch(url, {
|
|
1689
1680
|
method,
|
|
@@ -1712,7 +1703,7 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
1712
1703
|
}
|
|
1713
1704
|
}
|
|
1714
1705
|
if (!response.ok) {
|
|
1715
|
-
|
|
1706
|
+
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
1716
1707
|
}
|
|
1717
1708
|
}
|
|
1718
1709
|
var fetchCommand = defineCommand20({
|
|
@@ -1810,7 +1801,7 @@ var mcpCommand = defineCommand21({
|
|
|
1810
1801
|
if (transport !== "stdio" && transport !== "sse") {
|
|
1811
1802
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
1812
1803
|
}
|
|
1813
|
-
const { startMcpServer } = await import("./server-
|
|
1804
|
+
const { startMcpServer } = await import("./server-IYR5LM63.js");
|
|
1814
1805
|
await startMcpServer(transport, port);
|
|
1815
1806
|
}
|
|
1816
1807
|
});
|
|
@@ -1821,7 +1812,7 @@ import { randomBytes } from "crypto";
|
|
|
1821
1812
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1822
1813
|
import { join } from "path";
|
|
1823
1814
|
import { defineCommand as defineCommand22 } from "citty";
|
|
1824
|
-
import
|
|
1815
|
+
import consola17 from "consola";
|
|
1825
1816
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
1826
1817
|
async function downloadTemplate(repo, targetDir) {
|
|
1827
1818
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
@@ -1838,16 +1829,16 @@ function installDeps(dir) {
|
|
|
1838
1829
|
}
|
|
1839
1830
|
}
|
|
1840
1831
|
async function promptChoice(message, choices) {
|
|
1841
|
-
const result = await
|
|
1832
|
+
const result = await consola17.prompt(message, { type: "select", options: choices });
|
|
1842
1833
|
if (typeof result === "symbol") {
|
|
1843
|
-
|
|
1834
|
+
throw new CliExit(0);
|
|
1844
1835
|
}
|
|
1845
1836
|
return result;
|
|
1846
1837
|
}
|
|
1847
1838
|
async function promptText(message, defaultValue) {
|
|
1848
|
-
const result = await
|
|
1839
|
+
const result = await consola17.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
1849
1840
|
if (typeof result === "symbol") {
|
|
1850
|
-
|
|
1841
|
+
throw new CliExit(0);
|
|
1851
1842
|
}
|
|
1852
1843
|
return result || defaultValue || "";
|
|
1853
1844
|
}
|
|
@@ -1894,23 +1885,22 @@ var initCommand = defineCommand22({
|
|
|
1894
1885
|
async function initSP(targetDir) {
|
|
1895
1886
|
const dir = targetDir || "my-app";
|
|
1896
1887
|
if (existsSync(join(dir, "package.json"))) {
|
|
1897
|
-
|
|
1898
|
-
return process.exit(1);
|
|
1888
|
+
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
1899
1889
|
}
|
|
1900
|
-
|
|
1890
|
+
consola17.start("Scaffolding SP starter...");
|
|
1901
1891
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
1902
|
-
|
|
1903
|
-
|
|
1892
|
+
consola17.success("Scaffolded from openape-sp-starter");
|
|
1893
|
+
consola17.start("Installing dependencies...");
|
|
1904
1894
|
installDeps(dir);
|
|
1905
|
-
|
|
1895
|
+
consola17.success("Dependencies installed");
|
|
1906
1896
|
const envExample = join(dir, ".env.example");
|
|
1907
1897
|
const envFile = join(dir, ".env");
|
|
1908
1898
|
if (existsSync(envExample) && !existsSync(envFile)) {
|
|
1909
1899
|
copyFileSync(envExample, envFile);
|
|
1910
|
-
|
|
1900
|
+
consola17.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
1911
1901
|
}
|
|
1912
1902
|
console.log("");
|
|
1913
|
-
|
|
1903
|
+
consola17.box([
|
|
1914
1904
|
`cd ${dir}`,
|
|
1915
1905
|
"npm run dev",
|
|
1916
1906
|
"",
|
|
@@ -1920,8 +1910,7 @@ async function initSP(targetDir) {
|
|
|
1920
1910
|
async function initIdP(targetDir) {
|
|
1921
1911
|
const dir = targetDir || "my-idp";
|
|
1922
1912
|
if (existsSync(join(dir, "package.json"))) {
|
|
1923
|
-
|
|
1924
|
-
return process.exit(1);
|
|
1913
|
+
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
1925
1914
|
}
|
|
1926
1915
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
1927
1916
|
const storage = await promptChoice("Storage backend", [
|
|
@@ -1930,15 +1919,15 @@ async function initIdP(targetDir) {
|
|
|
1930
1919
|
"s3 (S3-compatible)"
|
|
1931
1920
|
]);
|
|
1932
1921
|
const adminEmail = await promptText("Admin email");
|
|
1933
|
-
|
|
1922
|
+
consola17.start("Scaffolding IdP starter...");
|
|
1934
1923
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
1935
|
-
|
|
1936
|
-
|
|
1924
|
+
consola17.success("Scaffolded from openape-idp-starter");
|
|
1925
|
+
consola17.start("Installing dependencies...");
|
|
1937
1926
|
installDeps(dir);
|
|
1938
|
-
|
|
1927
|
+
consola17.success("Dependencies installed");
|
|
1939
1928
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
1940
1929
|
const managementToken = randomBytes(32).toString("hex");
|
|
1941
|
-
|
|
1930
|
+
consola17.success("Secrets generated");
|
|
1942
1931
|
const isLocalhost = domain === "localhost";
|
|
1943
1932
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
1944
1933
|
const envContent = [
|
|
@@ -1952,10 +1941,11 @@ async function initIdP(targetDir) {
|
|
|
1952
1941
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
1953
1942
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
1954
1943
|
].join("\n");
|
|
1955
|
-
writeFileSync(join(dir, ".env"), envContent
|
|
1956
|
-
|
|
1944
|
+
writeFileSync(join(dir, ".env"), `${envContent}
|
|
1945
|
+
`, { mode: 384 });
|
|
1946
|
+
consola17.success(".env created");
|
|
1957
1947
|
console.log("");
|
|
1958
|
-
|
|
1948
|
+
consola17.box([
|
|
1959
1949
|
`cd ${dir}`,
|
|
1960
1950
|
"npm run dev",
|
|
1961
1951
|
"",
|
|
@@ -1978,7 +1968,7 @@ import { generateKeyPairSync, sign } from "crypto";
|
|
|
1978
1968
|
import { dirname, resolve } from "path";
|
|
1979
1969
|
import { homedir } from "os";
|
|
1980
1970
|
import { defineCommand as defineCommand23 } from "citty";
|
|
1981
|
-
import
|
|
1971
|
+
import consola18 from "consola";
|
|
1982
1972
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
1983
1973
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
1984
1974
|
var POLL_INTERVAL = 3e3;
|
|
@@ -2083,38 +2073,48 @@ var enrollCommand = defineCommand23({
|
|
|
2083
2073
|
}
|
|
2084
2074
|
},
|
|
2085
2075
|
async run({ args }) {
|
|
2086
|
-
const idp = args.idp || await
|
|
2087
|
-
|
|
2076
|
+
const idp = args.idp || await consola18.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
|
|
2077
|
+
if (typeof r === "symbol") throw new CliExit(0);
|
|
2078
|
+
return r;
|
|
2079
|
+
}) || DEFAULT_IDP_URL2;
|
|
2080
|
+
const agentName = args.name || await consola18.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
2081
|
+
if (typeof r === "symbol") throw new CliExit(0);
|
|
2082
|
+
return r;
|
|
2083
|
+
});
|
|
2088
2084
|
if (!agentName) {
|
|
2089
|
-
|
|
2090
|
-
return process.exit(1);
|
|
2085
|
+
throw new CliError("Agent name is required.");
|
|
2091
2086
|
}
|
|
2092
|
-
const keyPath = args.key || await
|
|
2087
|
+
const keyPath = args.key || await consola18.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
|
|
2088
|
+
if (typeof r === "symbol") throw new CliExit(0);
|
|
2089
|
+
return r;
|
|
2090
|
+
}) || DEFAULT_KEY_PATH;
|
|
2093
2091
|
const resolvedKey = resolvePath(keyPath);
|
|
2094
2092
|
let publicKey;
|
|
2095
2093
|
if (existsSync2(resolvedKey)) {
|
|
2096
2094
|
publicKey = readPublicKey(resolvedKey);
|
|
2097
|
-
|
|
2095
|
+
consola18.success(`Using existing key ${keyPath}`);
|
|
2098
2096
|
} else {
|
|
2099
|
-
|
|
2097
|
+
consola18.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
2100
2098
|
publicKey = generateAndSaveKey(keyPath);
|
|
2101
|
-
|
|
2099
|
+
consola18.success(`Key pair generated at ${keyPath}`);
|
|
2102
2100
|
}
|
|
2103
2101
|
const encodedKey = encodeURIComponent(publicKey);
|
|
2104
2102
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
2105
|
-
|
|
2106
|
-
|
|
2103
|
+
consola18.info("Opening browser for enrollment...");
|
|
2104
|
+
consola18.info(`\u2192 ${idp}/enroll`);
|
|
2107
2105
|
openBrowser2(enrollUrl);
|
|
2108
2106
|
console.log("");
|
|
2109
|
-
const agentEmail = await
|
|
2107
|
+
const agentEmail = await consola18.prompt(
|
|
2110
2108
|
"Agent email (shown in browser after enrollment)",
|
|
2111
2109
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
2112
|
-
).then((r) =>
|
|
2110
|
+
).then((r) => {
|
|
2111
|
+
if (typeof r === "symbol") throw new CliExit(0);
|
|
2112
|
+
return r;
|
|
2113
|
+
});
|
|
2113
2114
|
if (!agentEmail) {
|
|
2114
|
-
|
|
2115
|
-
return process.exit(1);
|
|
2115
|
+
throw new CliError("Agent email is required to verify enrollment.");
|
|
2116
2116
|
}
|
|
2117
|
-
|
|
2117
|
+
consola18.start("Verifying enrollment...");
|
|
2118
2118
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
2119
2119
|
saveAuth({
|
|
2120
2120
|
idp,
|
|
@@ -2126,16 +2126,16 @@ var enrollCommand = defineCommand23({
|
|
|
2126
2126
|
config.defaults = { ...config.defaults, idp };
|
|
2127
2127
|
config.agent = { key: keyPath, email: agentEmail };
|
|
2128
2128
|
saveConfig(config);
|
|
2129
|
-
|
|
2130
|
-
|
|
2129
|
+
consola18.success(`Agent enrolled as ${agentEmail}`);
|
|
2130
|
+
consola18.success("Config saved to ~/.config/apes/");
|
|
2131
2131
|
console.log("");
|
|
2132
|
-
|
|
2132
|
+
consola18.info("Verify with: apes whoami");
|
|
2133
2133
|
}
|
|
2134
2134
|
});
|
|
2135
2135
|
|
|
2136
2136
|
// src/commands/dns-check.ts
|
|
2137
2137
|
import { defineCommand as defineCommand24 } from "citty";
|
|
2138
|
-
import
|
|
2138
|
+
import consola19 from "consola";
|
|
2139
2139
|
import { resolveDDISA } from "@openape/core";
|
|
2140
2140
|
var dnsCheckCommand = defineCommand24({
|
|
2141
2141
|
meta: {
|
|
@@ -2151,17 +2151,16 @@ var dnsCheckCommand = defineCommand24({
|
|
|
2151
2151
|
},
|
|
2152
2152
|
async run({ args }) {
|
|
2153
2153
|
const domain = args.domain;
|
|
2154
|
-
|
|
2154
|
+
consola19.start(`Checking _ddisa.${domain}...`);
|
|
2155
2155
|
try {
|
|
2156
2156
|
const result = await resolveDDISA(domain);
|
|
2157
2157
|
if (!result) {
|
|
2158
|
-
consola21.error(`No DDISA record found for ${domain}`);
|
|
2159
2158
|
console.log("");
|
|
2160
2159
|
console.log("To set up DDISA, add a DNS TXT record:");
|
|
2161
2160
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
2162
|
-
|
|
2161
|
+
throw new CliError(`No DDISA record found for ${domain}`);
|
|
2163
2162
|
}
|
|
2164
|
-
|
|
2163
|
+
consola19.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
2165
2164
|
console.log("");
|
|
2166
2165
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
2167
2166
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -2170,14 +2169,14 @@ var dnsCheckCommand = defineCommand24({
|
|
|
2170
2169
|
if (result.priority !== void 0)
|
|
2171
2170
|
console.log(` Priority: ${result.priority}`);
|
|
2172
2171
|
console.log("");
|
|
2173
|
-
|
|
2172
|
+
consola19.start(`Verifying IdP at ${result.idp}...`);
|
|
2174
2173
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
2175
2174
|
if (!discoResp.ok) {
|
|
2176
|
-
|
|
2175
|
+
consola19.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
2177
2176
|
return;
|
|
2178
2177
|
}
|
|
2179
2178
|
const disco = await discoResp.json();
|
|
2180
|
-
|
|
2179
|
+
consola19.success(`IdP is reachable`);
|
|
2181
2180
|
console.log(` Issuer: ${disco.issuer}`);
|
|
2182
2181
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
2183
2182
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -2187,15 +2186,130 @@ var dnsCheckCommand = defineCommand24({
|
|
|
2187
2186
|
console.log(` Grants: ${disco.openape_grant_types_supported.join(", ")}`);
|
|
2188
2187
|
}
|
|
2189
2188
|
} catch (err) {
|
|
2190
|
-
|
|
2191
|
-
|
|
2189
|
+
throw new CliError(`DNS check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
});
|
|
2193
|
+
|
|
2194
|
+
// src/commands/workflows.ts
|
|
2195
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
2196
|
+
import consola20 from "consola";
|
|
2197
|
+
|
|
2198
|
+
// src/guides/index.ts
|
|
2199
|
+
var guides = [
|
|
2200
|
+
{
|
|
2201
|
+
id: "timed-session",
|
|
2202
|
+
title: "Timed maintenance session",
|
|
2203
|
+
description: "Request a timed grant for multiple commands without per-command approval.",
|
|
2204
|
+
steps: [
|
|
2205
|
+
{ description: "Request a timed grant (e.g. 1 hour)", command: "apes run --approval timed -- <your-command>" },
|
|
2206
|
+
{ description: "Approve the grant in the browser (link is printed)" },
|
|
2207
|
+
{ description: "Subsequent commands reuse the timed grant until it expires" },
|
|
2208
|
+
{ note: "Use --approval always for standing permissions (revoke manually when done)" }
|
|
2209
|
+
]
|
|
2210
|
+
},
|
|
2211
|
+
{
|
|
2212
|
+
id: "agent-onboarding",
|
|
2213
|
+
title: "Onboard a new agent",
|
|
2214
|
+
description: "Register an AI agent with a DDISA identity in under 3 minutes.",
|
|
2215
|
+
steps: [
|
|
2216
|
+
{ description: "Initialize a new project (optional)", command: "apes init --sp my-app" },
|
|
2217
|
+
{ description: "Enroll the agent at an IdP", command: "apes enroll" },
|
|
2218
|
+
{ description: "Verify enrollment", command: "apes whoami" },
|
|
2219
|
+
{ description: "Check DNS discovery", command: "apes dns-check" }
|
|
2220
|
+
]
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
id: "delegation",
|
|
2224
|
+
title: "Delegate permissions",
|
|
2225
|
+
description: "Let an agent act on your behalf at a specific service.",
|
|
2226
|
+
steps: [
|
|
2227
|
+
{ description: "Create a delegation", command: "apes grants delegate --to agent@example.com --at api.example.com" },
|
|
2228
|
+
{ description: "List active delegations", command: "apes grants delegations" },
|
|
2229
|
+
{ description: "Revoke when no longer needed", command: "apes grants revoke <delegation-id>" }
|
|
2230
|
+
]
|
|
2231
|
+
},
|
|
2232
|
+
{
|
|
2233
|
+
id: "privilege-escalation",
|
|
2234
|
+
title: "Run commands as root (escapes)",
|
|
2235
|
+
description: "Execute privileged commands with grant-verified escalation.",
|
|
2236
|
+
steps: [
|
|
2237
|
+
{ description: "Request a grant to run a command as root", command: "apes run --as root -- apt-get upgrade" },
|
|
2238
|
+
{ description: "Approve the grant in the browser" },
|
|
2239
|
+
{ description: "The command executes via escapes with verified authorization" },
|
|
2240
|
+
{ note: "escapes must be installed on the target machine (cargo build && sudo make install)" }
|
|
2241
|
+
]
|
|
2242
|
+
}
|
|
2243
|
+
];
|
|
2244
|
+
|
|
2245
|
+
// src/commands/workflows.ts
|
|
2246
|
+
var workflowsCommand = defineCommand25({
|
|
2247
|
+
meta: {
|
|
2248
|
+
name: "workflows",
|
|
2249
|
+
description: "Discover workflow guides"
|
|
2250
|
+
},
|
|
2251
|
+
args: {
|
|
2252
|
+
id: {
|
|
2253
|
+
type: "positional",
|
|
2254
|
+
description: "Guide ID to show (omit for list)",
|
|
2255
|
+
required: false
|
|
2256
|
+
},
|
|
2257
|
+
json: {
|
|
2258
|
+
type: "boolean",
|
|
2259
|
+
description: "Output as JSON",
|
|
2260
|
+
default: false
|
|
2192
2261
|
}
|
|
2262
|
+
},
|
|
2263
|
+
run({ args }) {
|
|
2264
|
+
if (args.id) {
|
|
2265
|
+
const guide = guides.find((g) => g.id === String(args.id));
|
|
2266
|
+
if (!guide) {
|
|
2267
|
+
consola20.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
2268
|
+
throw new CliError(`Guide not found: ${args.id}`);
|
|
2269
|
+
}
|
|
2270
|
+
if (args.json) {
|
|
2271
|
+
console.log(JSON.stringify(guide, null, 2));
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
console.log(`
|
|
2275
|
+
${guide.title}`);
|
|
2276
|
+
console.log(` ${guide.description}
|
|
2277
|
+
`);
|
|
2278
|
+
for (let i = 0; i < guide.steps.length; i++) {
|
|
2279
|
+
const step = guide.steps[i];
|
|
2280
|
+
if (step.note) {
|
|
2281
|
+
console.log(` Note: ${step.note}`);
|
|
2282
|
+
} else {
|
|
2283
|
+
console.log(` ${i + 1}. ${step.description}`);
|
|
2284
|
+
if (step.command) {
|
|
2285
|
+
console.log(` $ ${step.command}`);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
console.log();
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
if (args.json) {
|
|
2293
|
+
console.log(JSON.stringify(guides.map((g) => ({ id: g.id, title: g.title, description: g.description })), null, 2));
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
console.log("\n Workflow Guides\n");
|
|
2297
|
+
for (const guide of guides) {
|
|
2298
|
+
console.log(` ${guide.id.padEnd(24)} ${guide.title}`);
|
|
2299
|
+
}
|
|
2300
|
+
console.log(`
|
|
2301
|
+
Show a guide: apes workflows <id>
|
|
2302
|
+
`);
|
|
2193
2303
|
}
|
|
2194
2304
|
});
|
|
2195
2305
|
|
|
2196
2306
|
// src/cli.ts
|
|
2307
|
+
process.stdout.on("error", (err) => {
|
|
2308
|
+
if (err.code === "EPIPE") process.exit(0);
|
|
2309
|
+
throw err;
|
|
2310
|
+
});
|
|
2197
2311
|
var debug = process.argv.includes("--debug");
|
|
2198
|
-
var grantsCommand =
|
|
2312
|
+
var grantsCommand = defineCommand26({
|
|
2199
2313
|
meta: {
|
|
2200
2314
|
name: "grants",
|
|
2201
2315
|
description: "Grant management"
|
|
@@ -2214,7 +2328,7 @@ var grantsCommand = defineCommand25({
|
|
|
2214
2328
|
delegations: delegationsCommand
|
|
2215
2329
|
}
|
|
2216
2330
|
});
|
|
2217
|
-
var configCommand =
|
|
2331
|
+
var configCommand = defineCommand26({
|
|
2218
2332
|
meta: {
|
|
2219
2333
|
name: "config",
|
|
2220
2334
|
description: "Configuration management"
|
|
@@ -2224,10 +2338,10 @@ var configCommand = defineCommand25({
|
|
|
2224
2338
|
set: configSetCommand
|
|
2225
2339
|
}
|
|
2226
2340
|
});
|
|
2227
|
-
var main =
|
|
2341
|
+
var main = defineCommand26({
|
|
2228
2342
|
meta: {
|
|
2229
2343
|
name: "apes",
|
|
2230
|
-
version: "0.
|
|
2344
|
+
version: "0.6.0",
|
|
2231
2345
|
description: "Unified CLI for OpenApe"
|
|
2232
2346
|
},
|
|
2233
2347
|
subCommands: {
|
|
@@ -2243,14 +2357,22 @@ var main = defineCommand25({
|
|
|
2243
2357
|
adapter: adapterCommand,
|
|
2244
2358
|
config: configCommand,
|
|
2245
2359
|
fetch: fetchCommand,
|
|
2246
|
-
mcp: mcpCommand
|
|
2360
|
+
mcp: mcpCommand,
|
|
2361
|
+
workflows: workflowsCommand
|
|
2247
2362
|
}
|
|
2248
2363
|
});
|
|
2249
2364
|
runMain(main).catch((err) => {
|
|
2365
|
+
if (err instanceof CliExit) {
|
|
2366
|
+
process.exit(err.exitCode);
|
|
2367
|
+
}
|
|
2368
|
+
if (err instanceof CliError) {
|
|
2369
|
+
consola21.error(err.message);
|
|
2370
|
+
process.exit(err.exitCode);
|
|
2371
|
+
}
|
|
2250
2372
|
if (debug) {
|
|
2251
|
-
|
|
2373
|
+
consola21.error(err);
|
|
2252
2374
|
} else {
|
|
2253
|
-
|
|
2375
|
+
consola21.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
2254
2376
|
}
|
|
2255
2377
|
process.exit(1);
|
|
2256
2378
|
});
|