@openape/apes 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{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 -334
- 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-BSBIEKFK.js → server-6B26NNLZ.js} +4 -3
- package/dist/{server-BSBIEKFK.js.map → server-6B26NNLZ.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 +1 -1
- /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
|
}
|
|
@@ -1754,16 +1765,793 @@ 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) return null;
|
|
2518
|
+
const result = await exchangeWithDelegation({
|
|
2519
|
+
idp: idpUrl,
|
|
2520
|
+
actorToken: auth.access_token,
|
|
2521
|
+
audience: ENROLL_AUDIENCE,
|
|
2522
|
+
delegationGrantId: grantId
|
|
2523
|
+
});
|
|
2524
|
+
return result.access_token;
|
|
2525
|
+
} catch {
|
|
2526
|
+
return null;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
async function findEnrollDelegationGrantId(idp, delegator, delegate) {
|
|
2530
|
+
const url = `${idp.replace(/\/$/, "")}/api/grants?status=approved&limit=200&requester=${encodeURIComponent(delegator)}`;
|
|
2531
|
+
const res = await apiFetch(url);
|
|
2532
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
2533
|
+
for (const g of res.data ?? []) {
|
|
2534
|
+
if (g.type !== "delegation") continue;
|
|
2535
|
+
if (g.status !== "approved") continue;
|
|
2536
|
+
if (g.request.delegate !== delegate) continue;
|
|
2537
|
+
const aud = g.request.audience;
|
|
2538
|
+
if (aud !== "*" && aud !== ENROLL_AUDIENCE) continue;
|
|
2539
|
+
if (g.expires_at && g.expires_at <= now) continue;
|
|
2540
|
+
return g.id;
|
|
2541
|
+
}
|
|
2542
|
+
return null;
|
|
2543
|
+
}
|
|
2544
|
+
function decodeJwtClaims(token) {
|
|
2545
|
+
try {
|
|
2546
|
+
const part = token.split(".")[1];
|
|
2547
|
+
if (!part) return null;
|
|
2548
|
+
const padded = part + "=".repeat((4 - part.length % 4) % 4);
|
|
2549
|
+
const json = Buffer3.from(padded, "base64").toString("utf8");
|
|
2550
|
+
return JSON.parse(json);
|
|
2551
|
+
} catch {
|
|
2552
|
+
return null;
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
1767
2555
|
async function issueAgentToken(input) {
|
|
1768
2556
|
const privateKey = createPrivateKey(input.privateKeyPem);
|
|
1769
2557
|
const challengeUrl = await getAgentChallengeEndpoint(input.idp);
|
|
@@ -1832,6 +2620,12 @@ done
|
|
|
1832
2620
|
return `#!/bin/bash
|
|
1833
2621
|
set -euo pipefail
|
|
1834
2622
|
|
|
2623
|
+
# escapes-spawned scripts inherit a minimal PATH that doesn't include
|
|
2624
|
+
# /usr/sbin \u2014 which is where chown / dscl / pwpolicy live. Force a
|
|
2625
|
+
# wide PATH so the privileged setup commands resolve without absolute
|
|
2626
|
+
# paths everywhere.
|
|
2627
|
+
export PATH="/usr/sbin:/usr/bin:/bin:/sbin:/opt/homebrew/bin:/usr/local/bin"
|
|
2628
|
+
|
|
1835
2629
|
NAME=${shQuote(name)}
|
|
1836
2630
|
HOME_DIR=${shQuote(homeDir)}
|
|
1837
2631
|
SHELL_PATH=${shQuote(shellPath)}
|
|
@@ -2417,16 +3211,16 @@ var listAgentsCommand = defineCommand22({
|
|
|
2417
3211
|
consola20.info(args["include-inactive"] ? "No agents found." : "No active agents found. Use --include-inactive to show deactivated.");
|
|
2418
3212
|
return;
|
|
2419
3213
|
}
|
|
2420
|
-
const nameW = Math.max(4, ...rows.map((
|
|
2421
|
-
const emailW = Math.max(5, ...rows.map((
|
|
3214
|
+
const nameW = Math.max(4, ...rows.map((r3) => r3.name.length));
|
|
3215
|
+
const emailW = Math.max(5, ...rows.map((r3) => r3.email.length));
|
|
2422
3216
|
const header = `${"NAME".padEnd(nameW)} ${"EMAIL".padEnd(emailW)} ACTIVE OS-USER HOME`;
|
|
2423
3217
|
console.log(header);
|
|
2424
3218
|
console.log("-".repeat(header.length));
|
|
2425
|
-
for (const
|
|
2426
|
-
const active =
|
|
2427
|
-
const os =
|
|
2428
|
-
const homeCol =
|
|
2429
|
-
console.log(`${
|
|
3219
|
+
for (const r3 of rows) {
|
|
3220
|
+
const active = r3.isActive ? "\u2713" : "\u2717";
|
|
3221
|
+
const os = r3.osUser ? "\u2713" : "\u2717";
|
|
3222
|
+
const homeCol = r3.home ?? (isDarwin() ? "(missing)" : "(non-darwin)");
|
|
3223
|
+
console.log(`${r3.name.padEnd(nameW)} ${r3.email.padEnd(emailW)} ${active.padEnd(6)} ${os.padEnd(7)} ${homeCol}`);
|
|
2430
3224
|
}
|
|
2431
3225
|
}
|
|
2432
3226
|
});
|
|
@@ -2553,12 +3347,12 @@ var fileTools = [
|
|
|
2553
3347
|
},
|
|
2554
3348
|
execute: async (args) => {
|
|
2555
3349
|
const a = args;
|
|
2556
|
-
const
|
|
2557
|
-
const content = readFileSync4(
|
|
3350
|
+
const p2 = jailPath(a.path);
|
|
3351
|
+
const content = readFileSync4(p2, "utf8");
|
|
2558
3352
|
if (Buffer.byteLength(content, "utf8") > MAX_BYTES) {
|
|
2559
|
-
return { path:
|
|
3353
|
+
return { path: p2, truncated: true, content: content.slice(0, MAX_BYTES) };
|
|
2560
3354
|
}
|
|
2561
|
-
return { path:
|
|
3355
|
+
return { path: p2, truncated: false, content };
|
|
2562
3356
|
}
|
|
2563
3357
|
},
|
|
2564
3358
|
{
|
|
@@ -2578,10 +3372,10 @@ var fileTools = [
|
|
|
2578
3372
|
if (Buffer.byteLength(a.content, "utf8") > MAX_BYTES) {
|
|
2579
3373
|
throw new Error(`content exceeds ${MAX_BYTES} byte cap`);
|
|
2580
3374
|
}
|
|
2581
|
-
const
|
|
2582
|
-
mkdirSync(dirname(
|
|
2583
|
-
writeFileSync2(
|
|
2584
|
-
return { path:
|
|
3375
|
+
const p2 = jailPath(a.path);
|
|
3376
|
+
mkdirSync(dirname(p2), { recursive: true });
|
|
3377
|
+
writeFileSync2(p2, a.content, { encoding: "utf8" });
|
|
3378
|
+
return { path: p2, bytes: Buffer.byteLength(a.content, "utf8") };
|
|
2585
3379
|
}
|
|
2586
3380
|
}
|
|
2587
3381
|
];
|
|
@@ -3070,7 +3864,7 @@ async function postRunResultToChat(opts) {
|
|
|
3070
3864
|
if (!contactsRes.ok) return;
|
|
3071
3865
|
const contacts = await contactsRes.json();
|
|
3072
3866
|
const ownerLower = opts.ownerEmail.toLowerCase();
|
|
3073
|
-
const ownerRow = contacts.find((
|
|
3867
|
+
const ownerRow = contacts.find((c2) => c2.peerEmail.toLowerCase() === ownerLower && c2.connected && c2.roomId);
|
|
3074
3868
|
if (!ownerRow?.roomId) {
|
|
3075
3869
|
consola22.info("chat DM skipped \u2014 no active room with owner (accept the contact request in chat to enable)");
|
|
3076
3870
|
return;
|
|
@@ -3400,8 +4194,8 @@ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as rea
|
|
|
3400
4194
|
import { generateKeyPairSync } from "crypto";
|
|
3401
4195
|
import { homedir as homedir7 } from "os";
|
|
3402
4196
|
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
3403
|
-
function resolveKeyPath(
|
|
3404
|
-
return resolve3(
|
|
4197
|
+
function resolveKeyPath(p2) {
|
|
4198
|
+
return resolve3(p2.replace(/^~/, homedir7()));
|
|
3405
4199
|
}
|
|
3406
4200
|
function buildSshEd25519Line(rawPub) {
|
|
3407
4201
|
const keyTypeStr = "ssh-ed25519";
|
|
@@ -3678,7 +4472,15 @@ and try again.`
|
|
|
3678
4472
|
email: registration.email,
|
|
3679
4473
|
expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
|
|
3680
4474
|
keyPath: `${homeDir}/.ssh/id_ed25519`,
|
|
3681
|
-
|
|
4475
|
+
// The IdP resolves the owner transitively (when the caller
|
|
4476
|
+
// is itself an agent — e.g. a Nest spawning a child — the
|
|
4477
|
+
// human at the top of the chain becomes owner). Use the
|
|
4478
|
+
// server-resolved owner, not the local caller's auth.email,
|
|
4479
|
+
// otherwise the agent's auth.json will carry the Nest's
|
|
4480
|
+
// email and troop will reject sync calls because the
|
|
4481
|
+
// encoded owner-domain in the agent email doesn't match
|
|
4482
|
+
// the auth.json's owner_email domain.
|
|
4483
|
+
ownerEmail: registration.owner
|
|
3682
4484
|
});
|
|
3683
4485
|
const includeClaudeHook = !args["no-claude-hook"];
|
|
3684
4486
|
const claudeOauthToken = await resolveClaudeToken({
|
|
@@ -3918,7 +4720,7 @@ var agentsCommand = defineCommand28({
|
|
|
3918
4720
|
});
|
|
3919
4721
|
|
|
3920
4722
|
// src/commands/nest/index.ts
|
|
3921
|
-
import { defineCommand as
|
|
4723
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
3922
4724
|
|
|
3923
4725
|
// src/commands/nest/authorize.ts
|
|
3924
4726
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
@@ -4010,7 +4812,19 @@ var enrollNestCommand = defineCommand29({
|
|
|
4010
4812
|
|
|
4011
4813
|
// src/commands/nest/authorize.ts
|
|
4012
4814
|
var DEFAULT_ALLOW_PATTERNS = [
|
|
4013
|
-
//
|
|
4815
|
+
// Caller → Nest API gates. The new-style flow: `apes nest <op>`
|
|
4816
|
+
// requests a grant with audience='nest' and command=['nest','<op>',
|
|
4817
|
+
// ...]. Patrick's YOLO auto-approves these so the local Nest API
|
|
4818
|
+
// is gated cryptographically without a per-call human prompt.
|
|
4819
|
+
"nest status",
|
|
4820
|
+
"nest list",
|
|
4821
|
+
// spawn uses a wildcard-name grant (one approval, any name)
|
|
4822
|
+
"nest spawn",
|
|
4823
|
+
// destroy stays per-name (typed in command), so the YOLO pattern
|
|
4824
|
+
// matches the per-name shape `nest destroy igor18`.
|
|
4825
|
+
"nest destroy *",
|
|
4826
|
+
// Inner spawn/destroy grants the nest itself triggers via
|
|
4827
|
+
// `apes run --as root --wait -- apes agents spawn|destroy`.
|
|
4014
4828
|
"apes agents spawn *",
|
|
4015
4829
|
"apes agents destroy *",
|
|
4016
4830
|
"apes agents sync",
|
|
@@ -4021,11 +4835,12 @@ var DEFAULT_ALLOW_PATTERNS = [
|
|
|
4021
4835
|
// glob below limits the auto-approval to that exact lifecycle path
|
|
4022
4836
|
// — `bash *` would be unsafe.
|
|
4023
4837
|
"bash *apes-spawn-*setup.sh",
|
|
4024
|
-
// Bridge invocation
|
|
4025
|
-
//
|
|
4026
|
-
//
|
|
4027
|
-
//
|
|
4028
|
-
|
|
4838
|
+
// Bridge invocation. The grant request escapes-helper sends to the
|
|
4839
|
+
// IdP contains the *inner* command only — `apes run --as <agent> --`
|
|
4840
|
+
// is the wrapper that gets unwrapped before grant creation. So the
|
|
4841
|
+
// YOLO target string is just `openape-chat-bridge`, not the full
|
|
4842
|
+
// wrapped invocation.
|
|
4843
|
+
"openape-chat-bridge"
|
|
4029
4844
|
];
|
|
4030
4845
|
var authorizeNestCommand = defineCommand30({
|
|
4031
4846
|
meta: {
|
|
@@ -4073,13 +4888,148 @@ var authorizeNestCommand = defineCommand30({
|
|
|
4073
4888
|
}
|
|
4074
4889
|
});
|
|
4075
4890
|
|
|
4891
|
+
// src/commands/nest/destroy.ts
|
|
4892
|
+
import process2 from "process";
|
|
4893
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
4894
|
+
import consola28 from "consola";
|
|
4895
|
+
|
|
4896
|
+
// src/lib/nest-grant-flow.ts
|
|
4897
|
+
import { hostname as hostname5 } from "os";
|
|
4898
|
+
import consola27 from "consola";
|
|
4899
|
+
var NEST_AUDIENCE = "nest";
|
|
4900
|
+
async function requestNestGrant(opts) {
|
|
4901
|
+
const auth = loadAuth();
|
|
4902
|
+
if (!auth) {
|
|
4903
|
+
throw new CliError("Not logged in. Run `apes login` first.");
|
|
4904
|
+
}
|
|
4905
|
+
const idp = getIdpUrl(opts.idp) ?? void 0;
|
|
4906
|
+
if (!idp) {
|
|
4907
|
+
throw new CliError("No IdP URL resolved. Pass --idp or run `apes login` first.");
|
|
4908
|
+
}
|
|
4909
|
+
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4910
|
+
const targetHost = opts.targetHost ?? hostname5();
|
|
4911
|
+
const approval = opts.approval ?? "always";
|
|
4912
|
+
const reusableId = await findReusableNestGrant({
|
|
4913
|
+
grantsUrl,
|
|
4914
|
+
requester: auth.email,
|
|
4915
|
+
command: opts.command,
|
|
4916
|
+
targetHost
|
|
4917
|
+
});
|
|
4918
|
+
if (reusableId) {
|
|
4919
|
+
const { authz_jwt: authz_jwt2 } = await apiFetch(`${grantsUrl}/${reusableId}/token`, {
|
|
4920
|
+
method: "POST"
|
|
4921
|
+
});
|
|
4922
|
+
return authz_jwt2;
|
|
4923
|
+
}
|
|
4924
|
+
consola27.info(`Requesting nest grant: ${opts.command.join(" ")}`);
|
|
4925
|
+
const grant = await apiFetch(grantsUrl, {
|
|
4926
|
+
method: "POST",
|
|
4927
|
+
body: {
|
|
4928
|
+
requester: auth.email,
|
|
4929
|
+
target_host: targetHost,
|
|
4930
|
+
audience: NEST_AUDIENCE,
|
|
4931
|
+
grant_type: approval,
|
|
4932
|
+
command: opts.command,
|
|
4933
|
+
reason: opts.reason ?? opts.command.join(" ")
|
|
4934
|
+
}
|
|
4935
|
+
});
|
|
4936
|
+
let approved = grant.status === "approved";
|
|
4937
|
+
const maxWait = 15 * 60 * 1e3;
|
|
4938
|
+
const interval = 3e3;
|
|
4939
|
+
const start = Date.now();
|
|
4940
|
+
while (!approved && Date.now() - start < maxWait) {
|
|
4941
|
+
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
4942
|
+
if (status.status === "approved") {
|
|
4943
|
+
approved = true;
|
|
4944
|
+
break;
|
|
4945
|
+
}
|
|
4946
|
+
if (status.status === "denied" || status.status === "revoked") {
|
|
4947
|
+
throw new CliError(`Grant ${status.status}.`);
|
|
4948
|
+
}
|
|
4949
|
+
if (!approved) {
|
|
4950
|
+
consola27.info(`Waiting for approval: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4951
|
+
await new Promise((r3) => setTimeout(r3, interval));
|
|
4952
|
+
}
|
|
4953
|
+
}
|
|
4954
|
+
if (!approved) {
|
|
4955
|
+
throw new CliError(
|
|
4956
|
+
`Grant approval timed out after 15 min (still pending). Check your DDISA inbox at ${idp}/grant-approval?grant_id=${grant.id}.`
|
|
4957
|
+
);
|
|
4958
|
+
}
|
|
4959
|
+
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
4960
|
+
method: "POST"
|
|
4961
|
+
});
|
|
4962
|
+
return authz_jwt;
|
|
4963
|
+
}
|
|
4964
|
+
function nestBaseUrl(port) {
|
|
4965
|
+
const p2 = port ?? Number(process.env.OPENAPE_NEST_PORT ?? 9091);
|
|
4966
|
+
return `http://127.0.0.1:${p2}`;
|
|
4967
|
+
}
|
|
4968
|
+
async function findReusableNestGrant(opts) {
|
|
4969
|
+
try {
|
|
4970
|
+
const grants = await apiFetch(
|
|
4971
|
+
`${opts.grantsUrl}?requester=${encodeURIComponent(opts.requester)}&status=approved&limit=50`
|
|
4972
|
+
);
|
|
4973
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
4974
|
+
const match = grants.data.find((g) => {
|
|
4975
|
+
const r3 = g.request;
|
|
4976
|
+
if (r3.audience !== NEST_AUDIENCE) return false;
|
|
4977
|
+
if (r3.target_host !== opts.targetHost) return false;
|
|
4978
|
+
if (r3.grant_type === "once") return false;
|
|
4979
|
+
if (r3.grant_type === "timed" && g.expires_at && g.expires_at <= now) return false;
|
|
4980
|
+
const cmd = r3.command ?? [];
|
|
4981
|
+
if (cmd.length !== opts.command.length) return false;
|
|
4982
|
+
return cmd.every((c2, i) => c2 === opts.command[i]);
|
|
4983
|
+
});
|
|
4984
|
+
return match?.id ?? null;
|
|
4985
|
+
} catch {
|
|
4986
|
+
return null;
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
|
|
4990
|
+
// src/commands/nest/destroy.ts
|
|
4991
|
+
var destroyNestCommand = defineCommand31({
|
|
4992
|
+
meta: {
|
|
4993
|
+
name: "destroy",
|
|
4994
|
+
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."
|
|
4995
|
+
},
|
|
4996
|
+
args: {
|
|
4997
|
+
name: { type: "positional", required: true, description: "Agent name to destroy" },
|
|
4998
|
+
port: { type: "string", description: "Override nest port (default: 9091)" }
|
|
4999
|
+
},
|
|
5000
|
+
async run({ args }) {
|
|
5001
|
+
const name = String(args.name);
|
|
5002
|
+
const token = await requestNestGrant({ command: ["nest", "destroy", name] });
|
|
5003
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5004
|
+
try {
|
|
5005
|
+
const res = await fetch(`${base}/agents/${encodeURIComponent(name)}`, {
|
|
5006
|
+
method: "DELETE",
|
|
5007
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
5008
|
+
});
|
|
5009
|
+
if (!res.ok) {
|
|
5010
|
+
const text = await res.text().catch(() => "");
|
|
5011
|
+
throw new CliError(`nest DELETE /agents/${name} failed: ${res.status} ${text}`);
|
|
5012
|
+
}
|
|
5013
|
+
} catch (err) {
|
|
5014
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5015
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5016
|
+
consola28.error(`Nest daemon is not running at ${base}`);
|
|
5017
|
+
consola28.info(" Run: apes nest install");
|
|
5018
|
+
process2.exit(2);
|
|
5019
|
+
}
|
|
5020
|
+
throw err;
|
|
5021
|
+
}
|
|
5022
|
+
consola28.success(`Destroyed ${name}`);
|
|
5023
|
+
}
|
|
5024
|
+
});
|
|
5025
|
+
|
|
4076
5026
|
// src/commands/nest/install.ts
|
|
4077
5027
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4078
5028
|
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
4079
5029
|
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
4080
5030
|
import { dirname as dirname3, join as join10 } from "path";
|
|
4081
|
-
import { defineCommand as
|
|
4082
|
-
import
|
|
5031
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
5032
|
+
import consola29 from "consola";
|
|
4083
5033
|
|
|
4084
5034
|
// src/commands/nest/apes-agents-adapter.ts
|
|
4085
5035
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4196,7 +5146,7 @@ function installAdapter2() {
|
|
|
4196
5146
|
}
|
|
4197
5147
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4198
5148
|
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4199
|
-
|
|
5149
|
+
consola29.success(`Wrote shapes adapter ${target}`);
|
|
4200
5150
|
return true;
|
|
4201
5151
|
}
|
|
4202
5152
|
function findBinary(name) {
|
|
@@ -4206,12 +5156,12 @@ function findBinary(name) {
|
|
|
4206
5156
|
"/usr/local/bin",
|
|
4207
5157
|
"/usr/bin"
|
|
4208
5158
|
]) {
|
|
4209
|
-
const
|
|
4210
|
-
if (existsSync12(
|
|
5159
|
+
const p2 = join10(dir, name);
|
|
5160
|
+
if (existsSync12(p2)) return p2;
|
|
4211
5161
|
}
|
|
4212
5162
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4213
5163
|
}
|
|
4214
|
-
var installNestCommand =
|
|
5164
|
+
var installNestCommand = defineCommand32({
|
|
4215
5165
|
meta: {
|
|
4216
5166
|
name: "install",
|
|
4217
5167
|
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
@@ -4230,10 +5180,10 @@ var installNestCommand = defineCommand31({
|
|
|
4230
5180
|
}
|
|
4231
5181
|
const nestBin = findBinary("openape-nest");
|
|
4232
5182
|
const apesBin = findBinary("apes");
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
5183
|
+
consola29.info(`Installing nest at ${plistPath()}`);
|
|
5184
|
+
consola29.info(` nest binary: ${nestBin}`);
|
|
5185
|
+
consola29.info(` apes binary: ${apesBin}`);
|
|
5186
|
+
consola29.info(` HTTP port: ${port}`);
|
|
4237
5187
|
installAdapter2();
|
|
4238
5188
|
mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4239
5189
|
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
@@ -4245,9 +5195,9 @@ var installNestCommand = defineCommand31({
|
|
|
4245
5195
|
}
|
|
4246
5196
|
if (existing !== desired) {
|
|
4247
5197
|
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
4248
|
-
|
|
5198
|
+
consola29.success("Wrote launchd plist");
|
|
4249
5199
|
} else {
|
|
4250
|
-
|
|
5200
|
+
consola29.info("plist already up to date");
|
|
4251
5201
|
}
|
|
4252
5202
|
const uid = userInfo2().uid;
|
|
4253
5203
|
try {
|
|
@@ -4255,45 +5205,156 @@ var installNestCommand = defineCommand31({
|
|
|
4255
5205
|
} catch {
|
|
4256
5206
|
}
|
|
4257
5207
|
execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
5208
|
+
consola29.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
5209
|
+
consola29.info("");
|
|
5210
|
+
consola29.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
5211
|
+
consola29.info("");
|
|
5212
|
+
consola29.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
5213
|
+
consola29.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
5214
|
+
consola29.info("");
|
|
5215
|
+
consola29.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
5216
|
+
}
|
|
5217
|
+
});
|
|
5218
|
+
|
|
5219
|
+
// src/commands/nest/list.ts
|
|
5220
|
+
import process3 from "process";
|
|
5221
|
+
import { defineCommand as defineCommand33 } from "citty";
|
|
5222
|
+
import consola30 from "consola";
|
|
5223
|
+
var listNestCommand = defineCommand33({
|
|
5224
|
+
meta: {
|
|
5225
|
+
name: "list",
|
|
5226
|
+
description: "List agents registered with the local nest. Goes through DDISA grants \u2014 YOLO auto-approves under the standard nest policy."
|
|
5227
|
+
},
|
|
5228
|
+
args: {
|
|
5229
|
+
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
5230
|
+
json: { type: "boolean", description: "JSON output for scripts" }
|
|
5231
|
+
},
|
|
5232
|
+
async run({ args }) {
|
|
5233
|
+
const token = await requestNestGrant({ command: ["nest", "list"] });
|
|
5234
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5235
|
+
let resp;
|
|
5236
|
+
try {
|
|
5237
|
+
const res = await fetch(`${base}/agents`, {
|
|
5238
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
5239
|
+
});
|
|
5240
|
+
if (!res.ok) {
|
|
5241
|
+
const text = await res.text().catch(() => "");
|
|
5242
|
+
throw new CliError(`nest GET /agents failed: ${res.status} ${text}`);
|
|
5243
|
+
}
|
|
5244
|
+
resp = await res.json();
|
|
5245
|
+
} catch (err) {
|
|
5246
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5247
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5248
|
+
consola30.error(`Nest daemon is not running at ${base}`);
|
|
5249
|
+
consola30.info(" Run: apes nest install");
|
|
5250
|
+
process3.exit(2);
|
|
5251
|
+
}
|
|
5252
|
+
throw err;
|
|
5253
|
+
}
|
|
5254
|
+
if (args.json) {
|
|
5255
|
+
console.log(JSON.stringify(resp, null, 2));
|
|
5256
|
+
return;
|
|
5257
|
+
}
|
|
5258
|
+
if (resp.agents.length === 0) {
|
|
5259
|
+
consola30.info("(no agents registered with this nest)");
|
|
5260
|
+
return;
|
|
5261
|
+
}
|
|
5262
|
+
consola30.info(`${resp.agents.length} agent(s) registered with this nest:`);
|
|
5263
|
+
for (const a of resp.agents) {
|
|
5264
|
+
const bridge = a.bridge ? " bridge=on" : "";
|
|
5265
|
+
consola30.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
});
|
|
5269
|
+
|
|
5270
|
+
// src/commands/nest/spawn.ts
|
|
5271
|
+
import process4 from "process";
|
|
5272
|
+
import { defineCommand as defineCommand34 } from "citty";
|
|
5273
|
+
import consola31 from "consola";
|
|
5274
|
+
var spawnNestCommand = defineCommand34({
|
|
5275
|
+
meta: {
|
|
5276
|
+
name: "spawn",
|
|
5277
|
+
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."
|
|
5278
|
+
},
|
|
5279
|
+
args: {
|
|
5280
|
+
name: { type: "positional", required: true, description: "Agent name (lowercase, [a-z0-9-], max 24 chars)" },
|
|
5281
|
+
"no-bridge": { type: "boolean", description: "Skip installing the chat-bridge daemon (default: install it)" },
|
|
5282
|
+
"bridge-key": { type: "string", description: "Override LITELLM_API_KEY (default: read from ~/litellm/.env)" },
|
|
5283
|
+
"bridge-base-url": { type: "string", description: "Override LITELLM_BASE_URL (default: read from ~/litellm/.env)" },
|
|
5284
|
+
"bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" },
|
|
5285
|
+
"port": { type: "string", description: "Override nest port (default: 9091)" }
|
|
5286
|
+
},
|
|
5287
|
+
async run({ args }) {
|
|
5288
|
+
const name = String(args.name);
|
|
5289
|
+
const token = await requestNestGrant({ command: ["nest", "spawn"] });
|
|
5290
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5291
|
+
const reqBody = {
|
|
5292
|
+
name,
|
|
5293
|
+
bridge: !args["no-bridge"]
|
|
5294
|
+
};
|
|
5295
|
+
if (typeof args["bridge-key"] === "string") reqBody.bridgeKey = args["bridge-key"];
|
|
5296
|
+
if (typeof args["bridge-base-url"] === "string") reqBody.bridgeBaseUrl = args["bridge-base-url"];
|
|
5297
|
+
if (typeof args["bridge-model"] === "string") reqBody.bridgeModel = args["bridge-model"];
|
|
5298
|
+
let resp;
|
|
5299
|
+
try {
|
|
5300
|
+
const res = await fetch(`${base}/agents`, {
|
|
5301
|
+
method: "POST",
|
|
5302
|
+
headers: {
|
|
5303
|
+
"Authorization": `Bearer ${token}`,
|
|
5304
|
+
"Content-Type": "application/json"
|
|
5305
|
+
},
|
|
5306
|
+
body: JSON.stringify(reqBody)
|
|
5307
|
+
});
|
|
5308
|
+
if (!res.ok) {
|
|
5309
|
+
const text = await res.text().catch(() => "");
|
|
5310
|
+
throw new CliError(`nest POST /agents failed: ${res.status} ${text}`);
|
|
5311
|
+
}
|
|
5312
|
+
resp = await res.json();
|
|
5313
|
+
} catch (err) {
|
|
5314
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5315
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5316
|
+
consola31.error(`Nest daemon is not running at ${base}`);
|
|
5317
|
+
consola31.info(" Run: apes nest install");
|
|
5318
|
+
process4.exit(2);
|
|
5319
|
+
}
|
|
5320
|
+
throw err;
|
|
5321
|
+
}
|
|
5322
|
+
consola31.success(`Spawned ${resp.name} (uid=${resp.uid}, home=${resp.home})`);
|
|
4266
5323
|
}
|
|
4267
5324
|
});
|
|
4268
5325
|
|
|
4269
5326
|
// src/commands/nest/status.ts
|
|
4270
|
-
import
|
|
4271
|
-
import { defineCommand as
|
|
4272
|
-
import
|
|
4273
|
-
var
|
|
4274
|
-
var statusNestCommand = defineCommand32({
|
|
5327
|
+
import process5 from "process";
|
|
5328
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
5329
|
+
import consola32 from "consola";
|
|
5330
|
+
var statusNestCommand = defineCommand35({
|
|
4275
5331
|
meta: {
|
|
4276
5332
|
name: "status",
|
|
4277
|
-
description: "Print
|
|
5333
|
+
description: "Print health of the local nest-daemon (agents registered). Goes through DDISA grants."
|
|
4278
5334
|
},
|
|
4279
5335
|
args: {
|
|
4280
5336
|
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
4281
5337
|
json: { type: "boolean", description: "JSON output for scripts" }
|
|
4282
5338
|
},
|
|
4283
5339
|
async run({ args }) {
|
|
4284
|
-
const
|
|
4285
|
-
const
|
|
5340
|
+
const token = await requestNestGrant({ command: ["nest", "status"] });
|
|
5341
|
+
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
4286
5342
|
let status;
|
|
4287
5343
|
try {
|
|
4288
|
-
const res = await fetch(
|
|
4289
|
-
|
|
5344
|
+
const res = await fetch(`${base}/status`, {
|
|
5345
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
5346
|
+
});
|
|
5347
|
+
if (!res.ok) {
|
|
5348
|
+
const text = await res.text().catch(() => "");
|
|
5349
|
+
throw new CliError(`nest GET /status failed: ${res.status} ${text}`);
|
|
5350
|
+
}
|
|
4290
5351
|
status = await res.json();
|
|
4291
5352
|
} catch (err) {
|
|
4292
5353
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4293
5354
|
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
5355
|
+
consola32.error(`Nest daemon is not running at ${base}`);
|
|
5356
|
+
consola32.info(" Run: apes nest install");
|
|
5357
|
+
process5.exit(2);
|
|
4297
5358
|
}
|
|
4298
5359
|
throw err;
|
|
4299
5360
|
}
|
|
@@ -4301,34 +5362,19 @@ var statusNestCommand = defineCommand32({
|
|
|
4301
5362
|
console.log(JSON.stringify(status, null, 2));
|
|
4302
5363
|
return;
|
|
4303
5364
|
}
|
|
4304
|
-
|
|
4305
|
-
if (status.processes.length === 0) {
|
|
4306
|
-
consola28.info(" (no processes running)");
|
|
4307
|
-
return;
|
|
4308
|
-
}
|
|
4309
|
-
for (const p of status.processes) {
|
|
4310
|
-
const uptime = humanDuration(p.uptimeSec);
|
|
4311
|
-
const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
|
|
4312
|
-
consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
|
|
4313
|
-
}
|
|
5365
|
+
consola32.info(`Nest at ${base} \u2014 ${status.agents} agent(s) registered`);
|
|
4314
5366
|
}
|
|
4315
5367
|
});
|
|
4316
|
-
function humanDuration(sec) {
|
|
4317
|
-
if (sec < 60) return `${sec}s`;
|
|
4318
|
-
if (sec < 3600) return `${Math.floor(sec / 60)}m`;
|
|
4319
|
-
if (sec < 86400) return `${Math.floor(sec / 3600)}h`;
|
|
4320
|
-
return `${Math.floor(sec / 86400)}d`;
|
|
4321
|
-
}
|
|
4322
5368
|
|
|
4323
5369
|
// src/commands/nest/uninstall.ts
|
|
4324
5370
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4325
5371
|
import { existsSync as existsSync13, unlinkSync } from "fs";
|
|
4326
5372
|
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
4327
5373
|
import { join as join11 } from "path";
|
|
4328
|
-
import { defineCommand as
|
|
4329
|
-
import
|
|
5374
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
5375
|
+
import consola33 from "consola";
|
|
4330
5376
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4331
|
-
var uninstallNestCommand =
|
|
5377
|
+
var uninstallNestCommand = defineCommand36({
|
|
4332
5378
|
meta: {
|
|
4333
5379
|
name: "uninstall",
|
|
4334
5380
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
@@ -4338,40 +5384,43 @@ var uninstallNestCommand = defineCommand33({
|
|
|
4338
5384
|
const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4339
5385
|
try {
|
|
4340
5386
|
execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4341
|
-
|
|
5387
|
+
consola33.success("Nest daemon stopped");
|
|
4342
5388
|
} catch {
|
|
4343
|
-
|
|
5389
|
+
consola33.info("Nest daemon was not loaded");
|
|
4344
5390
|
}
|
|
4345
5391
|
if (existsSync13(path2)) {
|
|
4346
5392
|
unlinkSync(path2);
|
|
4347
|
-
|
|
5393
|
+
consola33.success(`Removed ${path2}`);
|
|
4348
5394
|
}
|
|
4349
|
-
|
|
5395
|
+
consola33.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4350
5396
|
}
|
|
4351
5397
|
});
|
|
4352
5398
|
|
|
4353
5399
|
// src/commands/nest/index.ts
|
|
4354
|
-
var nestCommand =
|
|
5400
|
+
var nestCommand = defineCommand37({
|
|
4355
5401
|
meta: {
|
|
4356
5402
|
name: "nest",
|
|
4357
|
-
description: "Manage the local Nest control-plane daemon
|
|
5403
|
+
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."
|
|
4358
5404
|
},
|
|
4359
5405
|
subCommands: {
|
|
4360
5406
|
install: installNestCommand,
|
|
4361
5407
|
enroll: enrollNestCommand,
|
|
4362
5408
|
authorize: authorizeNestCommand,
|
|
4363
5409
|
status: statusNestCommand,
|
|
5410
|
+
list: listNestCommand,
|
|
5411
|
+
spawn: spawnNestCommand,
|
|
5412
|
+
destroy: destroyNestCommand,
|
|
4364
5413
|
uninstall: uninstallNestCommand
|
|
4365
5414
|
}
|
|
4366
5415
|
});
|
|
4367
5416
|
|
|
4368
5417
|
// src/commands/yolo/index.ts
|
|
4369
|
-
import { defineCommand as
|
|
5418
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
4370
5419
|
|
|
4371
5420
|
// src/commands/yolo/clear.ts
|
|
4372
|
-
import { defineCommand as
|
|
4373
|
-
import
|
|
4374
|
-
var yoloClearCommand =
|
|
5421
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
5422
|
+
import consola34 from "consola";
|
|
5423
|
+
var yoloClearCommand = defineCommand38({
|
|
4375
5424
|
meta: {
|
|
4376
5425
|
name: "clear",
|
|
4377
5426
|
description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
|
|
@@ -4400,15 +5449,15 @@ var yoloClearCommand = defineCommand35({
|
|
|
4400
5449
|
const text = await res.text().catch(() => "");
|
|
4401
5450
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
4402
5451
|
}
|
|
4403
|
-
|
|
5452
|
+
consola34.success(`YOLO-policy cleared on ${email}`);
|
|
4404
5453
|
}
|
|
4405
5454
|
});
|
|
4406
5455
|
|
|
4407
5456
|
// src/commands/yolo/set.ts
|
|
4408
|
-
import { defineCommand as
|
|
4409
|
-
import
|
|
5457
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
5458
|
+
import consola35 from "consola";
|
|
4410
5459
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
4411
|
-
var yoloSetCommand =
|
|
5460
|
+
var yoloSetCommand = defineCommand39({
|
|
4412
5461
|
meta: {
|
|
4413
5462
|
name: "set",
|
|
4414
5463
|
description: "Write a YOLO-policy on a DDISA agent you own"
|
|
@@ -4456,12 +5505,12 @@ var yoloSetCommand = defineCommand36({
|
|
|
4456
5505
|
const denyPatterns = parseList(args.deny);
|
|
4457
5506
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
4458
5507
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
if (allowPatterns.length)
|
|
4462
|
-
if (denyPatterns.length)
|
|
4463
|
-
if (denyRiskThreshold)
|
|
4464
|
-
if (expiresAt)
|
|
5508
|
+
consola35.info(`Setting YOLO-policy on ${email}`);
|
|
5509
|
+
consola35.info(` mode: ${mode}`);
|
|
5510
|
+
if (allowPatterns.length) consola35.info(` allow_patterns: ${allowPatterns.join(", ")}`);
|
|
5511
|
+
if (denyPatterns.length) consola35.info(` deny_patterns: ${denyPatterns.join(", ")}`);
|
|
5512
|
+
if (denyRiskThreshold) consola35.info(` deny_risk: ${denyRiskThreshold}`);
|
|
5513
|
+
if (expiresAt) consola35.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
4465
5514
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
4466
5515
|
const res = await fetch(url, {
|
|
4467
5516
|
method: "PUT",
|
|
@@ -4481,27 +5530,27 @@ var yoloSetCommand = defineCommand36({
|
|
|
4481
5530
|
const text = await res.text().catch(() => "");
|
|
4482
5531
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
4483
5532
|
}
|
|
4484
|
-
|
|
5533
|
+
consola35.success(`YOLO-policy applied to ${email}`);
|
|
4485
5534
|
}
|
|
4486
5535
|
});
|
|
4487
5536
|
function parseList(s) {
|
|
4488
5537
|
if (!s) return [];
|
|
4489
|
-
return s.split(",").map((
|
|
5538
|
+
return s.split(",").map((p2) => p2.trim()).filter(Boolean);
|
|
4490
5539
|
}
|
|
4491
5540
|
function parseExpiresIn(s) {
|
|
4492
5541
|
if (!s) return null;
|
|
4493
5542
|
const m = s.match(/^(\d+)([hdw])$/);
|
|
4494
5543
|
if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
|
|
4495
|
-
const
|
|
5544
|
+
const n2 = Number(m[1]);
|
|
4496
5545
|
const unit = m[2];
|
|
4497
5546
|
const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
|
|
4498
|
-
return Math.floor(Date.now() / 1e3) +
|
|
5547
|
+
return Math.floor(Date.now() / 1e3) + n2 * seconds;
|
|
4499
5548
|
}
|
|
4500
5549
|
|
|
4501
5550
|
// src/commands/yolo/show.ts
|
|
4502
|
-
import { defineCommand as
|
|
4503
|
-
import
|
|
4504
|
-
var yoloShowCommand =
|
|
5551
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
5552
|
+
import consola36 from "consola";
|
|
5553
|
+
var yoloShowCommand = defineCommand40({
|
|
4505
5554
|
meta: {
|
|
4506
5555
|
name: "show",
|
|
4507
5556
|
description: "Print the YOLO-policy currently set on a DDISA agent"
|
|
@@ -4538,17 +5587,17 @@ var yoloShowCommand = defineCommand37({
|
|
|
4538
5587
|
console.log(JSON.stringify(policy, null, 2));
|
|
4539
5588
|
return;
|
|
4540
5589
|
}
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
5590
|
+
consola36.info(`YOLO-policy for ${email}`);
|
|
5591
|
+
consola36.info(` mode: ${policy.mode}`);
|
|
5592
|
+
consola36.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
|
|
5593
|
+
consola36.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
|
|
5594
|
+
consola36.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
|
|
5595
|
+
consola36.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
|
|
4547
5596
|
}
|
|
4548
5597
|
});
|
|
4549
5598
|
|
|
4550
5599
|
// src/commands/yolo/index.ts
|
|
4551
|
-
var yoloCommand =
|
|
5600
|
+
var yoloCommand = defineCommand41({
|
|
4552
5601
|
meta: {
|
|
4553
5602
|
name: "yolo",
|
|
4554
5603
|
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)."
|
|
@@ -4561,15 +5610,15 @@ var yoloCommand = defineCommand38({
|
|
|
4561
5610
|
});
|
|
4562
5611
|
|
|
4563
5612
|
// src/commands/adapter/index.ts
|
|
4564
|
-
import { defineCommand as
|
|
4565
|
-
import
|
|
4566
|
-
var adapterCommand =
|
|
5613
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5614
|
+
import consola37 from "consola";
|
|
5615
|
+
var adapterCommand = defineCommand42({
|
|
4567
5616
|
meta: {
|
|
4568
5617
|
name: "adapter",
|
|
4569
5618
|
description: "Manage CLI adapters"
|
|
4570
5619
|
},
|
|
4571
5620
|
subCommands: {
|
|
4572
|
-
list:
|
|
5621
|
+
list: defineCommand42({
|
|
4573
5622
|
meta: {
|
|
4574
5623
|
name: "list",
|
|
4575
5624
|
description: "List available adapters"
|
|
@@ -4600,7 +5649,7 @@ var adapterCommand = defineCommand39({
|
|
|
4600
5649
|
`);
|
|
4601
5650
|
return;
|
|
4602
5651
|
}
|
|
4603
|
-
|
|
5652
|
+
consola37.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4604
5653
|
for (const a of index2.adapters) {
|
|
4605
5654
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4606
5655
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4622,7 +5671,7 @@ var adapterCommand = defineCommand39({
|
|
|
4622
5671
|
return;
|
|
4623
5672
|
}
|
|
4624
5673
|
if (local.length === 0) {
|
|
4625
|
-
|
|
5674
|
+
consola37.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4626
5675
|
return;
|
|
4627
5676
|
}
|
|
4628
5677
|
for (const a of local) {
|
|
@@ -4630,7 +5679,7 @@ var adapterCommand = defineCommand39({
|
|
|
4630
5679
|
}
|
|
4631
5680
|
}
|
|
4632
5681
|
}),
|
|
4633
|
-
install:
|
|
5682
|
+
install: defineCommand42({
|
|
4634
5683
|
meta: {
|
|
4635
5684
|
name: "install",
|
|
4636
5685
|
description: "Install an adapter from the registry"
|
|
@@ -4659,24 +5708,24 @@ var adapterCommand = defineCommand39({
|
|
|
4659
5708
|
for (const id of ids) {
|
|
4660
5709
|
const entry = findAdapter(index, id);
|
|
4661
5710
|
if (!entry) {
|
|
4662
|
-
|
|
5711
|
+
consola37.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4663
5712
|
continue;
|
|
4664
5713
|
}
|
|
4665
5714
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4666
5715
|
if (conflicts.length > 0) {
|
|
4667
|
-
for (const
|
|
4668
|
-
|
|
4669
|
-
|
|
5716
|
+
for (const c2 of conflicts) {
|
|
5717
|
+
consola37.warn(`Conflicting adapter found: ${c2.path} (id: ${c2.adapterId}, executable: ${c2.executable})`);
|
|
5718
|
+
consola37.warn(` Remove it with: apes adapter remove ${c2.adapterId}`);
|
|
4670
5719
|
}
|
|
4671
5720
|
}
|
|
4672
5721
|
const result = await installAdapter(entry, { local });
|
|
4673
5722
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4674
|
-
|
|
4675
|
-
|
|
5723
|
+
consola37.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
5724
|
+
consola37.info(`Digest: ${result.digest}`);
|
|
4676
5725
|
}
|
|
4677
5726
|
}
|
|
4678
5727
|
}),
|
|
4679
|
-
remove:
|
|
5728
|
+
remove: defineCommand42({
|
|
4680
5729
|
meta: {
|
|
4681
5730
|
name: "remove",
|
|
4682
5731
|
description: "Remove an installed adapter"
|
|
@@ -4699,9 +5748,9 @@ var adapterCommand = defineCommand39({
|
|
|
4699
5748
|
let failed = false;
|
|
4700
5749
|
for (const id of ids) {
|
|
4701
5750
|
if (removeAdapter(id, local)) {
|
|
4702
|
-
|
|
5751
|
+
consola37.success(`Removed adapter: ${id}`);
|
|
4703
5752
|
} else {
|
|
4704
|
-
|
|
5753
|
+
consola37.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4705
5754
|
failed = true;
|
|
4706
5755
|
}
|
|
4707
5756
|
}
|
|
@@ -4709,7 +5758,7 @@ var adapterCommand = defineCommand39({
|
|
|
4709
5758
|
throw new CliError("Some adapters could not be removed");
|
|
4710
5759
|
}
|
|
4711
5760
|
}),
|
|
4712
|
-
info:
|
|
5761
|
+
info: defineCommand42({
|
|
4713
5762
|
meta: {
|
|
4714
5763
|
name: "info",
|
|
4715
5764
|
description: "Show detailed adapter information"
|
|
@@ -4751,7 +5800,7 @@ var adapterCommand = defineCommand39({
|
|
|
4751
5800
|
}
|
|
4752
5801
|
}
|
|
4753
5802
|
}),
|
|
4754
|
-
search:
|
|
5803
|
+
search: defineCommand42({
|
|
4755
5804
|
meta: {
|
|
4756
5805
|
name: "search",
|
|
4757
5806
|
description: "Search adapters in the registry"
|
|
@@ -4783,7 +5832,7 @@ var adapterCommand = defineCommand39({
|
|
|
4783
5832
|
return;
|
|
4784
5833
|
}
|
|
4785
5834
|
if (results.length === 0) {
|
|
4786
|
-
|
|
5835
|
+
consola37.info(`No adapters matching "${query}"`);
|
|
4787
5836
|
return;
|
|
4788
5837
|
}
|
|
4789
5838
|
for (const a of results) {
|
|
@@ -4792,7 +5841,7 @@ var adapterCommand = defineCommand39({
|
|
|
4792
5841
|
}
|
|
4793
5842
|
}
|
|
4794
5843
|
}),
|
|
4795
|
-
update:
|
|
5844
|
+
update: defineCommand42({
|
|
4796
5845
|
meta: {
|
|
4797
5846
|
name: "update",
|
|
4798
5847
|
description: "Update installed adapters"
|
|
@@ -4818,33 +5867,33 @@ var adapterCommand = defineCommand39({
|
|
|
4818
5867
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4819
5868
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4820
5869
|
if (targets.length === 0) {
|
|
4821
|
-
|
|
5870
|
+
consola37.info("No adapters installed to update.");
|
|
4822
5871
|
return;
|
|
4823
5872
|
}
|
|
4824
5873
|
for (const id of targets) {
|
|
4825
5874
|
const entry = findAdapter(index, id);
|
|
4826
5875
|
if (!entry) {
|
|
4827
|
-
|
|
5876
|
+
consola37.warn(`${id}: not found in registry, skipping`);
|
|
4828
5877
|
continue;
|
|
4829
5878
|
}
|
|
4830
5879
|
const localDigest = getInstalledDigest(id, false);
|
|
4831
5880
|
if (localDigest === entry.digest) {
|
|
4832
|
-
|
|
5881
|
+
consola37.info(`${id}: already up to date`);
|
|
4833
5882
|
continue;
|
|
4834
5883
|
}
|
|
4835
5884
|
if (localDigest && !args.yes) {
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
5885
|
+
consola37.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
5886
|
+
consola37.info(` Old: ${localDigest}`);
|
|
5887
|
+
consola37.info(` New: ${entry.digest}`);
|
|
5888
|
+
consola37.info(" Use --yes to confirm");
|
|
4840
5889
|
continue;
|
|
4841
5890
|
}
|
|
4842
5891
|
const result = await installAdapter(entry);
|
|
4843
|
-
|
|
5892
|
+
consola37.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4844
5893
|
}
|
|
4845
5894
|
}
|
|
4846
5895
|
}),
|
|
4847
|
-
verify:
|
|
5896
|
+
verify: defineCommand42({
|
|
4848
5897
|
meta: {
|
|
4849
5898
|
name: "verify",
|
|
4850
5899
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4877,7 +5926,7 @@ var adapterCommand = defineCommand39({
|
|
|
4877
5926
|
if (!localDigest)
|
|
4878
5927
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4879
5928
|
if (localDigest === entry.digest) {
|
|
4880
|
-
|
|
5929
|
+
consola37.success(`${id}: digest matches registry`);
|
|
4881
5930
|
} else {
|
|
4882
5931
|
console.log(` Local: ${localDigest}`);
|
|
4883
5932
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4890,10 +5939,10 @@ var adapterCommand = defineCommand39({
|
|
|
4890
5939
|
|
|
4891
5940
|
// src/commands/run.ts
|
|
4892
5941
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
4893
|
-
import { hostname as
|
|
5942
|
+
import { hostname as hostname6 } from "os";
|
|
4894
5943
|
import { basename } from "path";
|
|
4895
|
-
import { defineCommand as
|
|
4896
|
-
import
|
|
5944
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5945
|
+
import consola38 from "consola";
|
|
4897
5946
|
function shouldWaitForGrant(args) {
|
|
4898
5947
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
4899
5948
|
}
|
|
@@ -4911,16 +5960,16 @@ function getUserMode() {
|
|
|
4911
5960
|
function getAsyncExitCode() {
|
|
4912
5961
|
const envValue = process.env.APES_ASYNC_EXIT_CODE;
|
|
4913
5962
|
if (envValue !== void 0 && envValue !== "") {
|
|
4914
|
-
const
|
|
4915
|
-
if (Number.isFinite(
|
|
4916
|
-
return Math.floor(
|
|
5963
|
+
const n2 = Number(envValue);
|
|
5964
|
+
if (Number.isFinite(n2) && n2 >= 0 && n2 <= 255)
|
|
5965
|
+
return Math.floor(n2);
|
|
4917
5966
|
}
|
|
4918
5967
|
const cfg = loadConfig();
|
|
4919
5968
|
const cfgValue = cfg.defaults?.async_exit_code;
|
|
4920
5969
|
if (cfgValue !== void 0 && cfgValue !== "") {
|
|
4921
|
-
const
|
|
4922
|
-
if (Number.isFinite(
|
|
4923
|
-
return Math.floor(
|
|
5970
|
+
const n2 = Number(cfgValue);
|
|
5971
|
+
if (Number.isFinite(n2) && n2 >= 0 && n2 <= 255)
|
|
5972
|
+
return Math.floor(n2);
|
|
4924
5973
|
}
|
|
4925
5974
|
return 75;
|
|
4926
5975
|
}
|
|
@@ -4930,7 +5979,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4930
5979
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4931
5980
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4932
5981
|
if (mode === "human") {
|
|
4933
|
-
|
|
5982
|
+
consola38.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4934
5983
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4935
5984
|
console.log(` Check status: ${statusCmd}`);
|
|
4936
5985
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4940,7 +5989,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4940
5989
|
return;
|
|
4941
5990
|
}
|
|
4942
5991
|
const maxMin = getPollMaxMinutes();
|
|
4943
|
-
|
|
5992
|
+
consola38.success(`Grant ${grant.id} created (pending approval)`);
|
|
4944
5993
|
console.log(` Approve: ${approveUrl}`);
|
|
4945
5994
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4946
5995
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4962,7 +6011,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4962
6011
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4963
6012
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4964
6013
|
}
|
|
4965
|
-
var runCommand =
|
|
6014
|
+
var runCommand = defineCommand43({
|
|
4966
6015
|
meta: {
|
|
4967
6016
|
name: "run",
|
|
4968
6017
|
description: "Execute a grant-secured command"
|
|
@@ -5051,7 +6100,7 @@ async function runShellMode(command, args) {
|
|
|
5051
6100
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
5052
6101
|
if (adapterHandled) return;
|
|
5053
6102
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5054
|
-
const targetHost = args.host ||
|
|
6103
|
+
const targetHost = args.host || hostname6();
|
|
5055
6104
|
try {
|
|
5056
6105
|
const grants = await apiFetch(
|
|
5057
6106
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -5065,7 +6114,7 @@ async function runShellMode(command, args) {
|
|
|
5065
6114
|
}
|
|
5066
6115
|
} catch {
|
|
5067
6116
|
}
|
|
5068
|
-
|
|
6117
|
+
consola38.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
5069
6118
|
const grant = await apiFetch(grantsUrl, {
|
|
5070
6119
|
method: "POST",
|
|
5071
6120
|
body: {
|
|
@@ -5085,8 +6134,8 @@ async function runShellMode(command, args) {
|
|
|
5085
6134
|
host: targetHost
|
|
5086
6135
|
});
|
|
5087
6136
|
if (shouldWaitForGrant(args)) {
|
|
5088
|
-
|
|
5089
|
-
|
|
6137
|
+
consola38.info(`Grant requested: ${grant.id}`);
|
|
6138
|
+
consola38.info("Waiting for approval...");
|
|
5090
6139
|
const maxWait = 3e5;
|
|
5091
6140
|
const interval = 3e3;
|
|
5092
6141
|
const start = Date.now();
|
|
@@ -5096,7 +6145,7 @@ async function runShellMode(command, args) {
|
|
|
5096
6145
|
break;
|
|
5097
6146
|
if (status.status === "denied" || status.status === "revoked")
|
|
5098
6147
|
throw new CliError(`Grant ${status.status}.`);
|
|
5099
|
-
await new Promise((
|
|
6148
|
+
await new Promise((r3) => setTimeout(r3, interval));
|
|
5100
6149
|
}
|
|
5101
6150
|
execShellCommand(command);
|
|
5102
6151
|
return;
|
|
@@ -5117,13 +6166,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5117
6166
|
try {
|
|
5118
6167
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
5119
6168
|
} catch (err) {
|
|
5120
|
-
|
|
6169
|
+
consola38.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
5121
6170
|
return false;
|
|
5122
6171
|
}
|
|
5123
6172
|
try {
|
|
5124
6173
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5125
6174
|
if (existingGrantId) {
|
|
5126
|
-
|
|
6175
|
+
consola38.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
5127
6176
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5128
6177
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5129
6178
|
return true;
|
|
@@ -5131,27 +6180,27 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5131
6180
|
} catch {
|
|
5132
6181
|
}
|
|
5133
6182
|
const approval = args.approval ?? "once";
|
|
5134
|
-
|
|
6183
|
+
consola38.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
5135
6184
|
const grant = await createShapesGrant(resolved, {
|
|
5136
6185
|
idp,
|
|
5137
6186
|
approval,
|
|
5138
6187
|
reason: args.reason || `ape-shell: ${resolved.detail.display}`
|
|
5139
6188
|
});
|
|
5140
6189
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5141
|
-
const
|
|
5142
|
-
|
|
5143
|
-
|
|
6190
|
+
const n2 = grant.similar_grants.similar_grants.length;
|
|
6191
|
+
consola38.info("");
|
|
6192
|
+
consola38.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5144
6193
|
}
|
|
5145
6194
|
notifyGrantPending({
|
|
5146
6195
|
grantId: grant.id,
|
|
5147
6196
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
5148
6197
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
5149
6198
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
5150
|
-
host: args.host ||
|
|
6199
|
+
host: args.host || hostname6()
|
|
5151
6200
|
});
|
|
5152
6201
|
if (shouldWaitForGrant(args)) {
|
|
5153
|
-
|
|
5154
|
-
|
|
6202
|
+
consola38.info(`Grant requested: ${grant.id}`);
|
|
6203
|
+
consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5155
6204
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5156
6205
|
if (status !== "approved")
|
|
5157
6206
|
throw new CliError(`Grant ${status}`);
|
|
@@ -5225,7 +6274,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5225
6274
|
try {
|
|
5226
6275
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5227
6276
|
if (existingGrantId) {
|
|
5228
|
-
|
|
6277
|
+
consola38.info(`Reusing existing grant: ${existingGrantId}`);
|
|
5229
6278
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5230
6279
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5231
6280
|
return;
|
|
@@ -5238,18 +6287,18 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5238
6287
|
...args.reason ? { reason: args.reason } : {}
|
|
5239
6288
|
});
|
|
5240
6289
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5241
|
-
const
|
|
5242
|
-
|
|
5243
|
-
|
|
6290
|
+
const n2 = grant.similar_grants.similar_grants.length;
|
|
6291
|
+
consola38.info("");
|
|
6292
|
+
consola38.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5244
6293
|
if (grant.similar_grants.widened_details?.length) {
|
|
5245
6294
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
5246
|
-
|
|
6295
|
+
consola38.info(` Broader scope: ${wider}`);
|
|
5247
6296
|
}
|
|
5248
|
-
|
|
6297
|
+
consola38.info("");
|
|
5249
6298
|
}
|
|
5250
6299
|
if (shouldWaitForGrant(args)) {
|
|
5251
|
-
|
|
5252
|
-
|
|
6300
|
+
consola38.info(`Grant requested: ${grant.id}`);
|
|
6301
|
+
consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5253
6302
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5254
6303
|
if (status !== "approved")
|
|
5255
6304
|
throw new Error(`Grant ${status}`);
|
|
@@ -5268,8 +6317,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5268
6317
|
const idp = getIdpUrl(args.idp);
|
|
5269
6318
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5270
6319
|
const command = action.split(" ");
|
|
5271
|
-
const targetHost = args.host ||
|
|
5272
|
-
|
|
6320
|
+
const targetHost = args.host || hostname6();
|
|
6321
|
+
consola38.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5273
6322
|
const grant = await apiFetch(grantsUrl, {
|
|
5274
6323
|
method: "POST",
|
|
5275
6324
|
body: {
|
|
@@ -5286,9 +6335,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5286
6335
|
printPendingGrantInfo(grant, idp);
|
|
5287
6336
|
throw new CliExit(getAsyncExitCode());
|
|
5288
6337
|
}
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
6338
|
+
consola38.success(`Grant requested: ${grant.id}`);
|
|
6339
|
+
consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
6340
|
+
consola38.info("Waiting for approval...");
|
|
5292
6341
|
const maxWait = 15 * 60 * 1e3;
|
|
5293
6342
|
const interval = 3e3;
|
|
5294
6343
|
const start = Date.now();
|
|
@@ -5296,14 +6345,14 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5296
6345
|
while (Date.now() - start < maxWait) {
|
|
5297
6346
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5298
6347
|
if (status.status === "approved") {
|
|
5299
|
-
|
|
6348
|
+
consola38.success("Grant approved!");
|
|
5300
6349
|
approved = true;
|
|
5301
6350
|
break;
|
|
5302
6351
|
}
|
|
5303
6352
|
if (status.status === "denied" || status.status === "revoked") {
|
|
5304
6353
|
throw new CliError(`Grant ${status.status}.`);
|
|
5305
6354
|
}
|
|
5306
|
-
await new Promise((
|
|
6355
|
+
await new Promise((r3) => setTimeout(r3, interval));
|
|
5307
6356
|
}
|
|
5308
6357
|
if (!approved) {
|
|
5309
6358
|
const minutes = Math.round(maxWait / 6e4);
|
|
@@ -5311,12 +6360,12 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5311
6360
|
`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.`
|
|
5312
6361
|
);
|
|
5313
6362
|
}
|
|
5314
|
-
|
|
6363
|
+
consola38.info("Fetching grant token...");
|
|
5315
6364
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5316
6365
|
method: "POST"
|
|
5317
6366
|
});
|
|
5318
6367
|
if (audience === "escapes") {
|
|
5319
|
-
|
|
6368
|
+
consola38.info(`Executing: ${command.join(" ")}`);
|
|
5320
6369
|
try {
|
|
5321
6370
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5322
6371
|
execFileSync12(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
@@ -5334,8 +6383,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5334
6383
|
|
|
5335
6384
|
// src/commands/proxy.ts
|
|
5336
6385
|
import { spawn as spawn2 } from "child_process";
|
|
5337
|
-
import { defineCommand as
|
|
5338
|
-
import
|
|
6386
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
6387
|
+
import consola39 from "consola";
|
|
5339
6388
|
|
|
5340
6389
|
// src/proxy/config.ts
|
|
5341
6390
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5478,10 +6527,10 @@ function resolveProxyConfigOptions() {
|
|
|
5478
6527
|
77
|
|
5479
6528
|
);
|
|
5480
6529
|
}
|
|
5481
|
-
|
|
6530
|
+
consola39.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5482
6531
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5483
6532
|
}
|
|
5484
|
-
var proxyCommand =
|
|
6533
|
+
var proxyCommand = defineCommand44({
|
|
5485
6534
|
meta: {
|
|
5486
6535
|
name: "proxy",
|
|
5487
6536
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -5503,12 +6552,12 @@ var proxyCommand = defineCommand41({
|
|
|
5503
6552
|
let close = null;
|
|
5504
6553
|
if (reuseUrl) {
|
|
5505
6554
|
proxyUrl = reuseUrl;
|
|
5506
|
-
|
|
6555
|
+
consola39.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5507
6556
|
} else {
|
|
5508
6557
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5509
6558
|
proxyUrl = ephemeral.url;
|
|
5510
6559
|
close = ephemeral.close;
|
|
5511
|
-
|
|
6560
|
+
consola39.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5512
6561
|
}
|
|
5513
6562
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5514
6563
|
const childEnv = {
|
|
@@ -5540,7 +6589,7 @@ var proxyCommand = defineCommand41({
|
|
|
5540
6589
|
else resolveExit(code ?? 0);
|
|
5541
6590
|
});
|
|
5542
6591
|
child.once("error", (err) => {
|
|
5543
|
-
|
|
6592
|
+
consola39.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5544
6593
|
resolveExit(127);
|
|
5545
6594
|
});
|
|
5546
6595
|
});
|
|
@@ -5554,8 +6603,8 @@ function signalNumber(signal) {
|
|
|
5554
6603
|
}
|
|
5555
6604
|
|
|
5556
6605
|
// src/commands/explain.ts
|
|
5557
|
-
import { defineCommand as
|
|
5558
|
-
var explainCommand =
|
|
6606
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
6607
|
+
var explainCommand = defineCommand45({
|
|
5559
6608
|
meta: {
|
|
5560
6609
|
name: "explain",
|
|
5561
6610
|
description: "Show what permission a command would need"
|
|
@@ -5593,9 +6642,9 @@ var explainCommand = defineCommand42({
|
|
|
5593
6642
|
});
|
|
5594
6643
|
|
|
5595
6644
|
// src/commands/config/get.ts
|
|
5596
|
-
import { defineCommand as
|
|
5597
|
-
import
|
|
5598
|
-
var configGetCommand =
|
|
6645
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6646
|
+
import consola40 from "consola";
|
|
6647
|
+
var configGetCommand = defineCommand46({
|
|
5599
6648
|
meta: {
|
|
5600
6649
|
name: "get",
|
|
5601
6650
|
description: "Get a configuration value"
|
|
@@ -5615,7 +6664,7 @@ var configGetCommand = defineCommand43({
|
|
|
5615
6664
|
if (idp)
|
|
5616
6665
|
console.log(idp);
|
|
5617
6666
|
else
|
|
5618
|
-
|
|
6667
|
+
consola40.info("No IdP configured.");
|
|
5619
6668
|
break;
|
|
5620
6669
|
}
|
|
5621
6670
|
case "email": {
|
|
@@ -5623,7 +6672,7 @@ var configGetCommand = defineCommand43({
|
|
|
5623
6672
|
if (auth?.email)
|
|
5624
6673
|
console.log(auth.email);
|
|
5625
6674
|
else
|
|
5626
|
-
|
|
6675
|
+
consola40.info("Not logged in.");
|
|
5627
6676
|
break;
|
|
5628
6677
|
}
|
|
5629
6678
|
default: {
|
|
@@ -5636,7 +6685,7 @@ var configGetCommand = defineCommand43({
|
|
|
5636
6685
|
if (sectionObj && field in sectionObj) {
|
|
5637
6686
|
console.log(sectionObj[field]);
|
|
5638
6687
|
} else {
|
|
5639
|
-
|
|
6688
|
+
consola40.info(`Key "${key}" not set.`);
|
|
5640
6689
|
}
|
|
5641
6690
|
} else {
|
|
5642
6691
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5647,9 +6696,9 @@ var configGetCommand = defineCommand43({
|
|
|
5647
6696
|
});
|
|
5648
6697
|
|
|
5649
6698
|
// src/commands/config/set.ts
|
|
5650
|
-
import { defineCommand as
|
|
5651
|
-
import
|
|
5652
|
-
var configSetCommand =
|
|
6699
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
6700
|
+
import consola41 from "consola";
|
|
6701
|
+
var configSetCommand = defineCommand47({
|
|
5653
6702
|
meta: {
|
|
5654
6703
|
name: "set",
|
|
5655
6704
|
description: "Set a configuration value"
|
|
@@ -5685,12 +6734,12 @@ var configSetCommand = defineCommand44({
|
|
|
5685
6734
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5686
6735
|
}
|
|
5687
6736
|
saveConfig(config);
|
|
5688
|
-
|
|
6737
|
+
consola41.success(`Set ${key} = ${value}`);
|
|
5689
6738
|
}
|
|
5690
6739
|
});
|
|
5691
6740
|
|
|
5692
6741
|
// src/commands/fetch/index.ts
|
|
5693
|
-
import { defineCommand as
|
|
6742
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
5694
6743
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5695
6744
|
const token = getAuthToken();
|
|
5696
6745
|
if (!token) {
|
|
@@ -5726,13 +6775,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5726
6775
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5727
6776
|
}
|
|
5728
6777
|
}
|
|
5729
|
-
var fetchCommand =
|
|
6778
|
+
var fetchCommand = defineCommand48({
|
|
5730
6779
|
meta: {
|
|
5731
6780
|
name: "fetch",
|
|
5732
6781
|
description: "Make authenticated HTTP requests"
|
|
5733
6782
|
},
|
|
5734
6783
|
subCommands: {
|
|
5735
|
-
get:
|
|
6784
|
+
get: defineCommand48({
|
|
5736
6785
|
meta: {
|
|
5737
6786
|
name: "get",
|
|
5738
6787
|
description: "GET request with auth token"
|
|
@@ -5758,7 +6807,7 @@ var fetchCommand = defineCommand45({
|
|
|
5758
6807
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5759
6808
|
}
|
|
5760
6809
|
}),
|
|
5761
|
-
post:
|
|
6810
|
+
post: defineCommand48({
|
|
5762
6811
|
meta: {
|
|
5763
6812
|
name: "post",
|
|
5764
6813
|
description: "POST request with auth token"
|
|
@@ -5797,8 +6846,8 @@ var fetchCommand = defineCommand45({
|
|
|
5797
6846
|
});
|
|
5798
6847
|
|
|
5799
6848
|
// src/commands/mcp/index.ts
|
|
5800
|
-
import { defineCommand as
|
|
5801
|
-
var mcpCommand =
|
|
6849
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6850
|
+
var mcpCommand = defineCommand49({
|
|
5802
6851
|
meta: {
|
|
5803
6852
|
name: "mcp",
|
|
5804
6853
|
description: "Start MCP server for AI agents"
|
|
@@ -5821,7 +6870,7 @@ var mcpCommand = defineCommand46({
|
|
|
5821
6870
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5822
6871
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5823
6872
|
}
|
|
5824
|
-
const { startMcpServer } = await import("./server-
|
|
6873
|
+
const { startMcpServer } = await import("./server-6B26NNLZ.js");
|
|
5825
6874
|
await startMcpServer(transport, port);
|
|
5826
6875
|
}
|
|
5827
6876
|
});
|
|
@@ -5831,8 +6880,8 @@ import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSyn
|
|
|
5831
6880
|
import { randomBytes } from "crypto";
|
|
5832
6881
|
import { execFileSync as execFileSync13 } from "child_process";
|
|
5833
6882
|
import { join as join13 } from "path";
|
|
5834
|
-
import { defineCommand as
|
|
5835
|
-
import
|
|
6883
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6884
|
+
import consola42 from "consola";
|
|
5836
6885
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5837
6886
|
async function downloadTemplate(repo, targetDir) {
|
|
5838
6887
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
@@ -5849,20 +6898,20 @@ function installDeps(dir) {
|
|
|
5849
6898
|
}
|
|
5850
6899
|
}
|
|
5851
6900
|
async function promptChoice(message, choices) {
|
|
5852
|
-
const result = await
|
|
6901
|
+
const result = await consola42.prompt(message, { type: "select", options: choices });
|
|
5853
6902
|
if (typeof result === "symbol") {
|
|
5854
6903
|
throw new CliExit(0);
|
|
5855
6904
|
}
|
|
5856
6905
|
return result;
|
|
5857
6906
|
}
|
|
5858
6907
|
async function promptText(message, defaultValue) {
|
|
5859
|
-
const result = await
|
|
6908
|
+
const result = await consola42.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5860
6909
|
if (typeof result === "symbol") {
|
|
5861
6910
|
throw new CliExit(0);
|
|
5862
6911
|
}
|
|
5863
6912
|
return result || defaultValue || "";
|
|
5864
6913
|
}
|
|
5865
|
-
var initCommand =
|
|
6914
|
+
var initCommand = defineCommand50({
|
|
5866
6915
|
meta: {
|
|
5867
6916
|
name: "init",
|
|
5868
6917
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5907,20 +6956,20 @@ async function initSP(targetDir) {
|
|
|
5907
6956
|
if (existsSync14(join13(dir, "package.json"))) {
|
|
5908
6957
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5909
6958
|
}
|
|
5910
|
-
|
|
6959
|
+
consola42.start("Scaffolding SP starter...");
|
|
5911
6960
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5912
|
-
|
|
5913
|
-
|
|
6961
|
+
consola42.success("Scaffolded from openape-sp-starter");
|
|
6962
|
+
consola42.start("Installing dependencies...");
|
|
5914
6963
|
installDeps(dir);
|
|
5915
|
-
|
|
6964
|
+
consola42.success("Dependencies installed");
|
|
5916
6965
|
const envExample = join13(dir, ".env.example");
|
|
5917
6966
|
const envFile = join13(dir, ".env");
|
|
5918
6967
|
if (existsSync14(envExample) && !existsSync14(envFile)) {
|
|
5919
6968
|
copyFileSync(envExample, envFile);
|
|
5920
|
-
|
|
6969
|
+
consola42.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5921
6970
|
}
|
|
5922
6971
|
console.log("");
|
|
5923
|
-
|
|
6972
|
+
consola42.box([
|
|
5924
6973
|
`cd ${dir}`,
|
|
5925
6974
|
"npm run dev",
|
|
5926
6975
|
"",
|
|
@@ -5939,15 +6988,15 @@ async function initIdP(targetDir) {
|
|
|
5939
6988
|
"s3 (S3-compatible)"
|
|
5940
6989
|
]);
|
|
5941
6990
|
const adminEmail = await promptText("Admin email");
|
|
5942
|
-
|
|
6991
|
+
consola42.start("Scaffolding IdP starter...");
|
|
5943
6992
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5944
|
-
|
|
5945
|
-
|
|
6993
|
+
consola42.success("Scaffolded from openape-idp-starter");
|
|
6994
|
+
consola42.start("Installing dependencies...");
|
|
5946
6995
|
installDeps(dir);
|
|
5947
|
-
|
|
6996
|
+
consola42.success("Dependencies installed");
|
|
5948
6997
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5949
6998
|
const managementToken = randomBytes(32).toString("hex");
|
|
5950
|
-
|
|
6999
|
+
consola42.success("Secrets generated");
|
|
5951
7000
|
const isLocalhost = domain === "localhost";
|
|
5952
7001
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5953
7002
|
const envContent = [
|
|
@@ -5963,9 +7012,9 @@ async function initIdP(targetDir) {
|
|
|
5963
7012
|
].join("\n");
|
|
5964
7013
|
writeFileSync9(join13(dir, ".env"), `${envContent}
|
|
5965
7014
|
`, { mode: 384 });
|
|
5966
|
-
|
|
7015
|
+
consola42.success(".env created");
|
|
5967
7016
|
console.log("");
|
|
5968
|
-
|
|
7017
|
+
consola42.box([
|
|
5969
7018
|
`cd ${dir}`,
|
|
5970
7019
|
"npm run dev",
|
|
5971
7020
|
"",
|
|
@@ -5985,8 +7034,8 @@ import { Buffer as Buffer5 } from "buffer";
|
|
|
5985
7034
|
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
5986
7035
|
import { execFile as execFile2 } from "child_process";
|
|
5987
7036
|
import { sign as sign2 } from "crypto";
|
|
5988
|
-
import { defineCommand as
|
|
5989
|
-
import
|
|
7037
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
7038
|
+
import consola43 from "consola";
|
|
5990
7039
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5991
7040
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5992
7041
|
var POLL_INTERVAL = 3e3;
|
|
@@ -6029,7 +7078,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
6029
7078
|
}
|
|
6030
7079
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
6031
7080
|
}
|
|
6032
|
-
var enrollCommand =
|
|
7081
|
+
var enrollCommand = defineCommand51({
|
|
6033
7082
|
meta: {
|
|
6034
7083
|
name: "enroll",
|
|
6035
7084
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -6049,48 +7098,48 @@ var enrollCommand = defineCommand48({
|
|
|
6049
7098
|
}
|
|
6050
7099
|
},
|
|
6051
7100
|
async run({ args }) {
|
|
6052
|
-
const idp = args.idp || await
|
|
6053
|
-
if (typeof
|
|
6054
|
-
return
|
|
7101
|
+
const idp = args.idp || await consola43.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r3) => {
|
|
7102
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7103
|
+
return r3;
|
|
6055
7104
|
}) || DEFAULT_IDP_URL2;
|
|
6056
|
-
const agentName = args.name || await
|
|
6057
|
-
if (typeof
|
|
6058
|
-
return
|
|
7105
|
+
const agentName = args.name || await consola43.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r3) => {
|
|
7106
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7107
|
+
return r3;
|
|
6059
7108
|
});
|
|
6060
7109
|
if (!agentName) {
|
|
6061
7110
|
throw new CliError("Agent name is required.");
|
|
6062
7111
|
}
|
|
6063
|
-
const keyPath = args.key || await
|
|
6064
|
-
if (typeof
|
|
6065
|
-
return
|
|
7112
|
+
const keyPath = args.key || await consola43.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r3) => {
|
|
7113
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7114
|
+
return r3;
|
|
6066
7115
|
}) || DEFAULT_KEY_PATH;
|
|
6067
7116
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6068
7117
|
let publicKey;
|
|
6069
7118
|
if (existsSync15(resolvedKey)) {
|
|
6070
7119
|
publicKey = readPublicKey(resolvedKey);
|
|
6071
|
-
|
|
7120
|
+
consola43.success(`Using existing key ${keyPath}`);
|
|
6072
7121
|
} else {
|
|
6073
|
-
|
|
7122
|
+
consola43.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
6074
7123
|
publicKey = generateAndSaveKey(keyPath);
|
|
6075
|
-
|
|
7124
|
+
consola43.success(`Key pair generated at ${keyPath}`);
|
|
6076
7125
|
}
|
|
6077
7126
|
const encodedKey = encodeURIComponent(publicKey);
|
|
6078
7127
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
6079
|
-
|
|
6080
|
-
|
|
7128
|
+
consola43.info("Opening browser for enrollment...");
|
|
7129
|
+
consola43.info(`\u2192 ${idp}/enroll`);
|
|
6081
7130
|
openBrowser2(enrollUrl);
|
|
6082
7131
|
console.log("");
|
|
6083
|
-
const agentEmail = await
|
|
7132
|
+
const agentEmail = await consola43.prompt(
|
|
6084
7133
|
"Agent email (shown in browser after enrollment)",
|
|
6085
7134
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
6086
|
-
).then((
|
|
6087
|
-
if (typeof
|
|
6088
|
-
return
|
|
7135
|
+
).then((r3) => {
|
|
7136
|
+
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7137
|
+
return r3;
|
|
6089
7138
|
});
|
|
6090
7139
|
if (!agentEmail) {
|
|
6091
7140
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
6092
7141
|
}
|
|
6093
|
-
|
|
7142
|
+
consola43.start("Verifying enrollment...");
|
|
6094
7143
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
6095
7144
|
saveAuth({
|
|
6096
7145
|
idp,
|
|
@@ -6102,18 +7151,18 @@ var enrollCommand = defineCommand48({
|
|
|
6102
7151
|
config.defaults = { ...config.defaults, idp };
|
|
6103
7152
|
config.agent = { key: keyPath, email: agentEmail };
|
|
6104
7153
|
saveConfig(config);
|
|
6105
|
-
|
|
6106
|
-
|
|
7154
|
+
consola43.success(`Agent enrolled as ${agentEmail}`);
|
|
7155
|
+
consola43.success("Config saved to ~/.config/apes/");
|
|
6107
7156
|
console.log("");
|
|
6108
|
-
|
|
7157
|
+
consola43.info("Verify with: apes whoami");
|
|
6109
7158
|
}
|
|
6110
7159
|
});
|
|
6111
7160
|
|
|
6112
7161
|
// src/commands/register-user.ts
|
|
6113
7162
|
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
6114
|
-
import { defineCommand as
|
|
6115
|
-
import
|
|
6116
|
-
var registerUserCommand =
|
|
7163
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
7164
|
+
import consola44 from "consola";
|
|
7165
|
+
var registerUserCommand = defineCommand52({
|
|
6117
7166
|
meta: {
|
|
6118
7167
|
name: "register-user",
|
|
6119
7168
|
description: "Register a sub-user with SSH key"
|
|
@@ -6168,18 +7217,18 @@ var registerUserCommand = defineCommand49({
|
|
|
6168
7217
|
...userType ? { type: userType } : {}
|
|
6169
7218
|
}
|
|
6170
7219
|
});
|
|
6171
|
-
|
|
7220
|
+
consola44.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
6172
7221
|
}
|
|
6173
7222
|
});
|
|
6174
7223
|
|
|
6175
7224
|
// src/commands/utils/index.ts
|
|
6176
|
-
import { defineCommand as
|
|
7225
|
+
import { defineCommand as defineCommand54 } from "citty";
|
|
6177
7226
|
|
|
6178
7227
|
// src/commands/utils/dig.ts
|
|
6179
|
-
import { defineCommand as
|
|
6180
|
-
import
|
|
7228
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
7229
|
+
import consola45 from "consola";
|
|
6181
7230
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
6182
|
-
var digCommand =
|
|
7231
|
+
var digCommand = defineCommand53({
|
|
6183
7232
|
meta: {
|
|
6184
7233
|
name: "dig",
|
|
6185
7234
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -6252,12 +7301,12 @@ var digCommand = defineCommand50({
|
|
|
6252
7301
|
console.log(` domain: ${domain}`);
|
|
6253
7302
|
console.log("");
|
|
6254
7303
|
if (!result.ddisa.found) {
|
|
6255
|
-
|
|
7304
|
+
consola45.warn(`No DDISA record at _ddisa.${domain}`);
|
|
6256
7305
|
if (result.hint) console.log(`
|
|
6257
7306
|
${result.hint}`);
|
|
6258
7307
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6259
7308
|
}
|
|
6260
|
-
|
|
7309
|
+
consola45.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
6261
7310
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
6262
7311
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
6263
7312
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -6267,13 +7316,13 @@ ${result.hint}`);
|
|
|
6267
7316
|
return;
|
|
6268
7317
|
}
|
|
6269
7318
|
if (result.idpDiscovery.ok) {
|
|
6270
|
-
|
|
7319
|
+
consola45.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
6271
7320
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
6272
7321
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
6273
7322
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
6274
7323
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
6275
7324
|
} else {
|
|
6276
|
-
|
|
7325
|
+
consola45.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
6277
7326
|
if (result.hint) console.log(`
|
|
6278
7327
|
${result.hint}`);
|
|
6279
7328
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -6282,7 +7331,7 @@ ${result.hint}`);
|
|
|
6282
7331
|
});
|
|
6283
7332
|
|
|
6284
7333
|
// src/commands/utils/index.ts
|
|
6285
|
-
var utilsCommand =
|
|
7334
|
+
var utilsCommand = defineCommand54({
|
|
6286
7335
|
meta: {
|
|
6287
7336
|
name: "utils",
|
|
6288
7337
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -6293,12 +7342,12 @@ var utilsCommand = defineCommand51({
|
|
|
6293
7342
|
});
|
|
6294
7343
|
|
|
6295
7344
|
// src/commands/sessions/index.ts
|
|
6296
|
-
import { defineCommand as
|
|
7345
|
+
import { defineCommand as defineCommand57 } from "citty";
|
|
6297
7346
|
|
|
6298
7347
|
// src/commands/sessions/list.ts
|
|
6299
|
-
import { defineCommand as
|
|
6300
|
-
import
|
|
6301
|
-
var sessionsListCommand =
|
|
7348
|
+
import { defineCommand as defineCommand55 } from "citty";
|
|
7349
|
+
import consola46 from "consola";
|
|
7350
|
+
var sessionsListCommand = defineCommand55({
|
|
6302
7351
|
meta: {
|
|
6303
7352
|
name: "list",
|
|
6304
7353
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6316,7 +7365,7 @@ var sessionsListCommand = defineCommand52({
|
|
|
6316
7365
|
return;
|
|
6317
7366
|
}
|
|
6318
7367
|
if (result.data.length === 0) {
|
|
6319
|
-
|
|
7368
|
+
consola46.info("No active sessions.");
|
|
6320
7369
|
return;
|
|
6321
7370
|
}
|
|
6322
7371
|
for (const f of result.data) {
|
|
@@ -6328,9 +7377,9 @@ var sessionsListCommand = defineCommand52({
|
|
|
6328
7377
|
});
|
|
6329
7378
|
|
|
6330
7379
|
// src/commands/sessions/remove.ts
|
|
6331
|
-
import { defineCommand as
|
|
6332
|
-
import
|
|
6333
|
-
var sessionsRemoveCommand =
|
|
7380
|
+
import { defineCommand as defineCommand56 } from "citty";
|
|
7381
|
+
import consola47 from "consola";
|
|
7382
|
+
var sessionsRemoveCommand = defineCommand56({
|
|
6334
7383
|
meta: {
|
|
6335
7384
|
name: "remove",
|
|
6336
7385
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6346,12 +7395,12 @@ var sessionsRemoveCommand = defineCommand53({
|
|
|
6346
7395
|
const id = String(args.familyId).trim();
|
|
6347
7396
|
if (!id) throw new CliError("familyId required");
|
|
6348
7397
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6349
|
-
|
|
7398
|
+
consola47.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6350
7399
|
}
|
|
6351
7400
|
});
|
|
6352
7401
|
|
|
6353
7402
|
// src/commands/sessions/index.ts
|
|
6354
|
-
var sessionsCommand =
|
|
7403
|
+
var sessionsCommand = defineCommand57({
|
|
6355
7404
|
meta: {
|
|
6356
7405
|
name: "sessions",
|
|
6357
7406
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6363,10 +7412,10 @@ var sessionsCommand = defineCommand54({
|
|
|
6363
7412
|
});
|
|
6364
7413
|
|
|
6365
7414
|
// src/commands/dns-check.ts
|
|
6366
|
-
import { defineCommand as
|
|
6367
|
-
import
|
|
7415
|
+
import { defineCommand as defineCommand58 } from "citty";
|
|
7416
|
+
import consola48 from "consola";
|
|
6368
7417
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6369
|
-
var dnsCheckCommand =
|
|
7418
|
+
var dnsCheckCommand = defineCommand58({
|
|
6370
7419
|
meta: {
|
|
6371
7420
|
name: "dns-check",
|
|
6372
7421
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6380,7 +7429,7 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6380
7429
|
},
|
|
6381
7430
|
async run({ args }) {
|
|
6382
7431
|
const domain = args.domain;
|
|
6383
|
-
|
|
7432
|
+
consola48.start(`Checking _ddisa.${domain}...`);
|
|
6384
7433
|
try {
|
|
6385
7434
|
const result = await resolveDDISA3(domain);
|
|
6386
7435
|
if (!result) {
|
|
@@ -6389,7 +7438,7 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6389
7438
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
6390
7439
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6391
7440
|
}
|
|
6392
|
-
|
|
7441
|
+
consola48.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
6393
7442
|
console.log("");
|
|
6394
7443
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
6395
7444
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -6398,14 +7447,14 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6398
7447
|
if (result.priority !== void 0)
|
|
6399
7448
|
console.log(` Priority: ${result.priority}`);
|
|
6400
7449
|
console.log("");
|
|
6401
|
-
|
|
7450
|
+
consola48.start(`Verifying IdP at ${result.idp}...`);
|
|
6402
7451
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
6403
7452
|
if (!discoResp.ok) {
|
|
6404
|
-
|
|
7453
|
+
consola48.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
6405
7454
|
return;
|
|
6406
7455
|
}
|
|
6407
7456
|
const disco = await discoResp.json();
|
|
6408
|
-
|
|
7457
|
+
consola48.success(`IdP is reachable`);
|
|
6409
7458
|
console.log(` Issuer: ${disco.issuer}`);
|
|
6410
7459
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
6411
7460
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -6423,7 +7472,7 @@ var dnsCheckCommand = defineCommand55({
|
|
|
6423
7472
|
// src/commands/health.ts
|
|
6424
7473
|
import { exec } from "child_process";
|
|
6425
7474
|
import { promisify } from "util";
|
|
6426
|
-
import { defineCommand as
|
|
7475
|
+
import { defineCommand as defineCommand59 } from "citty";
|
|
6427
7476
|
var execAsync = promisify(exec);
|
|
6428
7477
|
async function resolveApeShellPath() {
|
|
6429
7478
|
try {
|
|
@@ -6459,7 +7508,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6459
7508
|
}
|
|
6460
7509
|
}
|
|
6461
7510
|
async function runHealth(args) {
|
|
6462
|
-
const version = true ? "1.
|
|
7511
|
+
const version = true ? "1.8.0" : "0.0.0";
|
|
6463
7512
|
const auth = loadAuth();
|
|
6464
7513
|
if (!auth) {
|
|
6465
7514
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6522,7 +7571,7 @@ async function runHealth(args) {
|
|
|
6522
7571
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
6523
7572
|
}
|
|
6524
7573
|
}
|
|
6525
|
-
var healthCommand =
|
|
7574
|
+
var healthCommand = defineCommand59({
|
|
6526
7575
|
meta: {
|
|
6527
7576
|
name: "health",
|
|
6528
7577
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -6540,8 +7589,8 @@ var healthCommand = defineCommand56({
|
|
|
6540
7589
|
});
|
|
6541
7590
|
|
|
6542
7591
|
// src/commands/workflows.ts
|
|
6543
|
-
import { defineCommand as
|
|
6544
|
-
import
|
|
7592
|
+
import { defineCommand as defineCommand60 } from "citty";
|
|
7593
|
+
import consola49 from "consola";
|
|
6545
7594
|
|
|
6546
7595
|
// src/guides/index.ts
|
|
6547
7596
|
var guides = [
|
|
@@ -6591,7 +7640,7 @@ var guides = [
|
|
|
6591
7640
|
];
|
|
6592
7641
|
|
|
6593
7642
|
// src/commands/workflows.ts
|
|
6594
|
-
var workflowsCommand =
|
|
7643
|
+
var workflowsCommand = defineCommand60({
|
|
6595
7644
|
meta: {
|
|
6596
7645
|
name: "workflows",
|
|
6597
7646
|
description: "Discover workflow guides"
|
|
@@ -6612,7 +7661,7 @@ var workflowsCommand = defineCommand57({
|
|
|
6612
7661
|
if (args.id) {
|
|
6613
7662
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6614
7663
|
if (!guide) {
|
|
6615
|
-
|
|
7664
|
+
consola49.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6616
7665
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6617
7666
|
}
|
|
6618
7667
|
if (args.json) {
|
|
@@ -6655,7 +7704,7 @@ var workflowsCommand = defineCommand57({
|
|
|
6655
7704
|
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
6656
7705
|
import { homedir as homedir13 } from "os";
|
|
6657
7706
|
import { join as join14 } from "path";
|
|
6658
|
-
import
|
|
7707
|
+
import consola50 from "consola";
|
|
6659
7708
|
var PACKAGE_NAME = "@openape/apes";
|
|
6660
7709
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6661
7710
|
var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
|
|
@@ -6700,7 +7749,7 @@ async function fetchLatestVersion() {
|
|
|
6700
7749
|
}
|
|
6701
7750
|
function warnIfBehind(currentVersion, latest) {
|
|
6702
7751
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6703
|
-
|
|
7752
|
+
consola50.warn(
|
|
6704
7753
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
6705
7754
|
);
|
|
6706
7755
|
}
|
|
@@ -6732,10 +7781,10 @@ if (shellRewrite) {
|
|
|
6732
7781
|
if (shellRewrite.action === "rewrite") {
|
|
6733
7782
|
process.argv = shellRewrite.argv;
|
|
6734
7783
|
} else if (shellRewrite.action === "version") {
|
|
6735
|
-
console.log(`ape-shell ${"1.
|
|
7784
|
+
console.log(`ape-shell ${"1.8.0"} (OpenApe DDISA shell wrapper)`);
|
|
6736
7785
|
process.exit(0);
|
|
6737
7786
|
} else if (shellRewrite.action === "help") {
|
|
6738
|
-
console.log(`ape-shell ${"1.
|
|
7787
|
+
console.log(`ape-shell ${"1.8.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6739
7788
|
console.log("");
|
|
6740
7789
|
console.log("Usage:");
|
|
6741
7790
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6750,7 +7799,7 @@ if (shellRewrite) {
|
|
|
6750
7799
|
console.log(" --help, -h Show this help message");
|
|
6751
7800
|
process.exit(0);
|
|
6752
7801
|
} else if (shellRewrite.action === "interactive") {
|
|
6753
|
-
const { runInteractiveShell } = await import("./orchestrator-
|
|
7802
|
+
const { runInteractiveShell } = await import("./orchestrator-2QS5KXFL.js");
|
|
6754
7803
|
await runInteractiveShell();
|
|
6755
7804
|
process.exit(0);
|
|
6756
7805
|
} else {
|
|
@@ -6759,7 +7808,7 @@ if (shellRewrite) {
|
|
|
6759
7808
|
}
|
|
6760
7809
|
}
|
|
6761
7810
|
var debug = process.argv.includes("--debug");
|
|
6762
|
-
var grantsCommand =
|
|
7811
|
+
var grantsCommand = defineCommand61({
|
|
6763
7812
|
meta: {
|
|
6764
7813
|
name: "grants",
|
|
6765
7814
|
description: "Grant management"
|
|
@@ -6780,7 +7829,7 @@ var grantsCommand = defineCommand58({
|
|
|
6780
7829
|
"delegation-revoke": delegationRevokeCommand
|
|
6781
7830
|
}
|
|
6782
7831
|
});
|
|
6783
|
-
var configCommand =
|
|
7832
|
+
var configCommand = defineCommand61({
|
|
6784
7833
|
meta: {
|
|
6785
7834
|
name: "config",
|
|
6786
7835
|
description: "Configuration management"
|
|
@@ -6790,10 +7839,10 @@ var configCommand = defineCommand58({
|
|
|
6790
7839
|
set: configSetCommand
|
|
6791
7840
|
}
|
|
6792
7841
|
});
|
|
6793
|
-
var main =
|
|
7842
|
+
var main = defineCommand61({
|
|
6794
7843
|
meta: {
|
|
6795
7844
|
name: "apes",
|
|
6796
|
-
version: "1.
|
|
7845
|
+
version: "1.8.0",
|
|
6797
7846
|
description: "Unified CLI for OpenApe"
|
|
6798
7847
|
},
|
|
6799
7848
|
subCommands: {
|
|
@@ -6841,29 +7890,29 @@ var NO_REFRESH_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
6841
7890
|
async function maybeRefreshAuth() {
|
|
6842
7891
|
const sub = process.argv[2];
|
|
6843
7892
|
if (!sub || NO_REFRESH_COMMANDS.has(sub)) return;
|
|
6844
|
-
const { loadAuth: loadAuth2 } = await import("./config-
|
|
7893
|
+
const { loadAuth: loadAuth2 } = await import("./config-DA2L3XOV.js");
|
|
6845
7894
|
if (!loadAuth2()) return;
|
|
6846
7895
|
try {
|
|
6847
|
-
const { ensureFreshToken } = await import("./http-
|
|
7896
|
+
const { ensureFreshToken } = await import("./http-JWS5LV2U.js");
|
|
6848
7897
|
await ensureFreshToken();
|
|
6849
7898
|
} catch {
|
|
6850
7899
|
}
|
|
6851
7900
|
}
|
|
6852
7901
|
await maybeRefreshAuth();
|
|
6853
|
-
await maybeWarnStaleVersion("1.
|
|
7902
|
+
await maybeWarnStaleVersion("1.8.0").catch(() => {
|
|
6854
7903
|
});
|
|
6855
7904
|
runMain(main).catch((err) => {
|
|
6856
7905
|
if (err instanceof CliExit) {
|
|
6857
7906
|
process.exit(err.exitCode);
|
|
6858
7907
|
}
|
|
6859
7908
|
if (err instanceof CliError) {
|
|
6860
|
-
|
|
7909
|
+
consola51.error(err.message);
|
|
6861
7910
|
process.exit(err.exitCode);
|
|
6862
7911
|
}
|
|
6863
7912
|
if (debug) {
|
|
6864
|
-
|
|
7913
|
+
consola51.error(err);
|
|
6865
7914
|
} else {
|
|
6866
|
-
|
|
7915
|
+
consola51.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6867
7916
|
}
|
|
6868
7917
|
process.exit(1);
|
|
6869
7918
|
});
|