@openape/apes 1.7.1 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{auth-lock-4IRWI3ED.js → auth-lock-BBSP72GH.js} +2 -1
- package/dist/{auth-lock-4IRWI3ED.js.map → auth-lock-BBSP72GH.js.map} +1 -1
- package/dist/chunk-7OCVIDC7.js +12 -0
- package/dist/{chunk-N3THIFIS.js → chunk-QJJ7DG5C.js} +3 -3
- package/dist/chunk-WGF3SPIH.js +3598 -0
- package/dist/chunk-WGF3SPIH.js.map +1 -0
- package/dist/cli.js +1383 -335
- package/dist/cli.js.map +1 -1
- package/dist/{config-MOB5DJ6H.js → config-DA2L3XOV.js} +2 -1
- package/dist/{http-5F7FX4V7.js → http-JWS5LV2U.js} +3 -2
- package/dist/index.js +2 -1
- package/dist/multipart-parser-FVZBRBXW.js +182 -0
- package/dist/multipart-parser-FVZBRBXW.js.map +1 -0
- package/dist/{orchestrator-EH6V5ATG.js → orchestrator-2QS5KXFL.js} +3 -2
- package/dist/{orchestrator-EH6V5ATG.js.map → orchestrator-2QS5KXFL.js.map} +1 -1
- package/dist/{server-JBBJXDDS.js → server-EKUVZDZC.js} +4 -3
- package/dist/{server-JBBJXDDS.js.map → server-EKUVZDZC.js.map} +1 -1
- package/dist/{ssh-key-YBNNG5K5.js → ssh-key-6X3YZXSD.js} +2 -1
- package/dist/ssh-key-6X3YZXSD.js.map +1 -0
- package/package.json +3 -3
- /package/dist/{config-MOB5DJ6H.js.map → chunk-7OCVIDC7.js.map} +0 -0
- /package/dist/{chunk-N3THIFIS.js.map → chunk-QJJ7DG5C.js.map} +0 -0
- /package/dist/{http-5F7FX4V7.js.map → config-DA2L3XOV.js.map} +0 -0
- /package/dist/{ssh-key-YBNNG5K5.js.map → http-JWS5LV2U.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
checkSudoRejection,
|
|
4
|
+
isApesSelfDispatch,
|
|
5
|
+
notifyGrantPending
|
|
6
|
+
} from "./chunk-3COOEDPF.js";
|
|
2
7
|
import {
|
|
3
8
|
CliError,
|
|
4
9
|
CliExit,
|
|
@@ -9,10 +14,15 @@ import {
|
|
|
9
14
|
readPublicKeyComment
|
|
10
15
|
} from "./chunk-ION3CWD5.js";
|
|
11
16
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
Mi,
|
|
18
|
+
Mn,
|
|
19
|
+
br,
|
|
20
|
+
dt,
|
|
21
|
+
le,
|
|
22
|
+
qn,
|
|
23
|
+
ut,
|
|
24
|
+
ye
|
|
25
|
+
} from "./chunk-WGF3SPIH.js";
|
|
16
26
|
import {
|
|
17
27
|
GENERIC_OPERATION_ID,
|
|
18
28
|
buildGenericResolved,
|
|
@@ -48,7 +58,7 @@ import {
|
|
|
48
58
|
getAgentChallengeEndpoint,
|
|
49
59
|
getDelegationsEndpoint,
|
|
50
60
|
getGrantsEndpoint
|
|
51
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-QJJ7DG5C.js";
|
|
52
62
|
import {
|
|
53
63
|
AUTH_FILE,
|
|
54
64
|
CONFIG_DIR,
|
|
@@ -61,9 +71,10 @@ import {
|
|
|
61
71
|
saveAuth,
|
|
62
72
|
saveConfig
|
|
63
73
|
} from "./chunk-OBF7IMQ2.js";
|
|
74
|
+
import "./chunk-7OCVIDC7.js";
|
|
64
75
|
|
|
65
76
|
// src/cli.ts
|
|
66
|
-
import
|
|
77
|
+
import consola51 from "consola";
|
|
67
78
|
|
|
68
79
|
// src/ape-shell.ts
|
|
69
80
|
import path from "path";
|
|
@@ -93,7 +104,7 @@ function rewriteApeShellArgs(argv, argv0) {
|
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
// src/cli.ts
|
|
96
|
-
import { defineCommand as
|
|
107
|
+
import { defineCommand as defineCommand61, runMain } from "citty";
|
|
97
108
|
|
|
98
109
|
// src/commands/auth/login.ts
|
|
99
110
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -381,7 +392,7 @@ async function loginWithPKCE(idp) {
|
|
|
381
392
|
async function loginWithKey(idp, keyPath, agentEmail) {
|
|
382
393
|
const { readFileSync: readFileSync15 } = await import("fs");
|
|
383
394
|
const { sign: sign3 } = await import("crypto");
|
|
384
|
-
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-
|
|
395
|
+
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-6X3YZXSD.js");
|
|
385
396
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
386
397
|
const challengeResp = await fetch(challengeUrl, {
|
|
387
398
|
method: "POST",
|
|
@@ -760,7 +771,7 @@ async function waitForApproval(grantsUrl, grantId) {
|
|
|
760
771
|
if (grant.status === "revoked") {
|
|
761
772
|
throw new CliError("Grant revoked.");
|
|
762
773
|
}
|
|
763
|
-
await new Promise((
|
|
774
|
+
await new Promise((r3) => setTimeout(r3, interval));
|
|
764
775
|
}
|
|
765
776
|
throw new CliError("Timed out waiting for approval.");
|
|
766
777
|
}
|
|
@@ -1108,12 +1119,12 @@ var revokeCommand = defineCommand11({
|
|
|
1108
1119
|
{ method: "POST", body: { operations }, token }
|
|
1109
1120
|
);
|
|
1110
1121
|
let succeeded = 0;
|
|
1111
|
-
for (const
|
|
1112
|
-
if (
|
|
1113
|
-
consola11.success(`Grant ${
|
|
1122
|
+
for (const r3 of results) {
|
|
1123
|
+
if (r3.success) {
|
|
1124
|
+
consola11.success(`Grant ${r3.id} revoked.`);
|
|
1114
1125
|
succeeded++;
|
|
1115
1126
|
} else {
|
|
1116
|
-
consola11.error(`Grant ${
|
|
1127
|
+
consola11.error(`Grant ${r3.id}: ${r3.error?.title || "Failed"}`);
|
|
1117
1128
|
}
|
|
1118
1129
|
}
|
|
1119
1130
|
if (succeeded < results.length) {
|
|
@@ -1133,32 +1144,32 @@ import consola12 from "consola";
|
|
|
1133
1144
|
function getPollIntervalSeconds() {
|
|
1134
1145
|
const envValue = process.env.APES_GRANT_POLL_INTERVAL;
|
|
1135
1146
|
if (envValue) {
|
|
1136
|
-
const
|
|
1137
|
-
if (Number.isFinite(
|
|
1138
|
-
return Math.floor(
|
|
1147
|
+
const n2 = Number(envValue);
|
|
1148
|
+
if (Number.isFinite(n2) && n2 > 0)
|
|
1149
|
+
return Math.floor(n2);
|
|
1139
1150
|
}
|
|
1140
1151
|
const cfg = loadConfig();
|
|
1141
1152
|
const cfgValue = cfg.defaults?.grant_poll_interval_seconds;
|
|
1142
1153
|
if (cfgValue) {
|
|
1143
|
-
const
|
|
1144
|
-
if (Number.isFinite(
|
|
1145
|
-
return Math.floor(
|
|
1154
|
+
const n2 = Number(cfgValue);
|
|
1155
|
+
if (Number.isFinite(n2) && n2 > 0)
|
|
1156
|
+
return Math.floor(n2);
|
|
1146
1157
|
}
|
|
1147
1158
|
return 10;
|
|
1148
1159
|
}
|
|
1149
1160
|
function getPollMaxMinutes() {
|
|
1150
1161
|
const envValue = process.env.APES_GRANT_POLL_MAX_MINUTES;
|
|
1151
1162
|
if (envValue) {
|
|
1152
|
-
const
|
|
1153
|
-
if (Number.isFinite(
|
|
1154
|
-
return Math.floor(
|
|
1163
|
+
const n2 = Number(envValue);
|
|
1164
|
+
if (Number.isFinite(n2) && n2 > 0)
|
|
1165
|
+
return Math.floor(n2);
|
|
1155
1166
|
}
|
|
1156
1167
|
const cfg = loadConfig();
|
|
1157
1168
|
const cfgValue = cfg.defaults?.grant_poll_max_minutes;
|
|
1158
1169
|
if (cfgValue) {
|
|
1159
|
-
const
|
|
1160
|
-
if (Number.isFinite(
|
|
1161
|
-
return Math.floor(
|
|
1170
|
+
const n2 = Number(cfgValue);
|
|
1171
|
+
if (Number.isFinite(n2) && n2 > 0)
|
|
1172
|
+
return Math.floor(n2);
|
|
1162
1173
|
}
|
|
1163
1174
|
return 5;
|
|
1164
1175
|
}
|
|
@@ -1175,7 +1186,7 @@ async function pollGrantUntilResolved(idp, grantId) {
|
|
|
1175
1186
|
return { kind: "approved" };
|
|
1176
1187
|
if (grant.status === "denied" || grant.status === "revoked" || grant.status === "used")
|
|
1177
1188
|
return { kind: "terminal", status: grant.status };
|
|
1178
|
-
await new Promise((
|
|
1189
|
+
await new Promise((r3) => setTimeout(r3, intervalMs));
|
|
1179
1190
|
}
|
|
1180
1191
|
return { kind: "timeout" };
|
|
1181
1192
|
}
|
|
@@ -1355,7 +1366,7 @@ var delegateCommand = defineCommand14({
|
|
|
1355
1366
|
const body = {
|
|
1356
1367
|
delegate: args.to,
|
|
1357
1368
|
audience: args.at,
|
|
1358
|
-
|
|
1369
|
+
grant_type: args.approval
|
|
1359
1370
|
};
|
|
1360
1371
|
if (args.scopes) {
|
|
1361
1372
|
body.scopes = args.scopes.split(",").map((s) => s.trim());
|
|
@@ -1754,16 +1765,798 @@ import consola18 from "consola";
|
|
|
1754
1765
|
// src/lib/agent-bootstrap.ts
|
|
1755
1766
|
import { Buffer as Buffer3 } from "buffer";
|
|
1756
1767
|
import { createPrivateKey, sign } from "crypto";
|
|
1768
|
+
|
|
1769
|
+
// ../../node_modules/.pnpm/ofetch@1.5.1/node_modules/ofetch/dist/node.mjs
|
|
1770
|
+
import http from "http";
|
|
1771
|
+
import https from "https";
|
|
1772
|
+
|
|
1773
|
+
// ../../node_modules/.pnpm/node-fetch-native@1.6.7/node_modules/node-fetch-native/dist/index.mjs
|
|
1774
|
+
import "http";
|
|
1775
|
+
import "https";
|
|
1776
|
+
import "zlib";
|
|
1777
|
+
import "stream";
|
|
1778
|
+
import "buffer";
|
|
1779
|
+
import "util";
|
|
1780
|
+
import "url";
|
|
1781
|
+
import "net";
|
|
1782
|
+
import "fs";
|
|
1783
|
+
import "path";
|
|
1784
|
+
var o = !!globalThis.process?.env?.FORCE_NODE_FETCH;
|
|
1785
|
+
var r = !o && globalThis.fetch || Mi;
|
|
1786
|
+
var p = !o && globalThis.Blob || ut;
|
|
1787
|
+
var F = !o && globalThis.File || qn;
|
|
1788
|
+
var h = !o && globalThis.FormData || br;
|
|
1789
|
+
var n = !o && globalThis.Headers || ye;
|
|
1790
|
+
var c = !o && globalThis.Request || dt;
|
|
1791
|
+
var R = !o && globalThis.Response || le;
|
|
1792
|
+
var T = !o && globalThis.AbortController || Mn;
|
|
1793
|
+
|
|
1794
|
+
// ../../node_modules/.pnpm/destr@2.0.5/node_modules/destr/dist/index.mjs
|
|
1795
|
+
var suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
|
|
1796
|
+
var suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
|
|
1797
|
+
var JsonSigRx = /^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;
|
|
1798
|
+
function jsonParseTransform(key, value) {
|
|
1799
|
+
if (key === "__proto__" || key === "constructor" && value && typeof value === "object" && "prototype" in value) {
|
|
1800
|
+
warnKeyDropped(key);
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
return value;
|
|
1804
|
+
}
|
|
1805
|
+
function warnKeyDropped(key) {
|
|
1806
|
+
console.warn(`[destr] Dropping "${key}" key to prevent prototype pollution.`);
|
|
1807
|
+
}
|
|
1808
|
+
function destr(value, options = {}) {
|
|
1809
|
+
if (typeof value !== "string") {
|
|
1810
|
+
return value;
|
|
1811
|
+
}
|
|
1812
|
+
if (value[0] === '"' && value[value.length - 1] === '"' && value.indexOf("\\") === -1) {
|
|
1813
|
+
return value.slice(1, -1);
|
|
1814
|
+
}
|
|
1815
|
+
const _value = value.trim();
|
|
1816
|
+
if (_value.length <= 9) {
|
|
1817
|
+
switch (_value.toLowerCase()) {
|
|
1818
|
+
case "true": {
|
|
1819
|
+
return true;
|
|
1820
|
+
}
|
|
1821
|
+
case "false": {
|
|
1822
|
+
return false;
|
|
1823
|
+
}
|
|
1824
|
+
case "undefined": {
|
|
1825
|
+
return void 0;
|
|
1826
|
+
}
|
|
1827
|
+
case "null": {
|
|
1828
|
+
return null;
|
|
1829
|
+
}
|
|
1830
|
+
case "nan": {
|
|
1831
|
+
return Number.NaN;
|
|
1832
|
+
}
|
|
1833
|
+
case "infinity": {
|
|
1834
|
+
return Number.POSITIVE_INFINITY;
|
|
1835
|
+
}
|
|
1836
|
+
case "-infinity": {
|
|
1837
|
+
return Number.NEGATIVE_INFINITY;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
if (!JsonSigRx.test(value)) {
|
|
1842
|
+
if (options.strict) {
|
|
1843
|
+
throw new SyntaxError("[destr] Invalid JSON");
|
|
1844
|
+
}
|
|
1845
|
+
return value;
|
|
1846
|
+
}
|
|
1847
|
+
try {
|
|
1848
|
+
if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) {
|
|
1849
|
+
if (options.strict) {
|
|
1850
|
+
throw new Error("[destr] Possible prototype pollution");
|
|
1851
|
+
}
|
|
1852
|
+
return JSON.parse(value, jsonParseTransform);
|
|
1853
|
+
}
|
|
1854
|
+
return JSON.parse(value);
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
if (options.strict) {
|
|
1857
|
+
throw error;
|
|
1858
|
+
}
|
|
1859
|
+
return value;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// ../../node_modules/.pnpm/ufo@1.6.3/node_modules/ufo/dist/index.mjs
|
|
1864
|
+
var r2 = String.fromCharCode;
|
|
1865
|
+
var HASH_RE = /#/g;
|
|
1866
|
+
var AMPERSAND_RE = /&/g;
|
|
1867
|
+
var SLASH_RE = /\//g;
|
|
1868
|
+
var EQUAL_RE = /=/g;
|
|
1869
|
+
var PLUS_RE = /\+/g;
|
|
1870
|
+
var ENC_CARET_RE = /%5e/gi;
|
|
1871
|
+
var ENC_BACKTICK_RE = /%60/gi;
|
|
1872
|
+
var ENC_PIPE_RE = /%7c/gi;
|
|
1873
|
+
var ENC_SPACE_RE = /%20/gi;
|
|
1874
|
+
function encode(text) {
|
|
1875
|
+
return encodeURI("" + text).replace(ENC_PIPE_RE, "|");
|
|
1876
|
+
}
|
|
1877
|
+
function encodeQueryValue(input) {
|
|
1878
|
+
return encode(typeof input === "string" ? input : JSON.stringify(input)).replace(PLUS_RE, "%2B").replace(ENC_SPACE_RE, "+").replace(HASH_RE, "%23").replace(AMPERSAND_RE, "%26").replace(ENC_BACKTICK_RE, "`").replace(ENC_CARET_RE, "^").replace(SLASH_RE, "%2F");
|
|
1879
|
+
}
|
|
1880
|
+
function encodeQueryKey(text) {
|
|
1881
|
+
return encodeQueryValue(text).replace(EQUAL_RE, "%3D");
|
|
1882
|
+
}
|
|
1883
|
+
function decode(text = "") {
|
|
1884
|
+
try {
|
|
1885
|
+
return decodeURIComponent("" + text);
|
|
1886
|
+
} catch {
|
|
1887
|
+
return "" + text;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
function decodeQueryKey(text) {
|
|
1891
|
+
return decode(text.replace(PLUS_RE, " "));
|
|
1892
|
+
}
|
|
1893
|
+
function decodeQueryValue(text) {
|
|
1894
|
+
return decode(text.replace(PLUS_RE, " "));
|
|
1895
|
+
}
|
|
1896
|
+
function parseQuery(parametersString = "") {
|
|
1897
|
+
const object = /* @__PURE__ */ Object.create(null);
|
|
1898
|
+
if (parametersString[0] === "?") {
|
|
1899
|
+
parametersString = parametersString.slice(1);
|
|
1900
|
+
}
|
|
1901
|
+
for (const parameter of parametersString.split("&")) {
|
|
1902
|
+
const s = parameter.match(/([^=]+)=?(.*)/) || [];
|
|
1903
|
+
if (s.length < 2) {
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
const key = decodeQueryKey(s[1]);
|
|
1907
|
+
if (key === "__proto__" || key === "constructor") {
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
const value = decodeQueryValue(s[2] || "");
|
|
1911
|
+
if (object[key] === void 0) {
|
|
1912
|
+
object[key] = value;
|
|
1913
|
+
} else if (Array.isArray(object[key])) {
|
|
1914
|
+
object[key].push(value);
|
|
1915
|
+
} else {
|
|
1916
|
+
object[key] = [object[key], value];
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
return object;
|
|
1920
|
+
}
|
|
1921
|
+
function encodeQueryItem(key, value) {
|
|
1922
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
1923
|
+
value = String(value);
|
|
1924
|
+
}
|
|
1925
|
+
if (!value) {
|
|
1926
|
+
return encodeQueryKey(key);
|
|
1927
|
+
}
|
|
1928
|
+
if (Array.isArray(value)) {
|
|
1929
|
+
return value.map(
|
|
1930
|
+
(_value) => `${encodeQueryKey(key)}=${encodeQueryValue(_value)}`
|
|
1931
|
+
).join("&");
|
|
1932
|
+
}
|
|
1933
|
+
return `${encodeQueryKey(key)}=${encodeQueryValue(value)}`;
|
|
1934
|
+
}
|
|
1935
|
+
function stringifyQuery(query) {
|
|
1936
|
+
return Object.keys(query).filter((k) => query[k] !== void 0).map((k) => encodeQueryItem(k, query[k])).filter(Boolean).join("&");
|
|
1937
|
+
}
|
|
1938
|
+
var PROTOCOL_STRICT_REGEX = /^[\s\w\0+.-]{2,}:([/\\]{1,2})/;
|
|
1939
|
+
var PROTOCOL_REGEX = /^[\s\w\0+.-]{2,}:([/\\]{2})?/;
|
|
1940
|
+
var PROTOCOL_RELATIVE_REGEX = /^([/\\]\s*){2,}[^/\\]/;
|
|
1941
|
+
var TRAILING_SLASH_RE = /\/$|\/\?|\/#/;
|
|
1942
|
+
var JOIN_LEADING_SLASH_RE = /^\.?\//;
|
|
1943
|
+
function hasProtocol(inputString, opts = {}) {
|
|
1944
|
+
if (typeof opts === "boolean") {
|
|
1945
|
+
opts = { acceptRelative: opts };
|
|
1946
|
+
}
|
|
1947
|
+
if (opts.strict) {
|
|
1948
|
+
return PROTOCOL_STRICT_REGEX.test(inputString);
|
|
1949
|
+
}
|
|
1950
|
+
return PROTOCOL_REGEX.test(inputString) || (opts.acceptRelative ? PROTOCOL_RELATIVE_REGEX.test(inputString) : false);
|
|
1951
|
+
}
|
|
1952
|
+
function hasTrailingSlash(input = "", respectQueryAndFragment) {
|
|
1953
|
+
if (!respectQueryAndFragment) {
|
|
1954
|
+
return input.endsWith("/");
|
|
1955
|
+
}
|
|
1956
|
+
return TRAILING_SLASH_RE.test(input);
|
|
1957
|
+
}
|
|
1958
|
+
function withoutTrailingSlash(input = "", respectQueryAndFragment) {
|
|
1959
|
+
if (!respectQueryAndFragment) {
|
|
1960
|
+
return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || "/";
|
|
1961
|
+
}
|
|
1962
|
+
if (!hasTrailingSlash(input, true)) {
|
|
1963
|
+
return input || "/";
|
|
1964
|
+
}
|
|
1965
|
+
let path2 = input;
|
|
1966
|
+
let fragment = "";
|
|
1967
|
+
const fragmentIndex = input.indexOf("#");
|
|
1968
|
+
if (fragmentIndex !== -1) {
|
|
1969
|
+
path2 = input.slice(0, fragmentIndex);
|
|
1970
|
+
fragment = input.slice(fragmentIndex);
|
|
1971
|
+
}
|
|
1972
|
+
const [s0, ...s] = path2.split("?");
|
|
1973
|
+
const cleanPath = s0.endsWith("/") ? s0.slice(0, -1) : s0;
|
|
1974
|
+
return (cleanPath || "/") + (s.length > 0 ? `?${s.join("?")}` : "") + fragment;
|
|
1975
|
+
}
|
|
1976
|
+
function withTrailingSlash(input = "", respectQueryAndFragment) {
|
|
1977
|
+
if (!respectQueryAndFragment) {
|
|
1978
|
+
return input.endsWith("/") ? input : input + "/";
|
|
1979
|
+
}
|
|
1980
|
+
if (hasTrailingSlash(input, true)) {
|
|
1981
|
+
return input || "/";
|
|
1982
|
+
}
|
|
1983
|
+
let path2 = input;
|
|
1984
|
+
let fragment = "";
|
|
1985
|
+
const fragmentIndex = input.indexOf("#");
|
|
1986
|
+
if (fragmentIndex !== -1) {
|
|
1987
|
+
path2 = input.slice(0, fragmentIndex);
|
|
1988
|
+
fragment = input.slice(fragmentIndex);
|
|
1989
|
+
if (!path2) {
|
|
1990
|
+
return fragment;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
const [s0, ...s] = path2.split("?");
|
|
1994
|
+
return s0 + "/" + (s.length > 0 ? `?${s.join("?")}` : "") + fragment;
|
|
1995
|
+
}
|
|
1996
|
+
function withBase(input, base) {
|
|
1997
|
+
if (isEmptyURL(base) || hasProtocol(input)) {
|
|
1998
|
+
return input;
|
|
1999
|
+
}
|
|
2000
|
+
const _base = withoutTrailingSlash(base);
|
|
2001
|
+
if (input.startsWith(_base)) {
|
|
2002
|
+
const nextChar = input[_base.length];
|
|
2003
|
+
if (!nextChar || nextChar === "/" || nextChar === "?") {
|
|
2004
|
+
return input;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
return joinURL(_base, input);
|
|
2008
|
+
}
|
|
2009
|
+
function withQuery(input, query) {
|
|
2010
|
+
const parsed = parseURL(input);
|
|
2011
|
+
const mergedQuery = { ...parseQuery(parsed.search), ...query };
|
|
2012
|
+
parsed.search = stringifyQuery(mergedQuery);
|
|
2013
|
+
return stringifyParsedURL(parsed);
|
|
2014
|
+
}
|
|
2015
|
+
function isEmptyURL(url) {
|
|
2016
|
+
return !url || url === "/";
|
|
2017
|
+
}
|
|
2018
|
+
function isNonEmptyURL(url) {
|
|
2019
|
+
return url && url !== "/";
|
|
2020
|
+
}
|
|
2021
|
+
function joinURL(base, ...input) {
|
|
2022
|
+
let url = base || "";
|
|
2023
|
+
for (const segment of input.filter((url2) => isNonEmptyURL(url2))) {
|
|
2024
|
+
if (url) {
|
|
2025
|
+
const _segment = segment.replace(JOIN_LEADING_SLASH_RE, "");
|
|
2026
|
+
url = withTrailingSlash(url) + _segment;
|
|
2027
|
+
} else {
|
|
2028
|
+
url = segment;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
return url;
|
|
2032
|
+
}
|
|
2033
|
+
var protocolRelative = /* @__PURE__ */ Symbol.for("ufo:protocolRelative");
|
|
2034
|
+
function parseURL(input = "", defaultProto) {
|
|
2035
|
+
const _specialProtoMatch = input.match(
|
|
2036
|
+
/^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i
|
|
2037
|
+
);
|
|
2038
|
+
if (_specialProtoMatch) {
|
|
2039
|
+
const [, _proto, _pathname = ""] = _specialProtoMatch;
|
|
2040
|
+
return {
|
|
2041
|
+
protocol: _proto.toLowerCase(),
|
|
2042
|
+
pathname: _pathname,
|
|
2043
|
+
href: _proto + _pathname,
|
|
2044
|
+
auth: "",
|
|
2045
|
+
host: "",
|
|
2046
|
+
search: "",
|
|
2047
|
+
hash: ""
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
if (!hasProtocol(input, { acceptRelative: true })) {
|
|
2051
|
+
return defaultProto ? parseURL(defaultProto + input) : parsePath(input);
|
|
2052
|
+
}
|
|
2053
|
+
const [, protocol = "", auth, hostAndPath = ""] = input.replace(/\\/g, "/").match(/^[\s\0]*([\w+.-]{2,}:)?\/\/([^/@]+@)?(.*)/) || [];
|
|
2054
|
+
let [, host = "", path2 = ""] = hostAndPath.match(/([^#/?]*)(.*)?/) || [];
|
|
2055
|
+
if (protocol === "file:") {
|
|
2056
|
+
path2 = path2.replace(/\/(?=[A-Za-z]:)/, "");
|
|
2057
|
+
}
|
|
2058
|
+
const { pathname, search, hash } = parsePath(path2);
|
|
2059
|
+
return {
|
|
2060
|
+
protocol: protocol.toLowerCase(),
|
|
2061
|
+
auth: auth ? auth.slice(0, Math.max(0, auth.length - 1)) : "",
|
|
2062
|
+
host,
|
|
2063
|
+
pathname,
|
|
2064
|
+
search,
|
|
2065
|
+
hash,
|
|
2066
|
+
[protocolRelative]: !protocol
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function parsePath(input = "") {
|
|
2070
|
+
const [pathname = "", search = "", hash = ""] = (input.match(/([^#?]*)(\?[^#]*)?(#.*)?/) || []).splice(1);
|
|
2071
|
+
return {
|
|
2072
|
+
pathname,
|
|
2073
|
+
search,
|
|
2074
|
+
hash
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function stringifyParsedURL(parsed) {
|
|
2078
|
+
const pathname = parsed.pathname || "";
|
|
2079
|
+
const search = parsed.search ? (parsed.search.startsWith("?") ? "" : "?") + parsed.search : "";
|
|
2080
|
+
const hash = parsed.hash || "";
|
|
2081
|
+
const auth = parsed.auth ? parsed.auth + "@" : "";
|
|
2082
|
+
const host = parsed.host || "";
|
|
2083
|
+
const proto = parsed.protocol || parsed[protocolRelative] ? (parsed.protocol || "") + "//" : "";
|
|
2084
|
+
return proto + auth + host + pathname + search + hash;
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// ../../node_modules/.pnpm/ofetch@1.5.1/node_modules/ofetch/dist/shared/ofetch.CWycOUEr.mjs
|
|
2088
|
+
var FetchError = class extends Error {
|
|
2089
|
+
constructor(message, opts) {
|
|
2090
|
+
super(message, opts);
|
|
2091
|
+
this.name = "FetchError";
|
|
2092
|
+
if (opts?.cause && !this.cause) {
|
|
2093
|
+
this.cause = opts.cause;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
function createFetchError(ctx) {
|
|
2098
|
+
const errorMessage = ctx.error?.message || ctx.error?.toString() || "";
|
|
2099
|
+
const method = ctx.request?.method || ctx.options?.method || "GET";
|
|
2100
|
+
const url = ctx.request?.url || String(ctx.request) || "/";
|
|
2101
|
+
const requestStr = `[${method}] ${JSON.stringify(url)}`;
|
|
2102
|
+
const statusStr = ctx.response ? `${ctx.response.status} ${ctx.response.statusText}` : "<no response>";
|
|
2103
|
+
const message = `${requestStr}: ${statusStr}${errorMessage ? ` ${errorMessage}` : ""}`;
|
|
2104
|
+
const fetchError = new FetchError(
|
|
2105
|
+
message,
|
|
2106
|
+
ctx.error ? { cause: ctx.error } : void 0
|
|
2107
|
+
);
|
|
2108
|
+
for (const key of ["request", "options", "response"]) {
|
|
2109
|
+
Object.defineProperty(fetchError, key, {
|
|
2110
|
+
get() {
|
|
2111
|
+
return ctx[key];
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
for (const [key, refKey] of [
|
|
2116
|
+
["data", "_data"],
|
|
2117
|
+
["status", "status"],
|
|
2118
|
+
["statusCode", "status"],
|
|
2119
|
+
["statusText", "statusText"],
|
|
2120
|
+
["statusMessage", "statusText"]
|
|
2121
|
+
]) {
|
|
2122
|
+
Object.defineProperty(fetchError, key, {
|
|
2123
|
+
get() {
|
|
2124
|
+
return ctx.response && ctx.response[refKey];
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
return fetchError;
|
|
2129
|
+
}
|
|
2130
|
+
var payloadMethods = new Set(
|
|
2131
|
+
Object.freeze(["PATCH", "POST", "PUT", "DELETE"])
|
|
2132
|
+
);
|
|
2133
|
+
function isPayloadMethod(method = "GET") {
|
|
2134
|
+
return payloadMethods.has(method.toUpperCase());
|
|
2135
|
+
}
|
|
2136
|
+
function isJSONSerializable(value) {
|
|
2137
|
+
if (value === void 0) {
|
|
2138
|
+
return false;
|
|
2139
|
+
}
|
|
2140
|
+
const t = typeof value;
|
|
2141
|
+
if (t === "string" || t === "number" || t === "boolean" || t === null) {
|
|
2142
|
+
return true;
|
|
2143
|
+
}
|
|
2144
|
+
if (t !== "object") {
|
|
2145
|
+
return false;
|
|
2146
|
+
}
|
|
2147
|
+
if (Array.isArray(value)) {
|
|
2148
|
+
return true;
|
|
2149
|
+
}
|
|
2150
|
+
if (value.buffer) {
|
|
2151
|
+
return false;
|
|
2152
|
+
}
|
|
2153
|
+
if (value instanceof FormData || value instanceof URLSearchParams) {
|
|
2154
|
+
return false;
|
|
2155
|
+
}
|
|
2156
|
+
return value.constructor && value.constructor.name === "Object" || typeof value.toJSON === "function";
|
|
2157
|
+
}
|
|
2158
|
+
var textTypes = /* @__PURE__ */ new Set([
|
|
2159
|
+
"image/svg",
|
|
2160
|
+
"application/xml",
|
|
2161
|
+
"application/xhtml",
|
|
2162
|
+
"application/html"
|
|
2163
|
+
]);
|
|
2164
|
+
var JSON_RE = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;
|
|
2165
|
+
function detectResponseType(_contentType = "") {
|
|
2166
|
+
if (!_contentType) {
|
|
2167
|
+
return "json";
|
|
2168
|
+
}
|
|
2169
|
+
const contentType = _contentType.split(";").shift() || "";
|
|
2170
|
+
if (JSON_RE.test(contentType)) {
|
|
2171
|
+
return "json";
|
|
2172
|
+
}
|
|
2173
|
+
if (contentType === "text/event-stream") {
|
|
2174
|
+
return "stream";
|
|
2175
|
+
}
|
|
2176
|
+
if (textTypes.has(contentType) || contentType.startsWith("text/")) {
|
|
2177
|
+
return "text";
|
|
2178
|
+
}
|
|
2179
|
+
return "blob";
|
|
2180
|
+
}
|
|
2181
|
+
function resolveFetchOptions(request, input, defaults, Headers2) {
|
|
2182
|
+
const headers = mergeHeaders(
|
|
2183
|
+
input?.headers ?? request?.headers,
|
|
2184
|
+
defaults?.headers,
|
|
2185
|
+
Headers2
|
|
2186
|
+
);
|
|
2187
|
+
let query;
|
|
2188
|
+
if (defaults?.query || defaults?.params || input?.params || input?.query) {
|
|
2189
|
+
query = {
|
|
2190
|
+
...defaults?.params,
|
|
2191
|
+
...defaults?.query,
|
|
2192
|
+
...input?.params,
|
|
2193
|
+
...input?.query
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
return {
|
|
2197
|
+
...defaults,
|
|
2198
|
+
...input,
|
|
2199
|
+
query,
|
|
2200
|
+
params: query,
|
|
2201
|
+
headers
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
function mergeHeaders(input, defaults, Headers2) {
|
|
2205
|
+
if (!defaults) {
|
|
2206
|
+
return new Headers2(input);
|
|
2207
|
+
}
|
|
2208
|
+
const headers = new Headers2(defaults);
|
|
2209
|
+
if (input) {
|
|
2210
|
+
for (const [key, value] of Symbol.iterator in input || Array.isArray(input) ? input : new Headers2(input)) {
|
|
2211
|
+
headers.set(key, value);
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
return headers;
|
|
2215
|
+
}
|
|
2216
|
+
async function callHooks(context, hooks) {
|
|
2217
|
+
if (hooks) {
|
|
2218
|
+
if (Array.isArray(hooks)) {
|
|
2219
|
+
for (const hook of hooks) {
|
|
2220
|
+
await hook(context);
|
|
2221
|
+
}
|
|
2222
|
+
} else {
|
|
2223
|
+
await hooks(context);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
var retryStatusCodes = /* @__PURE__ */ new Set([
|
|
2228
|
+
408,
|
|
2229
|
+
// Request Timeout
|
|
2230
|
+
409,
|
|
2231
|
+
// Conflict
|
|
2232
|
+
425,
|
|
2233
|
+
// Too Early (Experimental)
|
|
2234
|
+
429,
|
|
2235
|
+
// Too Many Requests
|
|
2236
|
+
500,
|
|
2237
|
+
// Internal Server Error
|
|
2238
|
+
502,
|
|
2239
|
+
// Bad Gateway
|
|
2240
|
+
503,
|
|
2241
|
+
// Service Unavailable
|
|
2242
|
+
504
|
|
2243
|
+
// Gateway Timeout
|
|
2244
|
+
]);
|
|
2245
|
+
var nullBodyResponses = /* @__PURE__ */ new Set([101, 204, 205, 304]);
|
|
2246
|
+
function createFetch(globalOptions = {}) {
|
|
2247
|
+
const {
|
|
2248
|
+
fetch: fetch3 = globalThis.fetch,
|
|
2249
|
+
Headers: Headers2 = globalThis.Headers,
|
|
2250
|
+
AbortController: AbortController3 = globalThis.AbortController
|
|
2251
|
+
} = globalOptions;
|
|
2252
|
+
async function onError(context) {
|
|
2253
|
+
const isAbort = context.error && context.error.name === "AbortError" && !context.options.timeout || false;
|
|
2254
|
+
if (context.options.retry !== false && !isAbort) {
|
|
2255
|
+
let retries;
|
|
2256
|
+
if (typeof context.options.retry === "number") {
|
|
2257
|
+
retries = context.options.retry;
|
|
2258
|
+
} else {
|
|
2259
|
+
retries = isPayloadMethod(context.options.method) ? 0 : 1;
|
|
2260
|
+
}
|
|
2261
|
+
const responseCode = context.response && context.response.status || 500;
|
|
2262
|
+
if (retries > 0 && (Array.isArray(context.options.retryStatusCodes) ? context.options.retryStatusCodes.includes(responseCode) : retryStatusCodes.has(responseCode))) {
|
|
2263
|
+
const retryDelay = typeof context.options.retryDelay === "function" ? context.options.retryDelay(context) : context.options.retryDelay || 0;
|
|
2264
|
+
if (retryDelay > 0) {
|
|
2265
|
+
await new Promise((resolve5) => setTimeout(resolve5, retryDelay));
|
|
2266
|
+
}
|
|
2267
|
+
return $fetchRaw(context.request, {
|
|
2268
|
+
...context.options,
|
|
2269
|
+
retry: retries - 1
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
const error = createFetchError(context);
|
|
2274
|
+
if (Error.captureStackTrace) {
|
|
2275
|
+
Error.captureStackTrace(error, $fetchRaw);
|
|
2276
|
+
}
|
|
2277
|
+
throw error;
|
|
2278
|
+
}
|
|
2279
|
+
const $fetchRaw = async function $fetchRaw2(_request, _options = {}) {
|
|
2280
|
+
const context = {
|
|
2281
|
+
request: _request,
|
|
2282
|
+
options: resolveFetchOptions(
|
|
2283
|
+
_request,
|
|
2284
|
+
_options,
|
|
2285
|
+
globalOptions.defaults,
|
|
2286
|
+
Headers2
|
|
2287
|
+
),
|
|
2288
|
+
response: void 0,
|
|
2289
|
+
error: void 0
|
|
2290
|
+
};
|
|
2291
|
+
if (context.options.method) {
|
|
2292
|
+
context.options.method = context.options.method.toUpperCase();
|
|
2293
|
+
}
|
|
2294
|
+
if (context.options.onRequest) {
|
|
2295
|
+
await callHooks(context, context.options.onRequest);
|
|
2296
|
+
if (!(context.options.headers instanceof Headers2)) {
|
|
2297
|
+
context.options.headers = new Headers2(
|
|
2298
|
+
context.options.headers || {}
|
|
2299
|
+
/* compat */
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
if (typeof context.request === "string") {
|
|
2304
|
+
if (context.options.baseURL) {
|
|
2305
|
+
context.request = withBase(context.request, context.options.baseURL);
|
|
2306
|
+
}
|
|
2307
|
+
if (context.options.query) {
|
|
2308
|
+
context.request = withQuery(context.request, context.options.query);
|
|
2309
|
+
delete context.options.query;
|
|
2310
|
+
}
|
|
2311
|
+
if ("query" in context.options) {
|
|
2312
|
+
delete context.options.query;
|
|
2313
|
+
}
|
|
2314
|
+
if ("params" in context.options) {
|
|
2315
|
+
delete context.options.params;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
if (context.options.body && isPayloadMethod(context.options.method)) {
|
|
2319
|
+
if (isJSONSerializable(context.options.body)) {
|
|
2320
|
+
const contentType = context.options.headers.get("content-type");
|
|
2321
|
+
if (typeof context.options.body !== "string") {
|
|
2322
|
+
context.options.body = contentType === "application/x-www-form-urlencoded" ? new URLSearchParams(
|
|
2323
|
+
context.options.body
|
|
2324
|
+
).toString() : JSON.stringify(context.options.body);
|
|
2325
|
+
}
|
|
2326
|
+
if (!contentType) {
|
|
2327
|
+
context.options.headers.set("content-type", "application/json");
|
|
2328
|
+
}
|
|
2329
|
+
if (!context.options.headers.has("accept")) {
|
|
2330
|
+
context.options.headers.set("accept", "application/json");
|
|
2331
|
+
}
|
|
2332
|
+
} else if (
|
|
2333
|
+
// ReadableStream Body
|
|
2334
|
+
"pipeTo" in context.options.body && typeof context.options.body.pipeTo === "function" || // Node.js Stream Body
|
|
2335
|
+
typeof context.options.body.pipe === "function"
|
|
2336
|
+
) {
|
|
2337
|
+
if (!("duplex" in context.options)) {
|
|
2338
|
+
context.options.duplex = "half";
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
let abortTimeout;
|
|
2343
|
+
if (!context.options.signal && context.options.timeout) {
|
|
2344
|
+
const controller = new AbortController3();
|
|
2345
|
+
abortTimeout = setTimeout(() => {
|
|
2346
|
+
const error = new Error(
|
|
2347
|
+
"[TimeoutError]: The operation was aborted due to timeout"
|
|
2348
|
+
);
|
|
2349
|
+
error.name = "TimeoutError";
|
|
2350
|
+
error.code = 23;
|
|
2351
|
+
controller.abort(error);
|
|
2352
|
+
}, context.options.timeout);
|
|
2353
|
+
context.options.signal = controller.signal;
|
|
2354
|
+
}
|
|
2355
|
+
try {
|
|
2356
|
+
context.response = await fetch3(
|
|
2357
|
+
context.request,
|
|
2358
|
+
context.options
|
|
2359
|
+
);
|
|
2360
|
+
} catch (error) {
|
|
2361
|
+
context.error = error;
|
|
2362
|
+
if (context.options.onRequestError) {
|
|
2363
|
+
await callHooks(
|
|
2364
|
+
context,
|
|
2365
|
+
context.options.onRequestError
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
return await onError(context);
|
|
2369
|
+
} finally {
|
|
2370
|
+
if (abortTimeout) {
|
|
2371
|
+
clearTimeout(abortTimeout);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
const hasBody = (context.response.body || // https://github.com/unjs/ofetch/issues/324
|
|
2375
|
+
// https://github.com/unjs/ofetch/issues/294
|
|
2376
|
+
// https://github.com/JakeChampion/fetch/issues/1454
|
|
2377
|
+
context.response._bodyInit) && !nullBodyResponses.has(context.response.status) && context.options.method !== "HEAD";
|
|
2378
|
+
if (hasBody) {
|
|
2379
|
+
const responseType = (context.options.parseResponse ? "json" : context.options.responseType) || detectResponseType(context.response.headers.get("content-type") || "");
|
|
2380
|
+
switch (responseType) {
|
|
2381
|
+
case "json": {
|
|
2382
|
+
const data = await context.response.text();
|
|
2383
|
+
const parseFunction = context.options.parseResponse || destr;
|
|
2384
|
+
context.response._data = parseFunction(data);
|
|
2385
|
+
break;
|
|
2386
|
+
}
|
|
2387
|
+
case "stream": {
|
|
2388
|
+
context.response._data = context.response.body || context.response._bodyInit;
|
|
2389
|
+
break;
|
|
2390
|
+
}
|
|
2391
|
+
default: {
|
|
2392
|
+
context.response._data = await context.response[responseType]();
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
if (context.options.onResponse) {
|
|
2397
|
+
await callHooks(
|
|
2398
|
+
context,
|
|
2399
|
+
context.options.onResponse
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
if (!context.options.ignoreResponseError && context.response.status >= 400 && context.response.status < 600) {
|
|
2403
|
+
if (context.options.onResponseError) {
|
|
2404
|
+
await callHooks(
|
|
2405
|
+
context,
|
|
2406
|
+
context.options.onResponseError
|
|
2407
|
+
);
|
|
2408
|
+
}
|
|
2409
|
+
return await onError(context);
|
|
2410
|
+
}
|
|
2411
|
+
return context.response;
|
|
2412
|
+
};
|
|
2413
|
+
const $fetch = async function $fetch2(request, options) {
|
|
2414
|
+
const r3 = await $fetchRaw(request, options);
|
|
2415
|
+
return r3._data;
|
|
2416
|
+
};
|
|
2417
|
+
$fetch.raw = $fetchRaw;
|
|
2418
|
+
$fetch.native = (...args) => fetch3(...args);
|
|
2419
|
+
$fetch.create = (defaultOptions = {}, customGlobalOptions = {}) => createFetch({
|
|
2420
|
+
...globalOptions,
|
|
2421
|
+
...customGlobalOptions,
|
|
2422
|
+
defaults: {
|
|
2423
|
+
...globalOptions.defaults,
|
|
2424
|
+
...customGlobalOptions.defaults,
|
|
2425
|
+
...defaultOptions
|
|
2426
|
+
}
|
|
2427
|
+
});
|
|
2428
|
+
return $fetch;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// ../../node_modules/.pnpm/ofetch@1.5.1/node_modules/ofetch/dist/node.mjs
|
|
2432
|
+
function createNodeFetch() {
|
|
2433
|
+
const useKeepAlive = JSON.parse(process.env.FETCH_KEEP_ALIVE || "false");
|
|
2434
|
+
if (!useKeepAlive) {
|
|
2435
|
+
return r;
|
|
2436
|
+
}
|
|
2437
|
+
const agentOptions = { keepAlive: true };
|
|
2438
|
+
const httpAgent = new http.Agent(agentOptions);
|
|
2439
|
+
const httpsAgent = new https.Agent(agentOptions);
|
|
2440
|
+
const nodeFetchOptions = {
|
|
2441
|
+
agent(parsedURL) {
|
|
2442
|
+
return parsedURL.protocol === "http:" ? httpAgent : httpsAgent;
|
|
2443
|
+
}
|
|
2444
|
+
};
|
|
2445
|
+
return function nodeFetchWithKeepAlive(input, init) {
|
|
2446
|
+
return r(input, { ...nodeFetchOptions, ...init });
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
var fetch2 = globalThis.fetch ? (...args) => globalThis.fetch(...args) : createNodeFetch();
|
|
2450
|
+
var Headers = globalThis.Headers || n;
|
|
2451
|
+
var AbortController2 = globalThis.AbortController || T;
|
|
2452
|
+
var ofetch = createFetch({ fetch: fetch2, Headers, AbortController: AbortController2 });
|
|
2453
|
+
|
|
2454
|
+
// ../cli-auth/dist/index.js
|
|
2455
|
+
var AuthError = class extends Error {
|
|
2456
|
+
status;
|
|
2457
|
+
hint;
|
|
2458
|
+
constructor(status, message, hint) {
|
|
2459
|
+
super(hint ? `${message}
|
|
2460
|
+
${hint}` : message);
|
|
2461
|
+
this.name = "AuthError";
|
|
2462
|
+
this.status = status;
|
|
2463
|
+
this.hint = hint;
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2466
|
+
var TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
2467
|
+
var ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
|
|
2468
|
+
async function exchangeWithDelegation(req) {
|
|
2469
|
+
const url = `${req.idp.replace(/\/$/, "")}/api/oauth/token-exchange`;
|
|
2470
|
+
try {
|
|
2471
|
+
return await ofetch(url, {
|
|
2472
|
+
method: "POST",
|
|
2473
|
+
body: {
|
|
2474
|
+
grant_type: TOKEN_EXCHANGE_GRANT_TYPE,
|
|
2475
|
+
actor_token: req.actorToken,
|
|
2476
|
+
actor_token_type: ACCESS_TOKEN_TYPE,
|
|
2477
|
+
...req.subjectToken ? { subject_token: req.subjectToken, subject_token_type: ACCESS_TOKEN_TYPE } : {},
|
|
2478
|
+
...req.audience ? { audience: req.audience } : {},
|
|
2479
|
+
...req.delegationGrantId ? { delegation_grant_id: req.delegationGrantId } : {}
|
|
2480
|
+
}
|
|
2481
|
+
});
|
|
2482
|
+
} catch (err) {
|
|
2483
|
+
const status = err.status ?? err.statusCode ?? 0;
|
|
2484
|
+
const data = err.data;
|
|
2485
|
+
const title = data?.title ?? `Delegation token-exchange failed (HTTP ${status})`;
|
|
2486
|
+
throw new AuthError(status, title, data?.detail);
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// src/lib/agent-bootstrap.ts
|
|
1757
2491
|
var AGENT_NAME_REGEX = /^[a-z][a-z0-9-]{0,23}$/;
|
|
1758
2492
|
var SSH_ED25519_PREFIX = "ssh-ed25519 ";
|
|
1759
2493
|
var SSH_ED25519_REGEX = /^ssh-ed25519 [A-Za-z0-9+/=]+(\s.*)?$/;
|
|
2494
|
+
var ENROLL_AUDIENCE = "enroll-agent";
|
|
1760
2495
|
async function registerAgentAtIdp(input) {
|
|
2496
|
+
const delegated = await tryDelegatedEnrollToken(input.idp);
|
|
1761
2497
|
return await apiFetch("/api/enroll", {
|
|
1762
2498
|
method: "POST",
|
|
1763
2499
|
body: { name: input.name, publicKey: input.publicKey },
|
|
1764
|
-
idp: input.idp
|
|
2500
|
+
idp: input.idp,
|
|
2501
|
+
...delegated ? { headers: { Authorization: `Bearer ${delegated}` } } : {}
|
|
1765
2502
|
});
|
|
1766
2503
|
}
|
|
2504
|
+
async function tryDelegatedEnrollToken(idp) {
|
|
2505
|
+
try {
|
|
2506
|
+
const auth = loadAuth();
|
|
2507
|
+
if (!auth?.access_token) return null;
|
|
2508
|
+
const claims = decodeJwtClaims(auth.access_token);
|
|
2509
|
+
if (claims?.act !== "agent") return null;
|
|
2510
|
+
const ownerEmail = auth.owner_email;
|
|
2511
|
+
if (typeof ownerEmail !== "string" || !ownerEmail) return null;
|
|
2512
|
+
const myEmail = auth.email;
|
|
2513
|
+
if (typeof myEmail !== "string" || !myEmail) return null;
|
|
2514
|
+
const idpUrl = idp ?? auth.idp;
|
|
2515
|
+
if (!idpUrl) return null;
|
|
2516
|
+
const grantId = await findEnrollDelegationGrantId(idpUrl, ownerEmail, myEmail);
|
|
2517
|
+
if (!grantId) {
|
|
2518
|
+
console.warn(`[agent-bootstrap] no enroll-agent delegation from ${ownerEmail} to ${myEmail} \u2014 falling back to direct enroll`);
|
|
2519
|
+
return null;
|
|
2520
|
+
}
|
|
2521
|
+
const result = await exchangeWithDelegation({
|
|
2522
|
+
idp: idpUrl,
|
|
2523
|
+
actorToken: auth.access_token,
|
|
2524
|
+
audience: ENROLL_AUDIENCE,
|
|
2525
|
+
delegationGrantId: grantId
|
|
2526
|
+
});
|
|
2527
|
+
console.log(`[agent-bootstrap] using delegated token from grant ${grantId} (sub=${ownerEmail}, act=${myEmail})`);
|
|
2528
|
+
return result.access_token;
|
|
2529
|
+
} catch (err) {
|
|
2530
|
+
console.warn(`[agent-bootstrap] delegated-enroll exchange failed: ${err instanceof Error ? err.message : String(err)} \u2014 falling back to direct enroll`);
|
|
2531
|
+
return null;
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
async function findEnrollDelegationGrantId(idp, delegator, delegate) {
|
|
2535
|
+
const url = `${idp.replace(/\/$/, "")}/api/grants?status=approved&limit=200&requester=${encodeURIComponent(delegator)}`;
|
|
2536
|
+
const res = await apiFetch(url);
|
|
2537
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
2538
|
+
for (const g of res.data ?? []) {
|
|
2539
|
+
if (g.type !== "delegation") continue;
|
|
2540
|
+
if (g.status !== "approved") continue;
|
|
2541
|
+
if (g.request.delegate !== delegate) continue;
|
|
2542
|
+
const aud = g.request.audience;
|
|
2543
|
+
if (aud !== "*" && aud !== ENROLL_AUDIENCE) continue;
|
|
2544
|
+
if (g.expires_at && g.expires_at <= now) continue;
|
|
2545
|
+
return g.id;
|
|
2546
|
+
}
|
|
2547
|
+
return null;
|
|
2548
|
+
}
|
|
2549
|
+
function decodeJwtClaims(token) {
|
|
2550
|
+
try {
|
|
2551
|
+
const part = token.split(".")[1];
|
|
2552
|
+
if (!part) return null;
|
|
2553
|
+
const padded = part + "=".repeat((4 - part.length % 4) % 4);
|
|
2554
|
+
const json = Buffer3.from(padded, "base64").toString("utf8");
|
|
2555
|
+
return JSON.parse(json);
|
|
2556
|
+
} catch {
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
1767
2560
|
async function issueAgentToken(input) {
|
|
1768
2561
|
const privateKey = createPrivateKey(input.privateKeyPem);
|
|
1769
2562
|
const challengeUrl = await getAgentChallengeEndpoint(input.idp);
|
|
@@ -2423,16 +3216,16 @@ var listAgentsCommand = defineCommand22({
|
|
|
2423
3216
|
consola20.info(args["include-inactive"] ? "No agents found." : "No active agents found. Use --include-inactive to show deactivated.");
|
|
2424
3217
|
return;
|
|
2425
3218
|
}
|
|
2426
|
-
const nameW = Math.max(4, ...rows.map((
|
|
2427
|
-
const emailW = Math.max(5, ...rows.map((
|
|
3219
|
+
const nameW = Math.max(4, ...rows.map((r3) => r3.name.length));
|
|
3220
|
+
const emailW = Math.max(5, ...rows.map((r3) => r3.email.length));
|
|
2428
3221
|
const header = `${"NAME".padEnd(nameW)} ${"EMAIL".padEnd(emailW)} ACTIVE OS-USER HOME`;
|
|
2429
3222
|
console.log(header);
|
|
2430
3223
|
console.log("-".repeat(header.length));
|
|
2431
|
-
for (const
|
|
2432
|
-
const active =
|
|
2433
|
-
const os =
|
|
2434
|
-
const homeCol =
|
|
2435
|
-
console.log(`${
|
|
3224
|
+
for (const r3 of rows) {
|
|
3225
|
+
const active = r3.isActive ? "\u2713" : "\u2717";
|
|
3226
|
+
const os = r3.osUser ? "\u2713" : "\u2717";
|
|
3227
|
+
const homeCol = r3.home ?? (isDarwin() ? "(missing)" : "(non-darwin)");
|
|
3228
|
+
console.log(`${r3.name.padEnd(nameW)} ${r3.email.padEnd(emailW)} ${active.padEnd(6)} ${os.padEnd(7)} ${homeCol}`);
|
|
2436
3229
|
}
|
|
2437
3230
|
}
|
|
2438
3231
|
});
|
|
@@ -2559,12 +3352,12 @@ var fileTools = [
|
|
|
2559
3352
|
},
|
|
2560
3353
|
execute: async (args) => {
|
|
2561
3354
|
const a = args;
|
|
2562
|
-
const
|
|
2563
|
-
const content = readFileSync4(
|
|
3355
|
+
const p2 = jailPath(a.path);
|
|
3356
|
+
const content = readFileSync4(p2, "utf8");
|
|
2564
3357
|
if (Buffer.byteLength(content, "utf8") > MAX_BYTES) {
|
|
2565
|
-
return { path:
|
|
3358
|
+
return { path: p2, truncated: true, content: content.slice(0, MAX_BYTES) };
|
|
2566
3359
|
}
|
|
2567
|
-
return { path:
|
|
3360
|
+
return { path: p2, truncated: false, content };
|
|
2568
3361
|
}
|
|
2569
3362
|
},
|
|
2570
3363
|
{
|
|
@@ -2584,10 +3377,10 @@ var fileTools = [
|
|
|
2584
3377
|
if (Buffer.byteLength(a.content, "utf8") > MAX_BYTES) {
|
|
2585
3378
|
throw new Error(`content exceeds ${MAX_BYTES} byte cap`);
|
|
2586
3379
|
}
|
|
2587
|
-
const
|
|
2588
|
-
mkdirSync(dirname(
|
|
2589
|
-
writeFileSync2(
|
|
2590
|
-
return { path:
|
|
3380
|
+
const p2 = jailPath(a.path);
|
|
3381
|
+
mkdirSync(dirname(p2), { recursive: true });
|
|
3382
|
+
writeFileSync2(p2, a.content, { encoding: "utf8" });
|
|
3383
|
+
return { path: p2, bytes: Buffer.byteLength(a.content, "utf8") };
|
|
2591
3384
|
}
|
|
2592
3385
|
}
|
|
2593
3386
|
];
|
|
@@ -3076,7 +3869,7 @@ async function postRunResultToChat(opts) {
|
|
|
3076
3869
|
if (!contactsRes.ok) return;
|
|
3077
3870
|
const contacts = await contactsRes.json();
|
|
3078
3871
|
const ownerLower = opts.ownerEmail.toLowerCase();
|
|
3079
|
-
const ownerRow = contacts.find((
|
|
3872
|
+
const ownerRow = contacts.find((c2) => c2.peerEmail.toLowerCase() === ownerLower && c2.connected && c2.roomId);
|
|
3080
3873
|
if (!ownerRow?.roomId) {
|
|
3081
3874
|
consola22.info("chat DM skipped \u2014 no active room with owner (accept the contact request in chat to enable)");
|
|
3082
3875
|
return;
|
|
@@ -3406,8 +4199,8 @@ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as rea
|
|
|
3406
4199
|
import { generateKeyPairSync } from "crypto";
|
|
3407
4200
|
import { homedir as homedir7 } from "os";
|
|
3408
4201
|
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
3409
|
-
function resolveKeyPath(
|
|
3410
|
-
return resolve3(
|
|
4202
|
+
function resolveKeyPath(p2) {
|
|
4203
|
+
return resolve3(p2.replace(/^~/, homedir7()));
|
|
3411
4204
|
}
|
|
3412
4205
|
function buildSshEd25519Line(rawPub) {
|
|
3413
4206
|
const keyTypeStr = "ssh-ed25519";
|
|
@@ -3684,7 +4477,15 @@ and try again.`
|
|
|
3684
4477
|
email: registration.email,
|
|
3685
4478
|
expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
|
|
3686
4479
|
keyPath: `${homeDir}/.ssh/id_ed25519`,
|
|
3687
|
-
|
|
4480
|
+
// The IdP resolves the owner transitively (when the caller
|
|
4481
|
+
// is itself an agent — e.g. a Nest spawning a child — the
|
|
4482
|
+
// human at the top of the chain becomes owner). Use the
|
|
4483
|
+
// server-resolved owner, not the local caller's auth.email,
|
|
4484
|
+
// otherwise the agent's auth.json will carry the Nest's
|
|
4485
|
+
// email and troop will reject sync calls because the
|
|
4486
|
+
// encoded owner-domain in the agent email doesn't match
|
|
4487
|
+
// the auth.json's owner_email domain.
|
|
4488
|
+
ownerEmail: registration.owner
|
|
3688
4489
|
});
|
|
3689
4490
|
const includeClaudeHook = !args["no-claude-hook"];
|
|
3690
4491
|
const claudeOauthToken = await resolveClaudeToken({
|
|
@@ -3924,7 +4725,7 @@ var agentsCommand = defineCommand28({
|
|
|
3924
4725
|
});
|
|
3925
4726
|
|
|
3926
4727
|
// src/commands/nest/index.ts
|
|
3927
|
-
import { defineCommand as
|
|
4728
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
3928
4729
|
|
|
3929
4730
|
// src/commands/nest/authorize.ts
|
|
3930
4731
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
@@ -4016,7 +4817,19 @@ var enrollNestCommand = defineCommand29({
|
|
|
4016
4817
|
|
|
4017
4818
|
// src/commands/nest/authorize.ts
|
|
4018
4819
|
var DEFAULT_ALLOW_PATTERNS = [
|
|
4019
|
-
//
|
|
4820
|
+
// Caller → Nest API gates. The new-style flow: `apes nest <op>`
|
|
4821
|
+
// requests a grant with audience='nest' and command=['nest','<op>',
|
|
4822
|
+
// ...]. Patrick's YOLO auto-approves these so the local Nest API
|
|
4823
|
+
// is gated cryptographically without a per-call human prompt.
|
|
4824
|
+
"nest status",
|
|
4825
|
+
"nest list",
|
|
4826
|
+
// spawn uses a wildcard-name grant (one approval, any name)
|
|
4827
|
+
"nest spawn",
|
|
4828
|
+
// destroy stays per-name (typed in command), so the YOLO pattern
|
|
4829
|
+
// matches the per-name shape `nest destroy igor18`.
|
|
4830
|
+
"nest destroy *",
|
|
4831
|
+
// Inner spawn/destroy grants the nest itself triggers via
|
|
4832
|
+
// `apes run --as root --wait -- apes agents spawn|destroy`.
|
|
4020
4833
|
"apes agents spawn *",
|
|
4021
4834
|
"apes agents destroy *",
|
|
4022
4835
|
"apes agents sync",
|
|
@@ -4027,11 +4840,12 @@ var DEFAULT_ALLOW_PATTERNS = [
|
|
|
4027
4840
|
// glob below limits the auto-approval to that exact lifecycle path
|
|
4028
4841
|
// — `bash *` would be unsafe.
|
|
4029
4842
|
"bash *apes-spawn-*setup.sh",
|
|
4030
|
-
// Bridge invocation
|
|
4031
|
-
//
|
|
4032
|
-
//
|
|
4033
|
-
//
|
|
4034
|
-
|
|
4843
|
+
// Bridge invocation. The grant request escapes-helper sends to the
|
|
4844
|
+
// IdP contains the *inner* command only — `apes run --as <agent> --`
|
|
4845
|
+
// is the wrapper that gets unwrapped before grant creation. So the
|
|
4846
|
+
// YOLO target string is just `openape-chat-bridge`, not the full
|
|
4847
|
+
// wrapped invocation.
|
|
4848
|
+
"openape-chat-bridge"
|
|
4035
4849
|
];
|
|
4036
4850
|
var authorizeNestCommand = defineCommand30({
|
|
4037
4851
|
meta: {
|
|
@@ -4079,13 +4893,148 @@ var authorizeNestCommand = defineCommand30({
|
|
|
4079
4893
|
}
|
|
4080
4894
|
});
|
|
4081
4895
|
|
|
4896
|
+
// src/commands/nest/destroy.ts
|
|
4897
|
+
import process2 from "process";
|
|
4898
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
4899
|
+
import consola28 from "consola";
|
|
4900
|
+
|
|
4901
|
+
// src/lib/nest-grant-flow.ts
|
|
4902
|
+
import { hostname as hostname5 } from "os";
|
|
4903
|
+
import consola27 from "consola";
|
|
4904
|
+
var NEST_AUDIENCE = "nest";
|
|
4905
|
+
async function requestNestGrant(opts) {
|
|
4906
|
+
const auth = loadAuth();
|
|
4907
|
+
if (!auth) {
|
|
4908
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
4909
|
+
}
|
|
4910
|
+
const idp = getIdpUrl(opts.idp) ?? void 0;
|
|
4911
|
+
if (!idp) {
|
|
4912
|
+
throw new CliError("No IdP URL resolved. Pass --idp or run `apes login` first.");
|
|
4913
|
+
}
|
|
4914
|
+
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4915
|
+
const targetHost = opts.targetHost ?? hostname5();
|
|
4916
|
+
const approval = opts.approval ?? "always";
|
|
4917
|
+
const reusableId = await findReusableNestGrant({
|
|
4918
|
+
grantsUrl,
|
|
4919
|
+
requester: auth.email,
|
|
4920
|
+
command: opts.command,
|
|
4921
|
+
targetHost
|
|
4922
|
+
});
|
|
4923
|
+
if (reusableId) {
|
|
4924
|
+
const { authz_jwt: authz_jwt2 } = await apiFetch(`${grantsUrl}/${reusableId}/token`, {
|
|
4925
|
+
method: "POST"
|
|
4926
|
+
});
|
|
4927
|
+
return authz_jwt2;
|
|
4928
|
+
}
|
|
4929
|
+
consola27.info(`Requesting nest grant: ${opts.command.join(" ")}`);
|
|
4930
|
+
const grant = await apiFetch(grantsUrl, {
|
|
4931
|
+
method: "POST",
|
|
4932
|
+
body: {
|
|
4933
|
+
requester: auth.email,
|
|
4934
|
+
target_host: targetHost,
|
|
4935
|
+
audience: NEST_AUDIENCE,
|
|
4936
|
+
grant_type: approval,
|
|
4937
|
+
command: opts.command,
|
|
4938
|
+
reason: opts.reason ?? opts.command.join(" ")
|
|
4939
|
+
}
|
|
4940
|
+
});
|
|
4941
|
+
let approved = grant.status === "approved";
|
|
4942
|
+
const maxWait = 15 * 60 * 1e3;
|
|
4943
|
+
const interval = 3e3;
|
|
4944
|
+
const start = Date.now();
|
|
4945
|
+
while (!approved && Date.now() - start < maxWait) {
|
|
4946
|
+
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
4947
|
+
if (status.status === "approved") {
|
|
4948
|
+
approved = true;
|
|
4949
|
+
break;
|
|
4950
|
+
}
|
|
4951
|
+
if (status.status === "denied" || status.status === "revoked") {
|
|
4952
|
+
throw new CliError(`Grant ${status.status}.`);
|
|
4953
|
+
}
|
|
4954
|
+
if (!approved) {
|
|
4955
|
+
consola27.info(`Waiting for approval: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4956
|
+
await new Promise((r3) => setTimeout(r3, interval));
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
if (!approved) {
|
|
4960
|
+
throw new CliError(
|
|
4961
|
+
`Grant approval timed out after 15 min (still pending). Check your DDISA inbox at ${idp}/grant-approval?grant_id=${grant.id}.`
|
|
4962
|
+
);
|
|
4963
|
+
}
|
|
4964
|
+
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
4965
|
+
method: "POST"
|
|
4966
|
+
});
|
|
4967
|
+
return authz_jwt;
|
|
4968
|
+
}
|
|
4969
|
+
function nestBaseUrl(port) {
|
|
4970
|
+
const p2 = port ?? Number(process.env.OPENAPE_NEST_PORT ?? 9091);
|
|
4971
|
+
return `http://127.0.0.1:${p2}`;
|
|
4972
|
+
}
|
|
4973
|
+
async function findReusableNestGrant(opts) {
|
|
4974
|
+
try {
|
|
4975
|
+
const grants = await apiFetch(
|
|
4976
|
+
`${opts.grantsUrl}?requester=${encodeURIComponent(opts.requester)}&status=approved&limit=50`
|
|
4977
|
+
);
|
|
4978
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
4979
|
+
const match = grants.data.find((g) => {
|
|
4980
|
+
const r3 = g.request;
|
|
4981
|
+
if (r3.audience !== NEST_AUDIENCE) return false;
|
|
4982
|
+
if (r3.target_host !== opts.targetHost) return false;
|
|
4983
|
+
if (r3.grant_type === "once") return false;
|
|
4984
|
+
if (r3.grant_type === "timed" && g.expires_at && g.expires_at <= now) return false;
|
|
4985
|
+
const cmd = r3.command ?? [];
|
|
4986
|
+
if (cmd.length !== opts.command.length) return false;
|
|
4987
|
+
return cmd.every((c2, i) => c2 === opts.command[i]);
|
|
4988
|
+
});
|
|
4989
|
+
return match?.id ?? null;
|
|
4990
|
+
} catch {
|
|
4991
|
+
return null;
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
|
|
4995
|
+
// src/commands/nest/destroy.ts
|
|
4996
|
+
var destroyNestCommand = defineCommand31({
|
|
4997
|
+
meta: {
|
|
4998
|
+
name: "destroy",
|
|
4999
|
+
description: "Tear down an agent on the local nest (removes macOS user, hard-deletes IdP record, drops bridge plist). Requires a DDISA `nest destroy <name>` grant."
|
|
5000
|
+
},
|
|
5001
|
+
args: {
|
|
5002
|
+
name: { type: "positional", required: true, description: "Agent name to destroy" },
|
|
5003
|
+
port: { type: "string", description: "Override nest port (default: 9091)" }
|
|
5004
|
+
},
|
|
5005
|
+
async run({ args }) {
|
|
5006
|
+
const name = String(args.name);
|
|
5007
|
+
const token = await requestNestGrant({ command: ["nest", "destroy", name] });
|
|
5008
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5009
|
+
try {
|
|
5010
|
+
const res = await fetch(`${base}/agents/${encodeURIComponent(name)}`, {
|
|
5011
|
+
method: "DELETE",
|
|
5012
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
5013
|
+
});
|
|
5014
|
+
if (!res.ok) {
|
|
5015
|
+
const text = await res.text().catch(() => "");
|
|
5016
|
+
throw new CliError(`nest DELETE /agents/${name} failed: ${res.status} ${text}`);
|
|
5017
|
+
}
|
|
5018
|
+
} catch (err) {
|
|
5019
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5020
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5021
|
+
consola28.error(`Nest daemon is not running at ${base}`);
|
|
5022
|
+
consola28.info(" Run: apes nest install");
|
|
5023
|
+
process2.exit(2);
|
|
5024
|
+
}
|
|
5025
|
+
throw err;
|
|
5026
|
+
}
|
|
5027
|
+
consola28.success(`Destroyed ${name}`);
|
|
5028
|
+
}
|
|
5029
|
+
});
|
|
5030
|
+
|
|
4082
5031
|
// src/commands/nest/install.ts
|
|
4083
5032
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4084
5033
|
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
4085
5034
|
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
4086
5035
|
import { dirname as dirname3, join as join10 } from "path";
|
|
4087
|
-
import { defineCommand as
|
|
4088
|
-
import
|
|
5036
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
5037
|
+
import consola29 from "consola";
|
|
4089
5038
|
|
|
4090
5039
|
// src/commands/nest/apes-agents-adapter.ts
|
|
4091
5040
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4202,7 +5151,7 @@ function installAdapter2() {
|
|
|
4202
5151
|
}
|
|
4203
5152
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4204
5153
|
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4205
|
-
|
|
5154
|
+
consola29.success(`Wrote shapes adapter ${target}`);
|
|
4206
5155
|
return true;
|
|
4207
5156
|
}
|
|
4208
5157
|
function findBinary(name) {
|
|
@@ -4212,12 +5161,12 @@ function findBinary(name) {
|
|
|
4212
5161
|
"/usr/local/bin",
|
|
4213
5162
|
"/usr/bin"
|
|
4214
5163
|
]) {
|
|
4215
|
-
const
|
|
4216
|
-
if (existsSync12(
|
|
5164
|
+
const p2 = join10(dir, name);
|
|
5165
|
+
if (existsSync12(p2)) return p2;
|
|
4217
5166
|
}
|
|
4218
5167
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4219
5168
|
}
|
|
4220
|
-
var installNestCommand =
|
|
5169
|
+
var installNestCommand = defineCommand32({
|
|
4221
5170
|
meta: {
|
|
4222
5171
|
name: "install",
|
|
4223
5172
|
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
@@ -4236,10 +5185,10 @@ var installNestCommand = defineCommand31({
|
|
|
4236
5185
|
}
|
|
4237
5186
|
const nestBin = findBinary("openape-nest");
|
|
4238
5187
|
const apesBin = findBinary("apes");
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
5188
|
+
consola29.info(`Installing nest at ${plistPath()}`);
|
|
5189
|
+
consola29.info(` nest binary: ${nestBin}`);
|
|
5190
|
+
consola29.info(` apes binary: ${apesBin}`);
|
|
5191
|
+
consola29.info(` HTTP port: ${port}`);
|
|
4243
5192
|
installAdapter2();
|
|
4244
5193
|
mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4245
5194
|
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
@@ -4251,9 +5200,9 @@ var installNestCommand = defineCommand31({
|
|
|
4251
5200
|
}
|
|
4252
5201
|
if (existing !== desired) {
|
|
4253
5202
|
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
4254
|
-
|
|
5203
|
+
consola29.success("Wrote launchd plist");
|
|
4255
5204
|
} else {
|
|
4256
|
-
|
|
5205
|
+
consola29.info("plist already up to date");
|
|
4257
5206
|
}
|
|
4258
5207
|
const uid = userInfo2().uid;
|
|
4259
5208
|
try {
|
|
@@ -4261,45 +5210,156 @@ var installNestCommand = defineCommand31({
|
|
|
4261
5210
|
} catch {
|
|
4262
5211
|
}
|
|
4263
5212
|
execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
5213
|
+
consola29.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
5214
|
+
consola29.info("");
|
|
5215
|
+
consola29.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
5216
|
+
consola29.info("");
|
|
5217
|
+
consola29.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
5218
|
+
consola29.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
5219
|
+
consola29.info("");
|
|
5220
|
+
consola29.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
5221
|
+
}
|
|
5222
|
+
});
|
|
5223
|
+
|
|
5224
|
+
// src/commands/nest/list.ts
|
|
5225
|
+
import process3 from "process";
|
|
5226
|
+
import { defineCommand as defineCommand33 } from "citty";
|
|
5227
|
+
import consola30 from "consola";
|
|
5228
|
+
var listNestCommand = defineCommand33({
|
|
5229
|
+
meta: {
|
|
5230
|
+
name: "list",
|
|
5231
|
+
description: "List agents registered with the local nest. Goes through DDISA grants \u2014 YOLO auto-approves under the standard nest policy."
|
|
5232
|
+
},
|
|
5233
|
+
args: {
|
|
5234
|
+
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
5235
|
+
json: { type: "boolean", description: "JSON output for scripts" }
|
|
5236
|
+
},
|
|
5237
|
+
async run({ args }) {
|
|
5238
|
+
const token = await requestNestGrant({ command: ["nest", "list"] });
|
|
5239
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5240
|
+
let resp;
|
|
5241
|
+
try {
|
|
5242
|
+
const res = await fetch(`${base}/agents`, {
|
|
5243
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
5244
|
+
});
|
|
5245
|
+
if (!res.ok) {
|
|
5246
|
+
const text = await res.text().catch(() => "");
|
|
5247
|
+
throw new CliError(`nest GET /agents failed: ${res.status} ${text}`);
|
|
5248
|
+
}
|
|
5249
|
+
resp = await res.json();
|
|
5250
|
+
} catch (err) {
|
|
5251
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5252
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5253
|
+
consola30.error(`Nest daemon is not running at ${base}`);
|
|
5254
|
+
consola30.info(" Run: apes nest install");
|
|
5255
|
+
process3.exit(2);
|
|
5256
|
+
}
|
|
5257
|
+
throw err;
|
|
5258
|
+
}
|
|
5259
|
+
if (args.json) {
|
|
5260
|
+
console.log(JSON.stringify(resp, null, 2));
|
|
5261
|
+
return;
|
|
5262
|
+
}
|
|
5263
|
+
if (resp.agents.length === 0) {
|
|
5264
|
+
consola30.info("(no agents registered with this nest)");
|
|
5265
|
+
return;
|
|
5266
|
+
}
|
|
5267
|
+
consola30.info(`${resp.agents.length} agent(s) registered with this nest:`);
|
|
5268
|
+
for (const a of resp.agents) {
|
|
5269
|
+
const bridge = a.bridge ? " bridge=on" : "";
|
|
5270
|
+
consola30.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
5273
|
+
});
|
|
5274
|
+
|
|
5275
|
+
// src/commands/nest/spawn.ts
|
|
5276
|
+
import process4 from "process";
|
|
5277
|
+
import { defineCommand as defineCommand34 } from "citty";
|
|
5278
|
+
import consola31 from "consola";
|
|
5279
|
+
var spawnNestCommand = defineCommand34({
|
|
5280
|
+
meta: {
|
|
5281
|
+
name: "spawn",
|
|
5282
|
+
description: "Spawn a new agent on the local nest. Requires a DDISA `nest spawn <name>` grant \u2014 auto-approved by Patrick's policy on first use, reused on subsequent calls."
|
|
5283
|
+
},
|
|
5284
|
+
args: {
|
|
5285
|
+
name: { type: "positional", required: true, description: "Agent name (lowercase, [a-z0-9-], max 24 chars)" },
|
|
5286
|
+
"no-bridge": { type: "boolean", description: "Skip installing the chat-bridge daemon (default: install it)" },
|
|
5287
|
+
"bridge-key": { type: "string", description: "Override LITELLM_API_KEY (default: read from ~/litellm/.env)" },
|
|
5288
|
+
"bridge-base-url": { type: "string", description: "Override LITELLM_BASE_URL (default: read from ~/litellm/.env)" },
|
|
5289
|
+
"bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" },
|
|
5290
|
+
"port": { type: "string", description: "Override nest port (default: 9091)" }
|
|
5291
|
+
},
|
|
5292
|
+
async run({ args }) {
|
|
5293
|
+
const name = String(args.name);
|
|
5294
|
+
const token = await requestNestGrant({ command: ["nest", "spawn"] });
|
|
5295
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5296
|
+
const reqBody = {
|
|
5297
|
+
name,
|
|
5298
|
+
bridge: !args["no-bridge"]
|
|
5299
|
+
};
|
|
5300
|
+
if (typeof args["bridge-key"] === "string") reqBody.bridgeKey = args["bridge-key"];
|
|
5301
|
+
if (typeof args["bridge-base-url"] === "string") reqBody.bridgeBaseUrl = args["bridge-base-url"];
|
|
5302
|
+
if (typeof args["bridge-model"] === "string") reqBody.bridgeModel = args["bridge-model"];
|
|
5303
|
+
let resp;
|
|
5304
|
+
try {
|
|
5305
|
+
const res = await fetch(`${base}/agents`, {
|
|
5306
|
+
method: "POST",
|
|
5307
|
+
headers: {
|
|
5308
|
+
"Authorization": `Bearer ${token}`,
|
|
5309
|
+
"Content-Type": "application/json"
|
|
5310
|
+
},
|
|
5311
|
+
body: JSON.stringify(reqBody)
|
|
5312
|
+
});
|
|
5313
|
+
if (!res.ok) {
|
|
5314
|
+
const text = await res.text().catch(() => "");
|
|
5315
|
+
throw new CliError(`nest POST /agents failed: ${res.status} ${text}`);
|
|
5316
|
+
}
|
|
5317
|
+
resp = await res.json();
|
|
5318
|
+
} catch (err) {
|
|
5319
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5320
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5321
|
+
consola31.error(`Nest daemon is not running at ${base}`);
|
|
5322
|
+
consola31.info(" Run: apes nest install");
|
|
5323
|
+
process4.exit(2);
|
|
5324
|
+
}
|
|
5325
|
+
throw err;
|
|
5326
|
+
}
|
|
5327
|
+
consola31.success(`Spawned ${resp.name} (uid=${resp.uid}, home=${resp.home})`);
|
|
4272
5328
|
}
|
|
4273
5329
|
});
|
|
4274
5330
|
|
|
4275
5331
|
// src/commands/nest/status.ts
|
|
4276
|
-
import
|
|
4277
|
-
import { defineCommand as
|
|
4278
|
-
import
|
|
4279
|
-
var
|
|
4280
|
-
var statusNestCommand = defineCommand32({
|
|
5332
|
+
import process5 from "process";
|
|
5333
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
5334
|
+
import consola32 from "consola";
|
|
5335
|
+
var statusNestCommand = defineCommand35({
|
|
4281
5336
|
meta: {
|
|
4282
5337
|
name: "status",
|
|
4283
|
-
description: "Print
|
|
5338
|
+
description: "Print health of the local nest-daemon (agents registered). Goes through DDISA grants."
|
|
4284
5339
|
},
|
|
4285
5340
|
args: {
|
|
4286
5341
|
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
4287
5342
|
json: { type: "boolean", description: "JSON output for scripts" }
|
|
4288
5343
|
},
|
|
4289
5344
|
async run({ args }) {
|
|
4290
|
-
const
|
|
4291
|
-
const
|
|
5345
|
+
const token = await requestNestGrant({ command: ["nest", "status"] });
|
|
5346
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
4292
5347
|
let status;
|
|
4293
5348
|
try {
|
|
4294
|
-
const res = await fetch(
|
|
4295
|
-
|
|
5349
|
+
const res = await fetch(`${base}/status`, {
|
|
5350
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
5351
|
+
});
|
|
5352
|
+
if (!res.ok) {
|
|
5353
|
+
const text = await res.text().catch(() => "");
|
|
5354
|
+
throw new CliError(`nest GET /status failed: ${res.status} ${text}`);
|
|
5355
|
+
}
|
|
4296
5356
|
status = await res.json();
|
|
4297
5357
|
} catch (err) {
|
|
4298
5358
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4299
5359
|
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
5360
|
+
consola32.error(`Nest daemon is not running at ${base}`);
|
|
5361
|
+
consola32.info(" Run: apes nest install");
|
|
5362
|
+
process5.exit(2);
|
|
4303
5363
|
}
|
|
4304
5364
|
throw err;
|
|
4305
5365
|
}
|
|
@@ -4307,34 +5367,19 @@ var statusNestCommand = defineCommand32({
|
|
|
4307
5367
|
console.log(JSON.stringify(status, null, 2));
|
|
4308
5368
|
return;
|
|
4309
5369
|
}
|
|
4310
|
-
|
|
4311
|
-
if (status.processes.length === 0) {
|
|
4312
|
-
consola28.info(" (no processes running)");
|
|
4313
|
-
return;
|
|
4314
|
-
}
|
|
4315
|
-
for (const p of status.processes) {
|
|
4316
|
-
const uptime = humanDuration(p.uptimeSec);
|
|
4317
|
-
const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
|
|
4318
|
-
consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
|
|
4319
|
-
}
|
|
5370
|
+
consola32.info(`Nest at ${base} \u2014 ${status.agents} agent(s) registered`);
|
|
4320
5371
|
}
|
|
4321
5372
|
});
|
|
4322
|
-
function humanDuration(sec) {
|
|
4323
|
-
if (sec < 60) return `${sec}s`;
|
|
4324
|
-
if (sec < 3600) return `${Math.floor(sec / 60)}m`;
|
|
4325
|
-
if (sec < 86400) return `${Math.floor(sec / 3600)}h`;
|
|
4326
|
-
return `${Math.floor(sec / 86400)}d`;
|
|
4327
|
-
}
|
|
4328
5373
|
|
|
4329
5374
|
// src/commands/nest/uninstall.ts
|
|
4330
5375
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4331
5376
|
import { existsSync as existsSync13, unlinkSync } from "fs";
|
|
4332
5377
|
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
4333
5378
|
import { join as join11 } from "path";
|
|
4334
|
-
import { defineCommand as
|
|
4335
|
-
import
|
|
5379
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
5380
|
+
import consola33 from "consola";
|
|
4336
5381
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4337
|
-
var uninstallNestCommand =
|
|
5382
|
+
var uninstallNestCommand = defineCommand36({
|
|
4338
5383
|
meta: {
|
|
4339
5384
|
name: "uninstall",
|
|
4340
5385
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
@@ -4344,40 +5389,43 @@ var uninstallNestCommand = defineCommand33({
|
|
|
4344
5389
|
const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4345
5390
|
try {
|
|
4346
5391
|
execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4347
|
-
|
|
5392
|
+
consola33.success("Nest daemon stopped");
|
|
4348
5393
|
} catch {
|
|
4349
|
-
|
|
5394
|
+
consola33.info("Nest daemon was not loaded");
|
|
4350
5395
|
}
|
|
4351
5396
|
if (existsSync13(path2)) {
|
|
4352
5397
|
unlinkSync(path2);
|
|
4353
|
-
|
|
5398
|
+
consola33.success(`Removed ${path2}`);
|
|
4354
5399
|
}
|
|
4355
|
-
|
|
5400
|
+
consola33.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4356
5401
|
}
|
|
4357
5402
|
});
|
|
4358
5403
|
|
|
4359
5404
|
// src/commands/nest/index.ts
|
|
4360
|
-
var nestCommand =
|
|
5405
|
+
var nestCommand = defineCommand37({
|
|
4361
5406
|
meta: {
|
|
4362
5407
|
name: "nest",
|
|
4363
|
-
description: "Manage the local Nest control-plane daemon
|
|
5408
|
+
description: "Manage the local Nest control-plane daemon. One-time setup: `install` (launchd) \u2192 `enroll` (own DDISA identity) \u2192 `authorize` (YOLO-policy). Day-to-day: `status` / `list` (read-only) and `spawn` / `destroy` (mutating) \u2014 every API call is gated by a DDISA grant audited at the IdP, YOLO-approved silently under the policy `apes nest authorize` sets up."
|
|
4364
5409
|
},
|
|
4365
5410
|
subCommands: {
|
|
4366
5411
|
install: installNestCommand,
|
|
4367
5412
|
enroll: enrollNestCommand,
|
|
4368
5413
|
authorize: authorizeNestCommand,
|
|
4369
5414
|
status: statusNestCommand,
|
|
5415
|
+
list: listNestCommand,
|
|
5416
|
+
spawn: spawnNestCommand,
|
|
5417
|
+
destroy: destroyNestCommand,
|
|
4370
5418
|
uninstall: uninstallNestCommand
|
|
4371
5419
|
}
|
|
4372
5420
|
});
|
|
4373
5421
|
|
|
4374
5422
|
// src/commands/yolo/index.ts
|
|
4375
|
-
import { defineCommand as
|
|
5423
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
4376
5424
|
|
|
4377
5425
|
// src/commands/yolo/clear.ts
|
|
4378
|
-
import { defineCommand as
|
|
4379
|
-
import
|
|
4380
|
-
var yoloClearCommand =
|
|
5426
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
5427
|
+
import consola34 from "consola";
|
|
5428
|
+
var yoloClearCommand = defineCommand38({
|
|
4381
5429
|
meta: {
|
|
4382
5430
|
name: "clear",
|
|
4383
5431
|
description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
|
|
@@ -4406,15 +5454,15 @@ var yoloClearCommand = defineCommand35({
|
|
|
4406
5454
|
const text = await res.text().catch(() => "");
|
|
4407
5455
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
4408
5456
|
}
|
|
4409
|
-
|
|
5457
|
+
consola34.success(`YOLO-policy cleared on ${email}`);
|
|
4410
5458
|
}
|
|
4411
5459
|
});
|
|
4412
5460
|
|
|
4413
5461
|
// src/commands/yolo/set.ts
|
|
4414
|
-
import { defineCommand as
|
|
4415
|
-
import
|
|
5462
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
5463
|
+
import consola35 from "consola";
|
|
4416
5464
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
4417
|
-
var yoloSetCommand =
|
|
5465
|
+
var yoloSetCommand = defineCommand39({
|
|
4418
5466
|
meta: {
|
|
4419
5467
|
name: "set",
|
|
4420
5468
|
description: "Write a YOLO-policy on a DDISA agent you own"
|
|
@@ -4462,12 +5510,12 @@ var yoloSetCommand = defineCommand36({
|
|
|
4462
5510
|
const denyPatterns = parseList(args.deny);
|
|
4463
5511
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
4464
5512
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
if (allowPatterns.length)
|
|
4468
|
-
if (denyPatterns.length)
|
|
4469
|
-
if (denyRiskThreshold)
|
|
4470
|
-
if (expiresAt)
|
|
5513
|
+
consola35.info(`Setting YOLO-policy on ${email}`);
|
|
5514
|
+
consola35.info(` mode: ${mode}`);
|
|
5515
|
+
if (allowPatterns.length) consola35.info(` allow_patterns: ${allowPatterns.join(", ")}`);
|
|
5516
|
+
if (denyPatterns.length) consola35.info(` deny_patterns: ${denyPatterns.join(", ")}`);
|
|
5517
|
+
if (denyRiskThreshold) consola35.info(` deny_risk: ${denyRiskThreshold}`);
|
|
5518
|
+
if (expiresAt) consola35.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
4471
5519
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
4472
5520
|
const res = await fetch(url, {
|
|
4473
5521
|
method: "PUT",
|
|
@@ -4487,27 +5535,27 @@ var yoloSetCommand = defineCommand36({
|
|
|
4487
5535
|
const text = await res.text().catch(() => "");
|
|
4488
5536
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
4489
5537
|
}
|
|
4490
|
-
|
|
5538
|
+
consola35.success(`YOLO-policy applied to ${email}`);
|
|
4491
5539
|
}
|
|
4492
5540
|
});
|
|
4493
5541
|
function parseList(s) {
|
|
4494
5542
|
if (!s) return [];
|
|
4495
|
-
return s.split(",").map((
|
|
5543
|
+
return s.split(",").map((p2) => p2.trim()).filter(Boolean);
|
|
4496
5544
|
}
|
|
4497
5545
|
function parseExpiresIn(s) {
|
|
4498
5546
|
if (!s) return null;
|
|
4499
5547
|
const m = s.match(/^(\d+)([hdw])$/);
|
|
4500
5548
|
if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
|
|
4501
|
-
const
|
|
5549
|
+
const n2 = Number(m[1]);
|
|
4502
5550
|
const unit = m[2];
|
|
4503
5551
|
const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
|
|
4504
|
-
return Math.floor(Date.now() / 1e3) +
|
|
5552
|
+
return Math.floor(Date.now() / 1e3) + n2 * seconds;
|
|
4505
5553
|
}
|
|
4506
5554
|
|
|
4507
5555
|
// src/commands/yolo/show.ts
|
|
4508
|
-
import { defineCommand as
|
|
4509
|
-
import
|
|
4510
|
-
var yoloShowCommand =
|
|
5556
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
5557
|
+
import consola36 from "consola";
|
|
5558
|
+
var yoloShowCommand = defineCommand40({
|
|
4511
5559
|
meta: {
|
|
4512
5560
|
name: "show",
|
|
4513
5561
|
description: "Print the YOLO-policy currently set on a DDISA agent"
|
|
@@ -4544,17 +5592,17 @@ var yoloShowCommand = defineCommand37({
|
|
|
4544
5592
|
console.log(JSON.stringify(policy, null, 2));
|
|
4545
5593
|
return;
|
|
4546
5594
|
}
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
5595
|
+
consola36.info(`YOLO-policy for ${email}`);
|
|
5596
|
+
consola36.info(` mode: ${policy.mode}`);
|
|
5597
|
+
consola36.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
|
|
5598
|
+
consola36.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
|
|
5599
|
+
consola36.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
|
|
5600
|
+
consola36.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
|
|
4553
5601
|
}
|
|
4554
5602
|
});
|
|
4555
5603
|
|
|
4556
5604
|
// src/commands/yolo/index.ts
|
|
4557
|
-
var yoloCommand =
|
|
5605
|
+
var yoloCommand = defineCommand41({
|
|
4558
5606
|
meta: {
|
|
4559
5607
|
name: "yolo",
|
|
4560
5608
|
description: "Manage YOLO-policies on DDISA agents you own \u2014 auto-approve grant patterns at the IdP layer (allow-list) or block dangerous ones outright (deny-list)."
|
|
@@ -4567,15 +5615,15 @@ var yoloCommand = defineCommand38({
|
|
|
4567
5615
|
});
|
|
4568
5616
|
|
|
4569
5617
|
// src/commands/adapter/index.ts
|
|
4570
|
-
import { defineCommand as
|
|
4571
|
-
import
|
|
4572
|
-
var adapterCommand =
|
|
5618
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5619
|
+
import consola37 from "consola";
|
|
5620
|
+
var adapterCommand = defineCommand42({
|
|
4573
5621
|
meta: {
|
|
4574
5622
|
name: "adapter",
|
|
4575
5623
|
description: "Manage CLI adapters"
|
|
4576
5624
|
},
|
|
4577
5625
|
subCommands: {
|
|
4578
|
-
list:
|
|
5626
|
+
list: defineCommand42({
|
|
4579
5627
|
meta: {
|
|
4580
5628
|
name: "list",
|
|
4581
5629
|
description: "List available adapters"
|
|
@@ -4606,7 +5654,7 @@ var adapterCommand = defineCommand39({
|
|
|
4606
5654
|
`);
|
|
4607
5655
|
return;
|
|
4608
5656
|
}
|
|
4609
|
-
|
|
5657
|
+
consola37.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4610
5658
|
for (const a of index2.adapters) {
|
|
4611
5659
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4612
5660
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4628,7 +5676,7 @@ var adapterCommand = defineCommand39({
|
|
|
4628
5676
|
return;
|
|
4629
5677
|
}
|
|
4630
5678
|
if (local.length === 0) {
|
|
4631
|
-
|
|
5679
|
+
consola37.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4632
5680
|
return;
|
|
4633
5681
|
}
|
|
4634
5682
|
for (const a of local) {
|
|
@@ -4636,7 +5684,7 @@ var adapterCommand = defineCommand39({
|
|
|
4636
5684
|
}
|
|
4637
5685
|
}
|
|
4638
5686
|
}),
|
|
4639
|
-
install:
|
|
5687
|
+
install: defineCommand42({
|
|
4640
5688
|
meta: {
|
|
4641
5689
|
name: "install",
|
|
4642
5690
|
description: "Install an adapter from the registry"
|
|
@@ -4665,24 +5713,24 @@ var adapterCommand = defineCommand39({
|
|
|
4665
5713
|
for (const id of ids) {
|
|
4666
5714
|
const entry = findAdapter(index, id);
|
|
4667
5715
|
if (!entry) {
|
|
4668
|
-
|
|
5716
|
+
consola37.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4669
5717
|
continue;
|
|
4670
5718
|
}
|
|
4671
5719
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4672
5720
|
if (conflicts.length > 0) {
|
|
4673
|
-
for (const
|
|
4674
|
-
|
|
4675
|
-
|
|
5721
|
+
for (const c2 of conflicts) {
|
|
5722
|
+
consola37.warn(`Conflicting adapter found: ${c2.path} (id: ${c2.adapterId}, executable: ${c2.executable})`);
|
|
5723
|
+
consola37.warn(` Remove it with: apes adapter remove ${c2.adapterId}`);
|
|
4676
5724
|
}
|
|
4677
5725
|
}
|
|
4678
5726
|
const result = await installAdapter(entry, { local });
|
|
4679
5727
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4680
|
-
|
|
4681
|
-
|
|
5728
|
+
consola37.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
5729
|
+
consola37.info(`Digest: ${result.digest}`);
|
|
4682
5730
|
}
|
|
4683
5731
|
}
|
|
4684
5732
|
}),
|
|
4685
|
-
remove:
|
|
5733
|
+
remove: defineCommand42({
|
|
4686
5734
|
meta: {
|
|
4687
5735
|
name: "remove",
|
|
4688
5736
|
description: "Remove an installed adapter"
|
|
@@ -4705,9 +5753,9 @@ var adapterCommand = defineCommand39({
|
|
|
4705
5753
|
let failed = false;
|
|
4706
5754
|
for (const id of ids) {
|
|
4707
5755
|
if (removeAdapter(id, local)) {
|
|
4708
|
-
|
|
5756
|
+
consola37.success(`Removed adapter: ${id}`);
|
|
4709
5757
|
} else {
|
|
4710
|
-
|
|
5758
|
+
consola37.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4711
5759
|
failed = true;
|
|
4712
5760
|
}
|
|
4713
5761
|
}
|
|
@@ -4715,7 +5763,7 @@ var adapterCommand = defineCommand39({
|
|
|
4715
5763
|
throw new CliError("Some adapters could not be removed");
|
|
4716
5764
|
}
|
|
4717
5765
|
}),
|
|
4718
|
-
info:
|
|
5766
|
+
info: defineCommand42({
|
|
4719
5767
|
meta: {
|
|
4720
5768
|
name: "info",
|
|
4721
5769
|
description: "Show detailed adapter information"
|
|
@@ -4757,7 +5805,7 @@ var adapterCommand = defineCommand39({
|
|
|
4757
5805
|
}
|
|
4758
5806
|
}
|
|
4759
5807
|
}),
|
|
4760
|
-
search:
|
|
5808
|
+
search: defineCommand42({
|
|
4761
5809
|
meta: {
|
|
4762
5810
|
name: "search",
|
|
4763
5811
|
description: "Search adapters in the registry"
|
|
@@ -4789,7 +5837,7 @@ var adapterCommand = defineCommand39({
|
|
|
4789
5837
|
return;
|
|
4790
5838
|
}
|
|
4791
5839
|
if (results.length === 0) {
|
|
4792
|
-
|
|
5840
|
+
consola37.info(`No adapters matching "${query}"`);
|
|
4793
5841
|
return;
|
|
4794
5842
|
}
|
|
4795
5843
|
for (const a of results) {
|
|
@@ -4798,7 +5846,7 @@ var adapterCommand = defineCommand39({
|
|
|
4798
5846
|
}
|
|
4799
5847
|
}
|
|
4800
5848
|
}),
|
|
4801
|
-
update:
|
|
5849
|
+
update: defineCommand42({
|
|
4802
5850
|
meta: {
|
|
4803
5851
|
name: "update",
|
|
4804
5852
|
description: "Update installed adapters"
|
|
@@ -4824,33 +5872,33 @@ var adapterCommand = defineCommand39({
|
|
|
4824
5872
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4825
5873
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4826
5874
|
if (targets.length === 0) {
|
|
4827
|
-
|
|
5875
|
+
consola37.info("No adapters installed to update.");
|
|
4828
5876
|
return;
|
|
4829
5877
|
}
|
|
4830
5878
|
for (const id of targets) {
|
|
4831
5879
|
const entry = findAdapter(index, id);
|
|
4832
5880
|
if (!entry) {
|
|
4833
|
-
|
|
5881
|
+
consola37.warn(`${id}: not found in registry, skipping`);
|
|
4834
5882
|
continue;
|
|
4835
5883
|
}
|
|
4836
5884
|
const localDigest = getInstalledDigest(id, false);
|
|
4837
5885
|
if (localDigest === entry.digest) {
|
|
4838
|
-
|
|
5886
|
+
consola37.info(`${id}: already up to date`);
|
|
4839
5887
|
continue;
|
|
4840
5888
|
}
|
|
4841
5889
|
if (localDigest && !args.yes) {
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
5890
|
+
consola37.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
5891
|
+
consola37.info(` Old: ${localDigest}`);
|
|
5892
|
+
consola37.info(` New: ${entry.digest}`);
|
|
5893
|
+
consola37.info(" Use --yes to confirm");
|
|
4846
5894
|
continue;
|
|
4847
5895
|
}
|
|
4848
5896
|
const result = await installAdapter(entry);
|
|
4849
|
-
|
|
5897
|
+
consola37.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4850
5898
|
}
|
|
4851
5899
|
}
|
|
4852
5900
|
}),
|
|
4853
|
-
verify:
|
|
5901
|
+
verify: defineCommand42({
|
|
4854
5902
|
meta: {
|
|
4855
5903
|
name: "verify",
|
|
4856
5904
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4883,7 +5931,7 @@ var adapterCommand = defineCommand39({
|
|
|
4883
5931
|
if (!localDigest)
|
|
4884
5932
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4885
5933
|
if (localDigest === entry.digest) {
|
|
4886
|
-
|
|
5934
|
+
consola37.success(`${id}: digest matches registry`);
|
|
4887
5935
|
} else {
|
|
4888
5936
|
console.log(` Local: ${localDigest}`);
|
|
4889
5937
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4896,10 +5944,10 @@ var adapterCommand = defineCommand39({
|
|
|
4896
5944
|
|
|
4897
5945
|
// src/commands/run.ts
|
|
4898
5946
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
4899
|
-
import { hostname as
|
|
5947
|
+
import { hostname as hostname6 } from "os";
|
|
4900
5948
|
import { basename } from "path";
|
|
4901
|
-
import { defineCommand as
|
|
4902
|
-
import
|
|
5949
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5950
|
+
import consola38 from "consola";
|
|
4903
5951
|
function shouldWaitForGrant(args) {
|
|
4904
5952
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
4905
5953
|
}
|
|
@@ -4917,16 +5965,16 @@ function getUserMode() {
|
|
|
4917
5965
|
function getAsyncExitCode() {
|
|
4918
5966
|
const envValue = process.env.APES_ASYNC_EXIT_CODE;
|
|
4919
5967
|
if (envValue !== void 0 && envValue !== "") {
|
|
4920
|
-
const
|
|
4921
|
-
if (Number.isFinite(
|
|
4922
|
-
return Math.floor(
|
|
5968
|
+
const n2 = Number(envValue);
|
|
5969
|
+
if (Number.isFinite(n2) && n2 >= 0 && n2 <= 255)
|
|
5970
|
+
return Math.floor(n2);
|
|
4923
5971
|
}
|
|
4924
5972
|
const cfg = loadConfig();
|
|
4925
5973
|
const cfgValue = cfg.defaults?.async_exit_code;
|
|
4926
5974
|
if (cfgValue !== void 0 && cfgValue !== "") {
|
|
4927
|
-
const
|
|
4928
|
-
if (Number.isFinite(
|
|
4929
|
-
return Math.floor(
|
|
5975
|
+
const n2 = Number(cfgValue);
|
|
5976
|
+
if (Number.isFinite(n2) && n2 >= 0 && n2 <= 255)
|
|
5977
|
+
return Math.floor(n2);
|
|
4930
5978
|
}
|
|
4931
5979
|
return 75;
|
|
4932
5980
|
}
|
|
@@ -4936,7 +5984,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4936
5984
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4937
5985
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4938
5986
|
if (mode === "human") {
|
|
4939
|
-
|
|
5987
|
+
consola38.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4940
5988
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4941
5989
|
console.log(` Check status: ${statusCmd}`);
|
|
4942
5990
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4946,7 +5994,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4946
5994
|
return;
|
|
4947
5995
|
}
|
|
4948
5996
|
const maxMin = getPollMaxMinutes();
|
|
4949
|
-
|
|
5997
|
+
consola38.success(`Grant ${grant.id} created (pending approval)`);
|
|
4950
5998
|
console.log(` Approve: ${approveUrl}`);
|
|
4951
5999
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4952
6000
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4968,7 +6016,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4968
6016
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4969
6017
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4970
6018
|
}
|
|
4971
|
-
var runCommand =
|
|
6019
|
+
var runCommand = defineCommand43({
|
|
4972
6020
|
meta: {
|
|
4973
6021
|
name: "run",
|
|
4974
6022
|
description: "Execute a grant-secured command"
|
|
@@ -5057,7 +6105,7 @@ async function runShellMode(command, args) {
|
|
|
5057
6105
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
5058
6106
|
if (adapterHandled) return;
|
|
5059
6107
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5060
|
-
const targetHost = args.host ||
|
|
6108
|
+
const targetHost = args.host || hostname6();
|
|
5061
6109
|
try {
|
|
5062
6110
|
const grants = await apiFetch(
|
|
5063
6111
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -5071,7 +6119,7 @@ async function runShellMode(command, args) {
|
|
|
5071
6119
|
}
|
|
5072
6120
|
} catch {
|
|
5073
6121
|
}
|
|
5074
|
-
|
|
6122
|
+
consola38.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
5075
6123
|
const grant = await apiFetch(grantsUrl, {
|
|
5076
6124
|
method: "POST",
|
|
5077
6125
|
body: {
|
|
@@ -5091,8 +6139,8 @@ async function runShellMode(command, args) {
|
|
|
5091
6139
|
host: targetHost
|
|
5092
6140
|
});
|
|
5093
6141
|
if (shouldWaitForGrant(args)) {
|
|
5094
|
-
|
|
5095
|
-
|
|
6142
|
+
consola38.info(`Grant requested: ${grant.id}`);
|
|
6143
|
+
consola38.info("Waiting for approval...");
|
|
5096
6144
|
const maxWait = 3e5;
|
|
5097
6145
|
const interval = 3e3;
|
|
5098
6146
|
const start = Date.now();
|
|
@@ -5102,7 +6150,7 @@ async function runShellMode(command, args) {
|
|
|
5102
6150
|
break;
|
|
5103
6151
|
if (status.status === "denied" || status.status === "revoked")
|
|
5104
6152
|
throw new CliError(`Grant ${status.status}.`);
|
|
5105
|
-
await new Promise((
|
|
6153
|
+
await new Promise((r3) => setTimeout(r3, interval));
|
|
5106
6154
|
}
|
|
5107
6155
|
execShellCommand(command);
|
|
5108
6156
|
return;
|
|
@@ -5123,13 +6171,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5123
6171
|
try {
|
|
5124
6172
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
5125
6173
|
} catch (err) {
|
|
5126
|
-
|
|
6174
|
+
consola38.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
5127
6175
|
return false;
|
|
5128
6176
|
}
|
|
5129
6177
|
try {
|
|
5130
6178
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5131
6179
|
if (existingGrantId) {
|
|
5132
|
-
|
|
6180
|
+
consola38.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
5133
6181
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5134
6182
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5135
6183
|
return true;
|
|
@@ -5137,27 +6185,27 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5137
6185
|
} catch {
|
|
5138
6186
|
}
|
|
5139
6187
|
const approval = args.approval ?? "once";
|
|
5140
|
-
|
|
6188
|
+
consola38.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
5141
6189
|
const grant = await createShapesGrant(resolved, {
|
|
5142
6190
|
idp,
|
|
5143
6191
|
approval,
|
|
5144
6192
|
reason: args.reason || `ape-shell: ${resolved.detail.display}`
|
|
5145
6193
|
});
|
|
5146
6194
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5147
|
-
const
|
|
5148
|
-
|
|
5149
|
-
|
|
6195
|
+
const n2 = grant.similar_grants.similar_grants.length;
|
|
6196
|
+
consola38.info("");
|
|
6197
|
+
consola38.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5150
6198
|
}
|
|
5151
6199
|
notifyGrantPending({
|
|
5152
6200
|
grantId: grant.id,
|
|
5153
6201
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
5154
6202
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
5155
6203
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
5156
|
-
host: args.host ||
|
|
6204
|
+
host: args.host || hostname6()
|
|
5157
6205
|
});
|
|
5158
6206
|
if (shouldWaitForGrant(args)) {
|
|
5159
|
-
|
|
5160
|
-
|
|
6207
|
+
consola38.info(`Grant requested: ${grant.id}`);
|
|
6208
|
+
consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5161
6209
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5162
6210
|
if (status !== "approved")
|
|
5163
6211
|
throw new CliError(`Grant ${status}`);
|
|
@@ -5231,7 +6279,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5231
6279
|
try {
|
|
5232
6280
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5233
6281
|
if (existingGrantId) {
|
|
5234
|
-
|
|
6282
|
+
consola38.info(`Reusing existing grant: ${existingGrantId}`);
|
|
5235
6283
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5236
6284
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5237
6285
|
return;
|
|
@@ -5244,18 +6292,18 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5244
6292
|
...args.reason ? { reason: args.reason } : {}
|
|
5245
6293
|
});
|
|
5246
6294
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5247
|
-
const
|
|
5248
|
-
|
|
5249
|
-
|
|
6295
|
+
const n2 = grant.similar_grants.similar_grants.length;
|
|
6296
|
+
consola38.info("");
|
|
6297
|
+
consola38.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5250
6298
|
if (grant.similar_grants.widened_details?.length) {
|
|
5251
6299
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
5252
|
-
|
|
6300
|
+
consola38.info(` Broader scope: ${wider}`);
|
|
5253
6301
|
}
|
|
5254
|
-
|
|
6302
|
+
consola38.info("");
|
|
5255
6303
|
}
|
|
5256
6304
|
if (shouldWaitForGrant(args)) {
|
|
5257
|
-
|
|
5258
|
-
|
|
6305
|
+
consola38.info(`Grant requested: ${grant.id}`);
|
|
6306
|
+
consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5259
6307
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5260
6308
|
if (status !== "approved")
|
|
5261
6309
|
throw new Error(`Grant ${status}`);
|
|
@@ -5274,8 +6322,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5274
6322
|
const idp = getIdpUrl(args.idp);
|
|
5275
6323
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5276
6324
|
const command = action.split(" ");
|
|
5277
|
-
const targetHost = args.host ||
|
|
5278
|
-
|
|
6325
|
+
const targetHost = args.host || hostname6();
|
|
6326
|
+
consola38.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5279
6327
|
const grant = await apiFetch(grantsUrl, {
|
|
5280
6328
|
method: "POST",
|
|
5281
6329
|
body: {
|
|
@@ -5292,9 +6340,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5292
6340
|
printPendingGrantInfo(grant, idp);
|
|
5293
6341
|
throw new CliExit(getAsyncExitCode());
|
|
5294
6342
|
}
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
6343
|
+
consola38.success(`Grant requested: ${grant.id}`);
|
|
6344
|
+
consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
6345
|
+
consola38.info("Waiting for approval...");
|
|
5298
6346
|
const maxWait = 15 * 60 * 1e3;
|
|
5299
6347
|
const interval = 3e3;
|
|
5300
6348
|
const start = Date.now();
|
|
@@ -5302,14 +6350,14 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5302
6350
|
while (Date.now() - start < maxWait) {
|
|
5303
6351
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5304
6352
|
if (status.status === "approved") {
|
|
5305
|
-
|
|
6353
|
+
consola38.success("Grant approved!");
|
|
5306
6354
|
approved = true;
|
|
5307
6355
|
break;
|
|
5308
6356
|
}
|
|
5309
6357
|
if (status.status === "denied" || status.status === "revoked") {
|
|
5310
6358
|
throw new CliError(`Grant ${status.status}.`);
|
|
5311
6359
|
}
|
|
5312
|
-
await new Promise((
|
|
6360
|
+
await new Promise((r3) => setTimeout(r3, interval));
|
|
5313
6361
|
}
|
|
5314
6362
|
if (!approved) {
|
|
5315
6363
|
const minutes = Math.round(maxWait / 6e4);
|
|
@@ -5317,12 +6365,12 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5317
6365
|
`Grant approval timed out after ${minutes} min (still pending). Check your DDISA inbox at ${idp}/grant-approval?grant_id=${grant.id} \u2014 if approved later, re-run the same \`apes run\` command and it will reuse the grant.`
|
|
5318
6366
|
);
|
|
5319
6367
|
}
|
|
5320
|
-
|
|
6368
|
+
consola38.info("Fetching grant token...");
|
|
5321
6369
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5322
6370
|
method: "POST"
|
|
5323
6371
|
});
|
|
5324
6372
|
if (audience === "escapes") {
|
|
5325
|
-
|
|
6373
|
+
consola38.info(`Executing: ${command.join(" ")}`);
|
|
5326
6374
|
try {
|
|
5327
6375
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5328
6376
|
execFileSync12(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
@@ -5340,8 +6388,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5340
6388
|
|
|
5341
6389
|
// src/commands/proxy.ts
|
|
5342
6390
|
import { spawn as spawn2 } from "child_process";
|
|
5343
|
-
import { defineCommand as
|
|
5344
|
-
import
|
|
6391
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
6392
|
+
import consola39 from "consola";
|
|
5345
6393
|
|
|
5346
6394
|
// src/proxy/config.ts
|
|
5347
6395
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5484,10 +6532,10 @@ function resolveProxyConfigOptions() {
|
|
|
5484
6532
|
77
|
|
5485
6533
|
);
|
|
5486
6534
|
}
|
|
5487
|
-
|
|
6535
|
+
consola39.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5488
6536
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5489
6537
|
}
|
|
5490
|
-
var proxyCommand =
|
|
6538
|
+
var proxyCommand = defineCommand44({
|
|
5491
6539
|
meta: {
|
|
5492
6540
|
name: "proxy",
|
|
5493
6541
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -5509,12 +6557,12 @@ var proxyCommand = defineCommand41({
|
|
|
5509
6557
|
let close = null;
|
|
5510
6558
|
if (reuseUrl) {
|
|
5511
6559
|
proxyUrl = reuseUrl;
|
|
5512
|
-
|
|
6560
|
+
consola39.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5513
6561
|
} else {
|
|
5514
6562
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5515
6563
|
proxyUrl = ephemeral.url;
|
|
5516
6564
|
close = ephemeral.close;
|
|
5517
|
-
|
|
6565
|
+
consola39.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5518
6566
|
}
|
|
5519
6567
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5520
6568
|
const childEnv = {
|
|
@@ -5546,7 +6594,7 @@ var proxyCommand = defineCommand41({
|
|
|
5546
6594
|
else resolveExit(code ?? 0);
|
|
5547
6595
|
});
|
|
5548
6596
|
child.once("error", (err) => {
|
|
5549
|
-
|
|
6597
|
+
consola39.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5550
6598
|
resolveExit(127);
|
|
5551
6599
|
});
|
|
5552
6600
|
});
|
|
@@ -5560,8 +6608,8 @@ function signalNumber(signal) {
|
|
|
5560
6608
|
}
|
|
5561
6609
|
|
|
5562
6610
|
// src/commands/explain.ts
|
|
5563
|
-
import { defineCommand as
|
|
5564
|
-
var explainCommand =
|
|
6611
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
6612
|
+
var explainCommand = defineCommand45({
|
|
5565
6613
|
meta: {
|
|
5566
6614
|
name: "explain",
|
|
5567
6615
|
description: "Show what permission a command would need"
|
|
@@ -5599,9 +6647,9 @@ var explainCommand = defineCommand42({
|
|
|
5599
6647
|
});
|
|
5600
6648
|
|
|
5601
6649
|
// src/commands/config/get.ts
|
|
5602
|
-
import { defineCommand as
|
|
5603
|
-
import
|
|
5604
|
-
var configGetCommand =
|
|
6650
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6651
|
+
import consola40 from "consola";
|
|
6652
|
+
var configGetCommand = defineCommand46({
|
|
5605
6653
|
meta: {
|
|
5606
6654
|
name: "get",
|
|
5607
6655
|
description: "Get a configuration value"
|
|
@@ -5621,7 +6669,7 @@ var configGetCommand = defineCommand43({
|
|
|
5621
6669
|
if (idp)
|
|
5622
6670
|
console.log(idp);
|
|
5623
6671
|
else
|
|
5624
|
-
|
|
6672
|
+
consola40.info("No IdP configured.");
|
|
5625
6673
|
break;
|
|
5626
6674
|
}
|
|
5627
6675
|
case "email": {
|
|
@@ -5629,7 +6677,7 @@ var configGetCommand = defineCommand43({
|
|
|
5629
6677
|
if (auth?.email)
|
|
5630
6678
|
console.log(auth.email);
|
|
5631
6679
|
else
|
|
5632
|
-
|
|
6680
|
+
consola40.info("Not logged in.");
|
|
5633
6681
|
break;
|
|
5634
6682
|
}
|
|
5635
6683
|
default: {
|
|
@@ -5642,7 +6690,7 @@ var configGetCommand = defineCommand43({
|
|
|
5642
6690
|
if (sectionObj && field in sectionObj) {
|
|
5643
6691
|
console.log(sectionObj[field]);
|
|
5644
6692
|
} else {
|
|
5645
|
-
|
|
6693
|
+
consola40.info(`Key "${key}" not set.`);
|
|
5646
6694
|
}
|
|
5647
6695
|
} else {
|
|
5648
6696
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5653,9 +6701,9 @@ var configGetCommand = defineCommand43({
|
|
|
5653
6701
|
});
|
|
5654
6702
|
|
|
5655
6703
|
// src/commands/config/set.ts
|
|
5656
|
-
import { defineCommand as
|
|
5657
|
-
import
|
|
5658
|
-
var configSetCommand =
|
|
6704
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
6705
|
+
import consola41 from "consola";
|
|
6706
|
+
var configSetCommand = defineCommand47({
|
|
5659
6707
|
meta: {
|
|
5660
6708
|
name: "set",
|
|
5661
6709
|
description: "Set a configuration value"
|
|
@@ -5691,12 +6739,12 @@ var configSetCommand = defineCommand44({
|
|
|
5691
6739
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5692
6740
|
}
|
|
5693
6741
|
saveConfig(config);
|
|
5694
|
-
|
|
6742
|
+
consola41.success(`Set ${key} = ${value}`);
|
|
5695
6743
|
}
|
|
5696
6744
|
});
|
|
5697
6745
|
|
|
5698
6746
|
// src/commands/fetch/index.ts
|
|
5699
|
-
import { defineCommand as
|
|
6747
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
5700
6748
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5701
6749
|
const token = getAuthToken();
|
|
5702
6750
|
if (!token) {
|
|
@@ -5732,13 +6780,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5732
6780
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5733
6781
|
}
|
|
5734
6782
|
}
|
|
5735
|
-
var fetchCommand =
|
|
6783
|
+
var fetchCommand = defineCommand48({
|
|
5736
6784
|
meta: {
|
|
5737
6785
|
name: "fetch",
|
|
5738
6786
|
description: "Make authenticated HTTP requests"
|
|
5739
6787
|
},
|
|
5740
6788
|
subCommands: {
|
|
5741
|
-
get:
|
|
6789
|
+
get: defineCommand48({
|
|
5742
6790
|
meta: {
|
|
5743
6791
|
name: "get",
|
|
5744
6792
|
description: "GET request with auth token"
|
|
@@ -5764,7 +6812,7 @@ var fetchCommand = defineCommand45({
|
|
|
5764
6812
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5765
6813
|
}
|
|
5766
6814
|
}),
|
|
5767
|
-
post:
|
|
6815
|
+
post: defineCommand48({
|
|
5768
6816
|
meta: {
|
|
5769
6817
|
name: "post",
|
|
5770
6818
|
description: "POST request with auth token"
|
|
@@ -5803,8 +6851,8 @@ var fetchCommand = defineCommand45({
|
|
|
5803
6851
|
});
|
|
5804
6852
|
|
|
5805
6853
|
// src/commands/mcp/index.ts
|
|
5806
|
-
import { defineCommand as
|
|
5807
|
-
var mcpCommand =
|
|
6854
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6855
|
+
var mcpCommand = defineCommand49({
|
|
5808
6856
|
meta: {
|
|
5809
6857
|
name: "mcp",
|
|
5810
6858
|
description: "Start MCP server for AI agents"
|
|
@@ -5827,7 +6875,7 @@ var mcpCommand = defineCommand46({
|
|
|
5827
6875
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5828
6876
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5829
6877
|
}
|
|
5830
|
-
const { startMcpServer } = await import("./server-
|
|
6878
|
+
const { startMcpServer } = await import("./server-EKUVZDZC.js");
|
|
5831
6879
|
await startMcpServer(transport, port);
|
|
5832
6880
|
}
|
|
5833
6881
|
});
|
|
@@ -5837,8 +6885,8 @@ import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSyn
|
|
|
5837
6885
|
import { randomBytes } from "crypto";
|
|
5838
6886
|
import { execFileSync as execFileSync13 } from "child_process";
|
|
5839
6887
|
import { join as join13 } from "path";
|
|
5840
|
-
import { defineCommand as
|
|
5841
|
-
import
|
|
6888
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6889
|
+
import consola42 from "consola";
|
|
5842
6890
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5843
6891
|
async function downloadTemplate(repo, targetDir) {
|
|
5844
6892
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
@@ -5855,20 +6903,20 @@ function installDeps(dir) {
|
|
|
5855
6903
|
}
|
|
5856
6904
|
}
|
|
5857
6905
|
async function promptChoice(message, choices) {
|
|
5858
|
-
const result = await
|
|
6906
|
+
const result = await consola42.prompt(message, { type: "select", options: choices });
|
|
5859
6907
|
if (typeof result === "symbol") {
|
|
5860
6908
|
throw new CliExit(0);
|
|
5861
6909
|
}
|
|
5862
6910
|
return result;
|
|
5863
6911
|
}
|
|
5864
6912
|
async function promptText(message, defaultValue) {
|
|
5865
|
-
const result = await
|
|
6913
|
+
const result = await consola42.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5866
6914
|
if (typeof result === "symbol") {
|
|
5867
6915
|
throw new CliExit(0);
|
|
5868
6916
|
}
|
|
5869
6917
|
return result || defaultValue || "";
|
|
5870
6918
|
}
|
|
5871
|
-
var initCommand =
|
|
6919
|
+
var initCommand = defineCommand50({
|
|
5872
6920
|
meta: {
|
|
5873
6921
|
name: "init",
|
|
5874
6922
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5913,20 +6961,20 @@ async function initSP(targetDir) {
|
|
|
5913
6961
|
if (existsSync14(join13(dir, "package.json"))) {
|
|
5914
6962
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5915
6963
|
}
|
|
5916
|
-
|
|
6964
|
+
consola42.start("Scaffolding SP starter...");
|
|
5917
6965
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5918
|
-
|
|
5919
|
-
|
|
6966
|
+
consola42.success("Scaffolded from openape-sp-starter");
|
|
6967
|
+
consola42.start("Installing dependencies...");
|
|
5920
6968
|
installDeps(dir);
|
|
5921
|
-
|
|
6969
|
+
consola42.success("Dependencies installed");
|
|
5922
6970
|
const envExample = join13(dir, ".env.example");
|
|
5923
6971
|
const envFile = join13(dir, ".env");
|
|
5924
6972
|
if (existsSync14(envExample) && !existsSync14(envFile)) {
|
|
5925
6973
|
copyFileSync(envExample, envFile);
|
|
5926
|
-
|
|
6974
|
+
consola42.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5927
6975
|
}
|
|
5928
6976
|
console.log("");
|
|
5929
|
-
|
|
6977
|
+
consola42.box([
|
|
5930
6978
|
`cd ${dir}`,
|
|
5931
6979
|
"npm run dev",
|
|
5932
6980
|
"",
|
|
@@ -5945,15 +6993,15 @@ async function initIdP(targetDir) {
|
|
|
5945
6993
|
"s3 (S3-compatible)"
|
|
5946
6994
|
]);
|
|
5947
6995
|
const adminEmail = await promptText("Admin email");
|
|
5948
|
-
|
|
6996
|
+
consola42.start("Scaffolding IdP starter...");
|
|
5949
6997
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5950
|
-
|
|
5951
|
-
|
|
6998
|
+
consola42.success("Scaffolded from openape-idp-starter");
|
|
6999
|
+
consola42.start("Installing dependencies...");
|
|
5952
7000
|
installDeps(dir);
|
|
5953
|
-
|
|
7001
|
+
consola42.success("Dependencies installed");
|
|
5954
7002
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5955
7003
|
const managementToken = randomBytes(32).toString("hex");
|
|
5956
|
-
|
|
7004
|
+
consola42.success("Secrets generated");
|
|
5957
7005
|
const isLocalhost = domain === "localhost";
|
|
5958
7006
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5959
7007
|
const envContent = [
|
|
@@ -5969,9 +7017,9 @@ async function initIdP(targetDir) {
|
|
|
5969
7017
|
].join("\n");
|
|
5970
7018
|
writeFileSync9(join13(dir, ".env"), `${envContent}
|
|
5971
7019
|
`, { mode: 384 });
|
|
5972
|
-
|
|
7020
|
+
consola42.success(".env created");
|
|
5973
7021
|
console.log("");
|
|
5974
|
-
|
|
7022
|
+
consola42.box([
|
|
5975
7023
|
`cd ${dir}`,
|
|
5976
7024
|
"npm run dev",
|
|
5977
7025
|
"",
|
|
@@ -5991,8 +7039,8 @@ import { Buffer as Buffer5 } from "buffer";
|
|
|
5991
7039
|
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
5992
7040
|
import { execFile as execFile2 } from "child_process";
|
|
5993
7041
|
import { sign as sign2 } from "crypto";
|
|
5994
|
-
import { defineCommand as
|
|
5995
|
-
import
|
|
7042
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
7043
|
+
import consola43 from "consola";
|
|
5996
7044
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5997
7045
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5998
7046
|
var POLL_INTERVAL = 3e3;
|
|
@@ -6035,7 +7083,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
6035
7083
|
}
|
|
6036
7084
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
6037
7085
|
}
|
|
6038
|
-
var enrollCommand =
|
|
7086
|
+
var enrollCommand = defineCommand51({
|
|
6039
7087
|
meta: {
|
|
6040
7088
|
name: "enroll",
|
|
6041
7089
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -6055,48 +7103,48 @@ var enrollCommand = defineCommand48({
|
|
|
6055
7103
|
}
|
|
6056
7104
|
},
|
|
6057
7105
|
async run({ args }) {
|
|
6058
|
-
const idp = args.idp || await
|
|
6059
|
-
if (typeof
|
|
6060
|
-
return
|
|
7106
|
+
const idp = args.idp || await consola43.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r3) => {
|
|
7107
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7108
|
+
return r3;
|
|
6061
7109
|
}) || DEFAULT_IDP_URL2;
|
|
6062
|
-
const agentName = args.name || await
|
|
6063
|
-
if (typeof
|
|
6064
|
-
return
|
|
7110
|
+
const agentName = args.name || await consola43.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r3) => {
|
|
7111
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7112
|
+
return r3;
|
|
6065
7113
|
});
|
|
6066
7114
|
if (!agentName) {
|
|
6067
7115
|
throw new CliError("Agent name is required.");
|
|
6068
7116
|
}
|
|
6069
|
-
const keyPath = args.key || await
|
|
6070
|
-
if (typeof
|
|
6071
|
-
return
|
|
7117
|
+
const keyPath = args.key || await consola43.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r3) => {
|
|
7118
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7119
|
+
return r3;
|
|
6072
7120
|
}) || DEFAULT_KEY_PATH;
|
|
6073
7121
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6074
7122
|
let publicKey;
|
|
6075
7123
|
if (existsSync15(resolvedKey)) {
|
|
6076
7124
|
publicKey = readPublicKey(resolvedKey);
|
|
6077
|
-
|
|
7125
|
+
consola43.success(`Using existing key ${keyPath}`);
|
|
6078
7126
|
} else {
|
|
6079
|
-
|
|
7127
|
+
consola43.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
6080
7128
|
publicKey = generateAndSaveKey(keyPath);
|
|
6081
|
-
|
|
7129
|
+
consola43.success(`Key pair generated at ${keyPath}`);
|
|
6082
7130
|
}
|
|
6083
7131
|
const encodedKey = encodeURIComponent(publicKey);
|
|
6084
7132
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
6085
|
-
|
|
6086
|
-
|
|
7133
|
+
consola43.info("Opening browser for enrollment...");
|
|
7134
|
+
consola43.info(`\u2192 ${idp}/enroll`);
|
|
6087
7135
|
openBrowser2(enrollUrl);
|
|
6088
7136
|
console.log("");
|
|
6089
|
-
const agentEmail = await
|
|
7137
|
+
const agentEmail = await consola43.prompt(
|
|
6090
7138
|
"Agent email (shown in browser after enrollment)",
|
|
6091
7139
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
6092
|
-
).then((
|
|
6093
|
-
if (typeof
|
|
6094
|
-
return
|
|
7140
|
+
).then((r3) => {
|
|
7141
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7142
|
+
return r3;
|
|
6095
7143
|
});
|
|
6096
7144
|
if (!agentEmail) {
|
|
6097
7145
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
6098
7146
|
}
|
|
6099
|
-
|
|
7147
|
+
consola43.start("Verifying enrollment...");
|
|
6100
7148
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
6101
7149
|
saveAuth({
|
|
6102
7150
|
idp,
|
|
@@ -6108,18 +7156,18 @@ var enrollCommand = defineCommand48({
|
|
|
6108
7156
|
config.defaults = { ...config.defaults, idp };
|
|
6109
7157
|
config.agent = { key: keyPath, email: agentEmail };
|
|
6110
7158
|
saveConfig(config);
|
|
6111
|
-
|
|
6112
|
-
|
|
7159
|
+
consola43.success(`Agent enrolled as ${agentEmail}`);
|
|
7160
|
+
consola43.success("Config saved to ~/.config/apes/");
|
|
6113
7161
|
console.log("");
|
|
6114
|
-
|
|
7162
|
+
consola43.info("Verify with: apes whoami");
|
|
6115
7163
|
}
|
|
6116
7164
|
});
|
|
6117
7165
|
|
|
6118
7166
|
// src/commands/register-user.ts
|
|
6119
7167
|
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
6120
|
-
import { defineCommand as
|
|
6121
|
-
import
|
|
6122
|
-
var registerUserCommand =
|
|
7168
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
7169
|
+
import consola44 from "consola";
|
|
7170
|
+
var registerUserCommand = defineCommand52({
|
|
6123
7171
|
meta: {
|
|
6124
7172
|
name: "register-user",
|
|
6125
7173
|
description: "Register a sub-user with SSH key"
|
|
@@ -6174,18 +7222,18 @@ var registerUserCommand = defineCommand49({
|
|
|
6174
7222
|
...userType ? { type: userType } : {}
|
|
6175
7223
|
}
|
|
6176
7224
|
});
|
|
6177
|
-
|
|
7225
|
+
consola44.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
6178
7226
|
}
|
|
6179
7227
|
});
|
|
6180
7228
|
|
|
6181
7229
|
// src/commands/utils/index.ts
|
|
6182
|
-
import { defineCommand as
|
|
7230
|
+
import { defineCommand as defineCommand54 } from "citty";
|
|
6183
7231
|
|
|
6184
7232
|
// src/commands/utils/dig.ts
|
|
6185
|
-
import { defineCommand as
|
|
6186
|
-
import
|
|
7233
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
7234
|
+
import consola45 from "consola";
|
|
6187
7235
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
6188
|
-
var digCommand =
|
|
7236
|
+
var digCommand = defineCommand53({
|
|
6189
7237
|
meta: {
|
|
6190
7238
|
name: "dig",
|
|
6191
7239
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -6258,12 +7306,12 @@ var digCommand = defineCommand50({
|
|
|
6258
7306
|
console.log(` domain: ${domain}`);
|
|
6259
7307
|
console.log("");
|
|
6260
7308
|
if (!result.ddisa.found) {
|
|
6261
|
-
|
|
7309
|
+
consola45.warn(`No DDISA record at _ddisa.${domain}`);
|
|
6262
7310
|
if (result.hint) console.log(`
|
|
6263
7311
|
${result.hint}`);
|
|
6264
7312
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6265
7313
|
}
|
|
6266
|
-
|
|
7314
|
+
consola45.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
6267
7315
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
6268
7316
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
6269
7317
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -6273,13 +7321,13 @@ ${result.hint}`);
|
|
|
6273
7321
|
return;
|
|
6274
7322
|
}
|
|
6275
7323
|
if (result.idpDiscovery.ok) {
|
|
6276
|
-
|
|
7324
|
+
consola45.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
6277
7325
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
6278
7326
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
6279
7327
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
6280
7328
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
6281
7329
|
} else {
|
|
6282
|
-
|
|
7330
|
+
consola45.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
6283
7331
|
if (result.hint) console.log(`
|
|
6284
7332
|
${result.hint}`);
|
|
6285
7333
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -6288,7 +7336,7 @@ ${result.hint}`);
|
|
|
6288
7336
|
});
|
|
6289
7337
|
|
|
6290
7338
|
// src/commands/utils/index.ts
|
|
6291
|
-
var utilsCommand =
|
|
7339
|
+
var utilsCommand = defineCommand54({
|
|
6292
7340
|
meta: {
|
|
6293
7341
|
name: "utils",
|
|
6294
7342
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -6299,12 +7347,12 @@ var utilsCommand = defineCommand51({
|
|
|
6299
7347
|
});
|
|
6300
7348
|
|
|
6301
7349
|
// src/commands/sessions/index.ts
|
|
6302
|
-
import { defineCommand as
|
|
7350
|
+
import { defineCommand as defineCommand57 } from "citty";
|
|
6303
7351
|
|
|
6304
7352
|
// src/commands/sessions/list.ts
|
|
6305
|
-
import { defineCommand as
|
|
6306
|
-
import
|
|
6307
|
-
var sessionsListCommand =
|
|
7353
|
+
import { defineCommand as defineCommand55 } from "citty";
|
|
7354
|
+
import consola46 from "consola";
|
|
7355
|
+
var sessionsListCommand = defineCommand55({
|
|
6308
7356
|
meta: {
|
|
6309
7357
|
name: "list",
|
|
6310
7358
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6322,7 +7370,7 @@ var sessionsListCommand = defineCommand52({
|
|
|
6322
7370
|
return;
|
|
6323
7371
|
}
|
|
6324
7372
|
if (result.data.length === 0) {
|
|
6325
|
-
|
|
7373
|
+
consola46.info("No active sessions.");
|
|
6326
7374
|
return;
|
|
6327
7375
|
}
|
|
6328
7376
|
for (const f of result.data) {
|
|
@@ -6334,9 +7382,9 @@ var sessionsListCommand = defineCommand52({
|
|
|
6334
7382
|
});
|
|
6335
7383
|
|
|
6336
7384
|
// src/commands/sessions/remove.ts
|
|
6337
|
-
import { defineCommand as
|
|
6338
|
-
import
|
|
6339
|
-
var sessionsRemoveCommand =
|
|
7385
|
+
import { defineCommand as defineCommand56 } from "citty";
|
|
7386
|
+
import consola47 from "consola";
|
|
7387
|
+
var sessionsRemoveCommand = defineCommand56({
|
|
6340
7388
|
meta: {
|
|
6341
7389
|
name: "remove",
|
|
6342
7390
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6352,12 +7400,12 @@ var sessionsRemoveCommand = defineCommand53({
|
|
|
6352
7400
|
const id = String(args.familyId).trim();
|
|
6353
7401
|
if (!id) throw new CliError("familyId required");
|
|
6354
7402
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6355
|
-
|
|
7403
|
+
consola47.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6356
7404
|
}
|
|
6357
7405
|
});
|
|
6358
7406
|
|
|
6359
7407
|
// src/commands/sessions/index.ts
|
|
6360
|
-
var sessionsCommand =
|
|
7408
|
+
var sessionsCommand = defineCommand57({
|
|
6361
7409
|
meta: {
|
|
6362
7410
|
name: "sessions",
|
|
6363
7411
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6369,10 +7417,10 @@ var sessionsCommand = defineCommand54({
|
|
|
6369
7417
|
});
|
|
6370
7418
|
|
|
6371
7419
|
// src/commands/dns-check.ts
|
|
6372
|
-
import { defineCommand as
|
|
6373
|
-
import
|
|
7420
|
+
import { defineCommand as defineCommand58 } from "citty";
|
|
7421
|
+
import consola48 from "consola";
|
|
6374
7422
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6375
|
-
var dnsCheckCommand =
|
|
7423
|
+
var dnsCheckCommand = defineCommand58({
|
|
6376
7424
|
meta: {
|
|
6377
7425
|
name: "dns-check",
|
|
6378
7426
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6386,7 +7434,7 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6386
7434
|
},
|
|
6387
7435
|
async run({ args }) {
|
|
6388
7436
|
const domain = args.domain;
|
|
6389
|
-
|
|
7437
|
+
consola48.start(`Checking _ddisa.${domain}...`);
|
|
6390
7438
|
try {
|
|
6391
7439
|
const result = await resolveDDISA3(domain);
|
|
6392
7440
|
if (!result) {
|
|
@@ -6395,7 +7443,7 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6395
7443
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
6396
7444
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6397
7445
|
}
|
|
6398
|
-
|
|
7446
|
+
consola48.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
6399
7447
|
console.log("");
|
|
6400
7448
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
6401
7449
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -6404,14 +7452,14 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6404
7452
|
if (result.priority !== void 0)
|
|
6405
7453
|
console.log(` Priority: ${result.priority}`);
|
|
6406
7454
|
console.log("");
|
|
6407
|
-
|
|
7455
|
+
consola48.start(`Verifying IdP at ${result.idp}...`);
|
|
6408
7456
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
6409
7457
|
if (!discoResp.ok) {
|
|
6410
|
-
|
|
7458
|
+
consola48.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
6411
7459
|
return;
|
|
6412
7460
|
}
|
|
6413
7461
|
const disco = await discoResp.json();
|
|
6414
|
-
|
|
7462
|
+
consola48.success(`IdP is reachable`);
|
|
6415
7463
|
console.log(` Issuer: ${disco.issuer}`);
|
|
6416
7464
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
6417
7465
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -6429,7 +7477,7 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6429
7477
|
// src/commands/health.ts
|
|
6430
7478
|
import { exec } from "child_process";
|
|
6431
7479
|
import { promisify } from "util";
|
|
6432
|
-
import { defineCommand as
|
|
7480
|
+
import { defineCommand as defineCommand59 } from "citty";
|
|
6433
7481
|
var execAsync = promisify(exec);
|
|
6434
7482
|
async function resolveApeShellPath() {
|
|
6435
7483
|
try {
|
|
@@ -6465,7 +7513,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6465
7513
|
}
|
|
6466
7514
|
}
|
|
6467
7515
|
async function runHealth(args) {
|
|
6468
|
-
const version = true ? "1.
|
|
7516
|
+
const version = true ? "1.8.1" : "0.0.0";
|
|
6469
7517
|
const auth = loadAuth();
|
|
6470
7518
|
if (!auth) {
|
|
6471
7519
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6528,7 +7576,7 @@ async function runHealth(args) {
|
|
|
6528
7576
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
6529
7577
|
}
|
|
6530
7578
|
}
|
|
6531
|
-
var healthCommand =
|
|
7579
|
+
var healthCommand = defineCommand59({
|
|
6532
7580
|
meta: {
|
|
6533
7581
|
name: "health",
|
|
6534
7582
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -6546,8 +7594,8 @@ var healthCommand = defineCommand56({
|
|
|
6546
7594
|
});
|
|
6547
7595
|
|
|
6548
7596
|
// src/commands/workflows.ts
|
|
6549
|
-
import { defineCommand as
|
|
6550
|
-
import
|
|
7597
|
+
import { defineCommand as defineCommand60 } from "citty";
|
|
7598
|
+
import consola49 from "consola";
|
|
6551
7599
|
|
|
6552
7600
|
// src/guides/index.ts
|
|
6553
7601
|
var guides = [
|
|
@@ -6597,7 +7645,7 @@ var guides = [
|
|
|
6597
7645
|
];
|
|
6598
7646
|
|
|
6599
7647
|
// src/commands/workflows.ts
|
|
6600
|
-
var workflowsCommand =
|
|
7648
|
+
var workflowsCommand = defineCommand60({
|
|
6601
7649
|
meta: {
|
|
6602
7650
|
name: "workflows",
|
|
6603
7651
|
description: "Discover workflow guides"
|
|
@@ -6618,7 +7666,7 @@ var workflowsCommand = defineCommand57({
|
|
|
6618
7666
|
if (args.id) {
|
|
6619
7667
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6620
7668
|
if (!guide) {
|
|
6621
|
-
|
|
7669
|
+
consola49.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6622
7670
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6623
7671
|
}
|
|
6624
7672
|
if (args.json) {
|
|
@@ -6661,7 +7709,7 @@ var workflowsCommand = defineCommand57({
|
|
|
6661
7709
|
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
6662
7710
|
import { homedir as homedir13 } from "os";
|
|
6663
7711
|
import { join as join14 } from "path";
|
|
6664
|
-
import
|
|
7712
|
+
import consola50 from "consola";
|
|
6665
7713
|
var PACKAGE_NAME = "@openape/apes";
|
|
6666
7714
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6667
7715
|
var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
|
|
@@ -6706,7 +7754,7 @@ async function fetchLatestVersion() {
|
|
|
6706
7754
|
}
|
|
6707
7755
|
function warnIfBehind(currentVersion, latest) {
|
|
6708
7756
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6709
|
-
|
|
7757
|
+
consola50.warn(
|
|
6710
7758
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
6711
7759
|
);
|
|
6712
7760
|
}
|
|
@@ -6738,10 +7786,10 @@ if (shellRewrite) {
|
|
|
6738
7786
|
if (shellRewrite.action === "rewrite") {
|
|
6739
7787
|
process.argv = shellRewrite.argv;
|
|
6740
7788
|
} else if (shellRewrite.action === "version") {
|
|
6741
|
-
console.log(`ape-shell ${"1.
|
|
7789
|
+
console.log(`ape-shell ${"1.8.1"} (OpenApe DDISA shell wrapper)`);
|
|
6742
7790
|
process.exit(0);
|
|
6743
7791
|
} else if (shellRewrite.action === "help") {
|
|
6744
|
-
console.log(`ape-shell ${"1.
|
|
7792
|
+
console.log(`ape-shell ${"1.8.1"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6745
7793
|
console.log("");
|
|
6746
7794
|
console.log("Usage:");
|
|
6747
7795
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6756,7 +7804,7 @@ if (shellRewrite) {
|
|
|
6756
7804
|
console.log(" --help, -h Show this help message");
|
|
6757
7805
|
process.exit(0);
|
|
6758
7806
|
} else if (shellRewrite.action === "interactive") {
|
|
6759
|
-
const { runInteractiveShell } = await import("./orchestrator-
|
|
7807
|
+
const { runInteractiveShell } = await import("./orchestrator-2QS5KXFL.js");
|
|
6760
7808
|
await runInteractiveShell();
|
|
6761
7809
|
process.exit(0);
|
|
6762
7810
|
} else {
|
|
@@ -6765,7 +7813,7 @@ if (shellRewrite) {
|
|
|
6765
7813
|
}
|
|
6766
7814
|
}
|
|
6767
7815
|
var debug = process.argv.includes("--debug");
|
|
6768
|
-
var grantsCommand =
|
|
7816
|
+
var grantsCommand = defineCommand61({
|
|
6769
7817
|
meta: {
|
|
6770
7818
|
name: "grants",
|
|
6771
7819
|
description: "Grant management"
|
|
@@ -6786,7 +7834,7 @@ var grantsCommand = defineCommand58({
|
|
|
6786
7834
|
"delegation-revoke": delegationRevokeCommand
|
|
6787
7835
|
}
|
|
6788
7836
|
});
|
|
6789
|
-
var configCommand =
|
|
7837
|
+
var configCommand = defineCommand61({
|
|
6790
7838
|
meta: {
|
|
6791
7839
|
name: "config",
|
|
6792
7840
|
description: "Configuration management"
|
|
@@ -6796,10 +7844,10 @@ var configCommand = defineCommand58({
|
|
|
6796
7844
|
set: configSetCommand
|
|
6797
7845
|
}
|
|
6798
7846
|
});
|
|
6799
|
-
var main =
|
|
7847
|
+
var main = defineCommand61({
|
|
6800
7848
|
meta: {
|
|
6801
7849
|
name: "apes",
|
|
6802
|
-
version: "1.
|
|
7850
|
+
version: "1.8.1",
|
|
6803
7851
|
description: "Unified CLI for OpenApe"
|
|
6804
7852
|
},
|
|
6805
7853
|
subCommands: {
|
|
@@ -6847,29 +7895,29 @@ var NO_REFRESH_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
6847
7895
|
async function maybeRefreshAuth() {
|
|
6848
7896
|
const sub = process.argv[2];
|
|
6849
7897
|
if (!sub || NO_REFRESH_COMMANDS.has(sub)) return;
|
|
6850
|
-
const { loadAuth: loadAuth2 } = await import("./config-
|
|
7898
|
+
const { loadAuth: loadAuth2 } = await import("./config-DA2L3XOV.js");
|
|
6851
7899
|
if (!loadAuth2()) return;
|
|
6852
7900
|
try {
|
|
6853
|
-
const { ensureFreshToken } = await import("./http-
|
|
7901
|
+
const { ensureFreshToken } = await import("./http-JWS5LV2U.js");
|
|
6854
7902
|
await ensureFreshToken();
|
|
6855
7903
|
} catch {
|
|
6856
7904
|
}
|
|
6857
7905
|
}
|
|
6858
7906
|
await maybeRefreshAuth();
|
|
6859
|
-
await maybeWarnStaleVersion("1.
|
|
7907
|
+
await maybeWarnStaleVersion("1.8.1").catch(() => {
|
|
6860
7908
|
});
|
|
6861
7909
|
runMain(main).catch((err) => {
|
|
6862
7910
|
if (err instanceof CliExit) {
|
|
6863
7911
|
process.exit(err.exitCode);
|
|
6864
7912
|
}
|
|
6865
7913
|
if (err instanceof CliError) {
|
|
6866
|
-
|
|
7914
|
+
consola51.error(err.message);
|
|
6867
7915
|
process.exit(err.exitCode);
|
|
6868
7916
|
}
|
|
6869
7917
|
if (debug) {
|
|
6870
|
-
|
|
7918
|
+
consola51.error(err);
|
|
6871
7919
|
} else {
|
|
6872
|
-
|
|
7920
|
+
consola51.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6873
7921
|
}
|
|
6874
7922
|
process.exit(1);
|
|
6875
7923
|
});
|