@openape/apes 1.7.1 → 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/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
- checkSudoRejection,
13
- isApesSelfDispatch,
14
- notifyGrantPending
15
- } from "./chunk-3COOEDPF.js";
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-N3THIFIS.js";
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 consola47 from "consola";
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 defineCommand58, runMain } from "citty";
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-YBNNG5K5.js");
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((r) => setTimeout(r, interval));
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 r of results) {
1112
- if (r.success) {
1113
- consola11.success(`Grant ${r.id} revoked.`);
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 ${r.id}: ${r.error?.title || "Failed"}`);
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 n = Number(envValue);
1137
- if (Number.isFinite(n) && n > 0)
1138
- return Math.floor(n);
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 n = Number(cfgValue);
1144
- if (Number.isFinite(n) && n > 0)
1145
- return Math.floor(n);
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 n = Number(envValue);
1153
- if (Number.isFinite(n) && n > 0)
1154
- return Math.floor(n);
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 n = Number(cfgValue);
1160
- if (Number.isFinite(n) && n > 0)
1161
- return Math.floor(n);
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((r) => setTimeout(r, intervalMs));
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);
@@ -2423,16 +3211,16 @@ var listAgentsCommand = defineCommand22({
2423
3211
  consola20.info(args["include-inactive"] ? "No agents found." : "No active agents found. Use --include-inactive to show deactivated.");
2424
3212
  return;
2425
3213
  }
2426
- const nameW = Math.max(4, ...rows.map((r) => r.name.length));
2427
- const emailW = Math.max(5, ...rows.map((r) => r.email.length));
3214
+ const nameW = Math.max(4, ...rows.map((r3) => r3.name.length));
3215
+ const emailW = Math.max(5, ...rows.map((r3) => r3.email.length));
2428
3216
  const header = `${"NAME".padEnd(nameW)} ${"EMAIL".padEnd(emailW)} ACTIVE OS-USER HOME`;
2429
3217
  console.log(header);
2430
3218
  console.log("-".repeat(header.length));
2431
- for (const r of rows) {
2432
- const active = r.isActive ? "\u2713" : "\u2717";
2433
- const os = r.osUser ? "\u2713" : "\u2717";
2434
- const homeCol = r.home ?? (isDarwin() ? "(missing)" : "(non-darwin)");
2435
- console.log(`${r.name.padEnd(nameW)} ${r.email.padEnd(emailW)} ${active.padEnd(6)} ${os.padEnd(7)} ${homeCol}`);
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}`);
2436
3224
  }
2437
3225
  }
2438
3226
  });
@@ -2559,12 +3347,12 @@ var fileTools = [
2559
3347
  },
2560
3348
  execute: async (args) => {
2561
3349
  const a = args;
2562
- const p = jailPath(a.path);
2563
- const content = readFileSync4(p, "utf8");
3350
+ const p2 = jailPath(a.path);
3351
+ const content = readFileSync4(p2, "utf8");
2564
3352
  if (Buffer.byteLength(content, "utf8") > MAX_BYTES) {
2565
- return { path: p, truncated: true, content: content.slice(0, MAX_BYTES) };
3353
+ return { path: p2, truncated: true, content: content.slice(0, MAX_BYTES) };
2566
3354
  }
2567
- return { path: p, truncated: false, content };
3355
+ return { path: p2, truncated: false, content };
2568
3356
  }
2569
3357
  },
2570
3358
  {
@@ -2584,10 +3372,10 @@ var fileTools = [
2584
3372
  if (Buffer.byteLength(a.content, "utf8") > MAX_BYTES) {
2585
3373
  throw new Error(`content exceeds ${MAX_BYTES} byte cap`);
2586
3374
  }
2587
- const p = jailPath(a.path);
2588
- mkdirSync(dirname(p), { recursive: true });
2589
- writeFileSync2(p, a.content, { encoding: "utf8" });
2590
- return { path: p, bytes: Buffer.byteLength(a.content, "utf8") };
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") };
2591
3379
  }
2592
3380
  }
2593
3381
  ];
@@ -3076,7 +3864,7 @@ async function postRunResultToChat(opts) {
3076
3864
  if (!contactsRes.ok) return;
3077
3865
  const contacts = await contactsRes.json();
3078
3866
  const ownerLower = opts.ownerEmail.toLowerCase();
3079
- const ownerRow = contacts.find((c) => c.peerEmail.toLowerCase() === ownerLower && c.connected && c.roomId);
3867
+ const ownerRow = contacts.find((c2) => c2.peerEmail.toLowerCase() === ownerLower && c2.connected && c2.roomId);
3080
3868
  if (!ownerRow?.roomId) {
3081
3869
  consola22.info("chat DM skipped \u2014 no active room with owner (accept the contact request in chat to enable)");
3082
3870
  return;
@@ -3406,8 +4194,8 @@ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as rea
3406
4194
  import { generateKeyPairSync } from "crypto";
3407
4195
  import { homedir as homedir7 } from "os";
3408
4196
  import { dirname as dirname2, resolve as resolve3 } from "path";
3409
- function resolveKeyPath(p) {
3410
- return resolve3(p.replace(/^~/, homedir7()));
4197
+ function resolveKeyPath(p2) {
4198
+ return resolve3(p2.replace(/^~/, homedir7()));
3411
4199
  }
3412
4200
  function buildSshEd25519Line(rawPub) {
3413
4201
  const keyTypeStr = "ssh-ed25519";
@@ -3684,7 +4472,15 @@ and try again.`
3684
4472
  email: registration.email,
3685
4473
  expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
3686
4474
  keyPath: `${homeDir}/.ssh/id_ed25519`,
3687
- ownerEmail: auth.email
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
3688
4484
  });
3689
4485
  const includeClaudeHook = !args["no-claude-hook"];
3690
4486
  const claudeOauthToken = await resolveClaudeToken({
@@ -3924,7 +4720,7 @@ var agentsCommand = defineCommand28({
3924
4720
  });
3925
4721
 
3926
4722
  // src/commands/nest/index.ts
3927
- import { defineCommand as defineCommand34 } from "citty";
4723
+ import { defineCommand as defineCommand37 } from "citty";
3928
4724
 
3929
4725
  // src/commands/nest/authorize.ts
3930
4726
  import { execFileSync as execFileSync9 } from "child_process";
@@ -4016,7 +4812,19 @@ var enrollNestCommand = defineCommand29({
4016
4812
 
4017
4813
  // src/commands/nest/authorize.ts
4018
4814
  var DEFAULT_ALLOW_PATTERNS = [
4019
- // Outer spawn-grant what the nest's HTTP handler invokes.
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`.
4020
4828
  "apes agents spawn *",
4021
4829
  "apes agents destroy *",
4022
4830
  "apes agents sync",
@@ -4027,11 +4835,12 @@ var DEFAULT_ALLOW_PATTERNS = [
4027
4835
  // glob below limits the auto-approval to that exact lifecycle path
4028
4836
  // — `bash *` would be unsafe.
4029
4837
  "bash *apes-spawn-*setup.sh",
4030
- // Bridge invocation the supervisor uses to keep agent processes
4031
- // running. Pattern is intentionally precisenot a generic
4032
- // `apes run --as *` wildcard so a compromised nest can't pivot
4033
- // to running arbitrary commands as arbitrary users.
4034
- "apes run --as * -- openape-chat-bridge"
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"
4035
4844
  ];
4036
4845
  var authorizeNestCommand = defineCommand30({
4037
4846
  meta: {
@@ -4079,13 +4888,148 @@ var authorizeNestCommand = defineCommand30({
4079
4888
  }
4080
4889
  });
4081
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
+
4082
5026
  // src/commands/nest/install.ts
4083
5027
  import { execFileSync as execFileSync10 } from "child_process";
4084
5028
  import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
4085
5029
  import { homedir as homedir11, userInfo as userInfo2 } from "os";
4086
5030
  import { dirname as dirname3, join as join10 } from "path";
4087
- import { defineCommand as defineCommand31 } from "citty";
4088
- import consola27 from "consola";
5031
+ import { defineCommand as defineCommand32 } from "citty";
5032
+ import consola29 from "consola";
4089
5033
 
4090
5034
  // src/commands/nest/apes-agents-adapter.ts
4091
5035
  var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
@@ -4202,7 +5146,7 @@ function installAdapter2() {
4202
5146
  }
4203
5147
  if (existing === APES_AGENTS_ADAPTER_TOML) return false;
4204
5148
  writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
4205
- consola27.success(`Wrote shapes adapter ${target}`);
5149
+ consola29.success(`Wrote shapes adapter ${target}`);
4206
5150
  return true;
4207
5151
  }
4208
5152
  function findBinary(name) {
@@ -4212,12 +5156,12 @@ function findBinary(name) {
4212
5156
  "/usr/local/bin",
4213
5157
  "/usr/bin"
4214
5158
  ]) {
4215
- const p = join10(dir, name);
4216
- if (existsSync12(p)) return p;
5159
+ const p2 = join10(dir, name);
5160
+ if (existsSync12(p2)) return p2;
4217
5161
  }
4218
5162
  throw new Error(`could not locate ${name} on PATH; install it first`);
4219
5163
  }
4220
- var installNestCommand = defineCommand31({
5164
+ var installNestCommand = defineCommand32({
4221
5165
  meta: {
4222
5166
  name: "install",
4223
5167
  description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
@@ -4236,10 +5180,10 @@ var installNestCommand = defineCommand31({
4236
5180
  }
4237
5181
  const nestBin = findBinary("openape-nest");
4238
5182
  const apesBin = findBinary("apes");
4239
- consola27.info(`Installing nest at ${plistPath()}`);
4240
- consola27.info(` nest binary: ${nestBin}`);
4241
- consola27.info(` apes binary: ${apesBin}`);
4242
- consola27.info(` HTTP port: ${port}`);
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}`);
4243
5187
  installAdapter2();
4244
5188
  mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
4245
5189
  mkdirSync5(NEST_DATA_DIR, { recursive: true });
@@ -4251,9 +5195,9 @@ var installNestCommand = defineCommand31({
4251
5195
  }
4252
5196
  if (existing !== desired) {
4253
5197
  writeFileSync7(plistPath(), desired, { mode: 420 });
4254
- consola27.success("Wrote launchd plist");
5198
+ consola29.success("Wrote launchd plist");
4255
5199
  } else {
4256
- consola27.info("plist already up to date");
5200
+ consola29.info("plist already up to date");
4257
5201
  }
4258
5202
  const uid = userInfo2().uid;
4259
5203
  try {
@@ -4261,45 +5205,156 @@ var installNestCommand = defineCommand31({
4261
5205
  } catch {
4262
5206
  }
4263
5207
  execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
4264
- consola27.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
4265
- consola27.info("");
4266
- consola27.info("Next steps for zero-prompt spawn \u2014 both one-time:");
4267
- consola27.info("");
4268
- consola27.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
4269
- consola27.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
4270
- consola27.info("");
4271
- consola27.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
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})`);
4272
5323
  }
4273
5324
  });
4274
5325
 
4275
5326
  // src/commands/nest/status.ts
4276
- import process2 from "process";
4277
- import { defineCommand as defineCommand32 } from "citty";
4278
- import consola28 from "consola";
4279
- var DEFAULT_PORT = 9091;
4280
- var statusNestCommand = defineCommand32({
5327
+ import process5 from "process";
5328
+ import { defineCommand as defineCommand35 } from "citty";
5329
+ import consola32 from "consola";
5330
+ var statusNestCommand = defineCommand35({
4281
5331
  meta: {
4282
5332
  name: "status",
4283
- description: "Print state of the local nest-daemon (agents registered, processes supervised)"
5333
+ description: "Print health of the local nest-daemon (agents registered). Goes through DDISA grants."
4284
5334
  },
4285
5335
  args: {
4286
5336
  port: { type: "string", description: "Override nest port (default: 9091)" },
4287
5337
  json: { type: "boolean", description: "JSON output for scripts" }
4288
5338
  },
4289
5339
  async run({ args }) {
4290
- const port = Number(args.port ?? process2.env.OPENAPE_NEST_PORT ?? DEFAULT_PORT);
4291
- const url = `http://127.0.0.1:${port}/status`;
5340
+ const token = await requestNestGrant({ command: ["nest", "status"] });
5341
+ const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
4292
5342
  let status;
4293
5343
  try {
4294
- const res = await fetch(url);
4295
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
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
+ }
4296
5351
  status = await res.json();
4297
5352
  } catch (err) {
4298
5353
  const msg = err instanceof Error ? err.message : String(err);
4299
5354
  if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
4300
- consola28.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
4301
- consola28.info(" Run: apes nest install");
4302
- process2.exit(2);
5355
+ consola32.error(`Nest daemon is not running at ${base}`);
5356
+ consola32.info(" Run: apes nest install");
5357
+ process5.exit(2);
4303
5358
  }
4304
5359
  throw err;
4305
5360
  }
@@ -4307,34 +5362,19 @@ var statusNestCommand = defineCommand32({
4307
5362
  console.log(JSON.stringify(status, null, 2));
4308
5363
  return;
4309
5364
  }
4310
- consola28.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
4311
- if (status.processes.length === 0) {
4312
- consola28.info(" (no processes running)");
4313
- return;
4314
- }
4315
- for (const p of status.processes) {
4316
- const uptime = humanDuration(p.uptimeSec);
4317
- const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
4318
- consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
4319
- }
5365
+ consola32.info(`Nest at ${base} \u2014 ${status.agents} agent(s) registered`);
4320
5366
  }
4321
5367
  });
4322
- function humanDuration(sec) {
4323
- if (sec < 60) return `${sec}s`;
4324
- if (sec < 3600) return `${Math.floor(sec / 60)}m`;
4325
- if (sec < 86400) return `${Math.floor(sec / 3600)}h`;
4326
- return `${Math.floor(sec / 86400)}d`;
4327
- }
4328
5368
 
4329
5369
  // src/commands/nest/uninstall.ts
4330
5370
  import { execFileSync as execFileSync11 } from "child_process";
4331
5371
  import { existsSync as existsSync13, unlinkSync } from "fs";
4332
5372
  import { homedir as homedir12, userInfo as userInfo3 } from "os";
4333
5373
  import { join as join11 } from "path";
4334
- import { defineCommand as defineCommand33 } from "citty";
4335
- import consola29 from "consola";
5374
+ import { defineCommand as defineCommand36 } from "citty";
5375
+ import consola33 from "consola";
4336
5376
  var PLIST_LABEL2 = "ai.openape.nest";
4337
- var uninstallNestCommand = defineCommand33({
5377
+ var uninstallNestCommand = defineCommand36({
4338
5378
  meta: {
4339
5379
  name: "uninstall",
4340
5380
  description: "Stop + remove the local nest-daemon (registry + agents preserved)"
@@ -4344,40 +5384,43 @@ var uninstallNestCommand = defineCommand33({
4344
5384
  const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
4345
5385
  try {
4346
5386
  execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
4347
- consola29.success("Nest daemon stopped");
5387
+ consola33.success("Nest daemon stopped");
4348
5388
  } catch {
4349
- consola29.info("Nest daemon was not loaded");
5389
+ consola33.info("Nest daemon was not loaded");
4350
5390
  }
4351
5391
  if (existsSync13(path2)) {
4352
5392
  unlinkSync(path2);
4353
- consola29.success(`Removed ${path2}`);
5393
+ consola33.success(`Removed ${path2}`);
4354
5394
  }
4355
- consola29.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
5395
+ consola33.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
4356
5396
  }
4357
5397
  });
4358
5398
 
4359
5399
  // src/commands/nest/index.ts
4360
- var nestCommand = defineCommand34({
5400
+ var nestCommand = defineCommand37({
4361
5401
  meta: {
4362
5402
  name: "nest",
4363
- description: "Manage the local Nest control-plane daemon (install / enroll / authorize / status / uninstall). One-time setup: `install` (launchd) \u2192 `enroll` (own DDISA identity) \u2192 `authorize` (YOLO-policy). After that, `apes agents spawn` runs without per-call DDISA approvals."
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."
4364
5404
  },
4365
5405
  subCommands: {
4366
5406
  install: installNestCommand,
4367
5407
  enroll: enrollNestCommand,
4368
5408
  authorize: authorizeNestCommand,
4369
5409
  status: statusNestCommand,
5410
+ list: listNestCommand,
5411
+ spawn: spawnNestCommand,
5412
+ destroy: destroyNestCommand,
4370
5413
  uninstall: uninstallNestCommand
4371
5414
  }
4372
5415
  });
4373
5416
 
4374
5417
  // src/commands/yolo/index.ts
4375
- import { defineCommand as defineCommand38 } from "citty";
5418
+ import { defineCommand as defineCommand41 } from "citty";
4376
5419
 
4377
5420
  // src/commands/yolo/clear.ts
4378
- import { defineCommand as defineCommand35 } from "citty";
4379
- import consola30 from "consola";
4380
- var yoloClearCommand = defineCommand35({
5421
+ import { defineCommand as defineCommand38 } from "citty";
5422
+ import consola34 from "consola";
5423
+ var yoloClearCommand = defineCommand38({
4381
5424
  meta: {
4382
5425
  name: "clear",
4383
5426
  description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
@@ -4406,15 +5449,15 @@ var yoloClearCommand = defineCommand35({
4406
5449
  const text = await res.text().catch(() => "");
4407
5450
  throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
4408
5451
  }
4409
- consola30.success(`YOLO-policy cleared on ${email}`);
5452
+ consola34.success(`YOLO-policy cleared on ${email}`);
4410
5453
  }
4411
5454
  });
4412
5455
 
4413
5456
  // src/commands/yolo/set.ts
4414
- import { defineCommand as defineCommand36 } from "citty";
4415
- import consola31 from "consola";
5457
+ import { defineCommand as defineCommand39 } from "citty";
5458
+ import consola35 from "consola";
4416
5459
  var VALID_MODES = ["allow-list", "deny-list"];
4417
- var yoloSetCommand = defineCommand36({
5460
+ var yoloSetCommand = defineCommand39({
4418
5461
  meta: {
4419
5462
  name: "set",
4420
5463
  description: "Write a YOLO-policy on a DDISA agent you own"
@@ -4462,12 +5505,12 @@ var yoloSetCommand = defineCommand36({
4462
5505
  const denyPatterns = parseList(args.deny);
4463
5506
  const denyRiskThreshold = args["deny-risk"] ?? null;
4464
5507
  const expiresAt = parseExpiresIn(args["expires-in"]);
4465
- consola31.info(`Setting YOLO-policy on ${email}`);
4466
- consola31.info(` mode: ${mode}`);
4467
- if (allowPatterns.length) consola31.info(` allow_patterns: ${allowPatterns.join(", ")}`);
4468
- if (denyPatterns.length) consola31.info(` deny_patterns: ${denyPatterns.join(", ")}`);
4469
- if (denyRiskThreshold) consola31.info(` deny_risk: ${denyRiskThreshold}`);
4470
- if (expiresAt) consola31.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
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()}`);
4471
5514
  const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
4472
5515
  const res = await fetch(url, {
4473
5516
  method: "PUT",
@@ -4487,27 +5530,27 @@ var yoloSetCommand = defineCommand36({
4487
5530
  const text = await res.text().catch(() => "");
4488
5531
  throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
4489
5532
  }
4490
- consola31.success(`YOLO-policy applied to ${email}`);
5533
+ consola35.success(`YOLO-policy applied to ${email}`);
4491
5534
  }
4492
5535
  });
4493
5536
  function parseList(s) {
4494
5537
  if (!s) return [];
4495
- return s.split(",").map((p) => p.trim()).filter(Boolean);
5538
+ return s.split(",").map((p2) => p2.trim()).filter(Boolean);
4496
5539
  }
4497
5540
  function parseExpiresIn(s) {
4498
5541
  if (!s) return null;
4499
5542
  const m = s.match(/^(\d+)([hdw])$/);
4500
5543
  if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
4501
- const n = Number(m[1]);
5544
+ const n2 = Number(m[1]);
4502
5545
  const unit = m[2];
4503
5546
  const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
4504
- return Math.floor(Date.now() / 1e3) + n * seconds;
5547
+ return Math.floor(Date.now() / 1e3) + n2 * seconds;
4505
5548
  }
4506
5549
 
4507
5550
  // src/commands/yolo/show.ts
4508
- import { defineCommand as defineCommand37 } from "citty";
4509
- import consola32 from "consola";
4510
- var yoloShowCommand = defineCommand37({
5551
+ import { defineCommand as defineCommand40 } from "citty";
5552
+ import consola36 from "consola";
5553
+ var yoloShowCommand = defineCommand40({
4511
5554
  meta: {
4512
5555
  name: "show",
4513
5556
  description: "Print the YOLO-policy currently set on a DDISA agent"
@@ -4544,17 +5587,17 @@ var yoloShowCommand = defineCommand37({
4544
5587
  console.log(JSON.stringify(policy, null, 2));
4545
5588
  return;
4546
5589
  }
4547
- consola32.info(`YOLO-policy for ${email}`);
4548
- consola32.info(` mode: ${policy.mode}`);
4549
- consola32.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
4550
- consola32.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
4551
- consola32.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
4552
- consola32.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
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)"}`);
4553
5596
  }
4554
5597
  });
4555
5598
 
4556
5599
  // src/commands/yolo/index.ts
4557
- var yoloCommand = defineCommand38({
5600
+ var yoloCommand = defineCommand41({
4558
5601
  meta: {
4559
5602
  name: "yolo",
4560
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)."
@@ -4567,15 +5610,15 @@ var yoloCommand = defineCommand38({
4567
5610
  });
4568
5611
 
4569
5612
  // src/commands/adapter/index.ts
4570
- import { defineCommand as defineCommand39 } from "citty";
4571
- import consola33 from "consola";
4572
- var adapterCommand = defineCommand39({
5613
+ import { defineCommand as defineCommand42 } from "citty";
5614
+ import consola37 from "consola";
5615
+ var adapterCommand = defineCommand42({
4573
5616
  meta: {
4574
5617
  name: "adapter",
4575
5618
  description: "Manage CLI adapters"
4576
5619
  },
4577
5620
  subCommands: {
4578
- list: defineCommand39({
5621
+ list: defineCommand42({
4579
5622
  meta: {
4580
5623
  name: "list",
4581
5624
  description: "List available adapters"
@@ -4606,7 +5649,7 @@ var adapterCommand = defineCommand39({
4606
5649
  `);
4607
5650
  return;
4608
5651
  }
4609
- consola33.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
5652
+ consola37.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
4610
5653
  for (const a of index2.adapters) {
4611
5654
  const installed = isInstalled(a.id, false) ? " [installed]" : "";
4612
5655
  console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
@@ -4628,7 +5671,7 @@ var adapterCommand = defineCommand39({
4628
5671
  return;
4629
5672
  }
4630
5673
  if (local.length === 0) {
4631
- consola33.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
5674
+ consola37.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
4632
5675
  return;
4633
5676
  }
4634
5677
  for (const a of local) {
@@ -4636,7 +5679,7 @@ var adapterCommand = defineCommand39({
4636
5679
  }
4637
5680
  }
4638
5681
  }),
4639
- install: defineCommand39({
5682
+ install: defineCommand42({
4640
5683
  meta: {
4641
5684
  name: "install",
4642
5685
  description: "Install an adapter from the registry"
@@ -4665,24 +5708,24 @@ var adapterCommand = defineCommand39({
4665
5708
  for (const id of ids) {
4666
5709
  const entry = findAdapter(index, id);
4667
5710
  if (!entry) {
4668
- consola33.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
5711
+ consola37.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
4669
5712
  continue;
4670
5713
  }
4671
5714
  const conflicts = findConflictingAdapters(entry.executable, id);
4672
5715
  if (conflicts.length > 0) {
4673
- for (const c of conflicts) {
4674
- consola33.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
4675
- consola33.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
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}`);
4676
5719
  }
4677
5720
  }
4678
5721
  const result = await installAdapter(entry, { local });
4679
5722
  const verb = result.updated ? "Updated" : "Installed";
4680
- consola33.success(`${verb} ${result.id} \u2192 ${result.path}`);
4681
- consola33.info(`Digest: ${result.digest}`);
5723
+ consola37.success(`${verb} ${result.id} \u2192 ${result.path}`);
5724
+ consola37.info(`Digest: ${result.digest}`);
4682
5725
  }
4683
5726
  }
4684
5727
  }),
4685
- remove: defineCommand39({
5728
+ remove: defineCommand42({
4686
5729
  meta: {
4687
5730
  name: "remove",
4688
5731
  description: "Remove an installed adapter"
@@ -4705,9 +5748,9 @@ var adapterCommand = defineCommand39({
4705
5748
  let failed = false;
4706
5749
  for (const id of ids) {
4707
5750
  if (removeAdapter(id, local)) {
4708
- consola33.success(`Removed adapter: ${id}`);
5751
+ consola37.success(`Removed adapter: ${id}`);
4709
5752
  } else {
4710
- consola33.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
5753
+ consola37.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4711
5754
  failed = true;
4712
5755
  }
4713
5756
  }
@@ -4715,7 +5758,7 @@ var adapterCommand = defineCommand39({
4715
5758
  throw new CliError("Some adapters could not be removed");
4716
5759
  }
4717
5760
  }),
4718
- info: defineCommand39({
5761
+ info: defineCommand42({
4719
5762
  meta: {
4720
5763
  name: "info",
4721
5764
  description: "Show detailed adapter information"
@@ -4757,7 +5800,7 @@ var adapterCommand = defineCommand39({
4757
5800
  }
4758
5801
  }
4759
5802
  }),
4760
- search: defineCommand39({
5803
+ search: defineCommand42({
4761
5804
  meta: {
4762
5805
  name: "search",
4763
5806
  description: "Search adapters in the registry"
@@ -4789,7 +5832,7 @@ var adapterCommand = defineCommand39({
4789
5832
  return;
4790
5833
  }
4791
5834
  if (results.length === 0) {
4792
- consola33.info(`No adapters matching "${query}"`);
5835
+ consola37.info(`No adapters matching "${query}"`);
4793
5836
  return;
4794
5837
  }
4795
5838
  for (const a of results) {
@@ -4798,7 +5841,7 @@ var adapterCommand = defineCommand39({
4798
5841
  }
4799
5842
  }
4800
5843
  }),
4801
- update: defineCommand39({
5844
+ update: defineCommand42({
4802
5845
  meta: {
4803
5846
  name: "update",
4804
5847
  description: "Update installed adapters"
@@ -4824,33 +5867,33 @@ var adapterCommand = defineCommand39({
4824
5867
  const targetId = args.id ? String(args.id) : void 0;
4825
5868
  const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
4826
5869
  if (targets.length === 0) {
4827
- consola33.info("No adapters installed to update.");
5870
+ consola37.info("No adapters installed to update.");
4828
5871
  return;
4829
5872
  }
4830
5873
  for (const id of targets) {
4831
5874
  const entry = findAdapter(index, id);
4832
5875
  if (!entry) {
4833
- consola33.warn(`${id}: not found in registry, skipping`);
5876
+ consola37.warn(`${id}: not found in registry, skipping`);
4834
5877
  continue;
4835
5878
  }
4836
5879
  const localDigest = getInstalledDigest(id, false);
4837
5880
  if (localDigest === entry.digest) {
4838
- consola33.info(`${id}: already up to date`);
5881
+ consola37.info(`${id}: already up to date`);
4839
5882
  continue;
4840
5883
  }
4841
5884
  if (localDigest && !args.yes) {
4842
- consola33.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
4843
- consola33.info(` Old: ${localDigest}`);
4844
- consola33.info(` New: ${entry.digest}`);
4845
- consola33.info(" Use --yes to confirm");
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");
4846
5889
  continue;
4847
5890
  }
4848
5891
  const result = await installAdapter(entry);
4849
- consola33.success(`Updated ${result.id} \u2192 ${result.path}`);
5892
+ consola37.success(`Updated ${result.id} \u2192 ${result.path}`);
4850
5893
  }
4851
5894
  }
4852
5895
  }),
4853
- verify: defineCommand39({
5896
+ verify: defineCommand42({
4854
5897
  meta: {
4855
5898
  name: "verify",
4856
5899
  description: "Verify installed adapter against registry digest"
@@ -4883,7 +5926,7 @@ var adapterCommand = defineCommand39({
4883
5926
  if (!localDigest)
4884
5927
  throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
4885
5928
  if (localDigest === entry.digest) {
4886
- consola33.success(`${id}: digest matches registry`);
5929
+ consola37.success(`${id}: digest matches registry`);
4887
5930
  } else {
4888
5931
  console.log(` Local: ${localDigest}`);
4889
5932
  console.log(` Registry: ${entry.digest}`);
@@ -4896,10 +5939,10 @@ var adapterCommand = defineCommand39({
4896
5939
 
4897
5940
  // src/commands/run.ts
4898
5941
  import { execFileSync as execFileSync12 } from "child_process";
4899
- import { hostname as hostname5 } from "os";
5942
+ import { hostname as hostname6 } from "os";
4900
5943
  import { basename } from "path";
4901
- import { defineCommand as defineCommand40 } from "citty";
4902
- import consola34 from "consola";
5944
+ import { defineCommand as defineCommand43 } from "citty";
5945
+ import consola38 from "consola";
4903
5946
  function shouldWaitForGrant(args) {
4904
5947
  return args.wait === true || process.env.APE_WAIT === "1";
4905
5948
  }
@@ -4917,16 +5960,16 @@ function getUserMode() {
4917
5960
  function getAsyncExitCode() {
4918
5961
  const envValue = process.env.APES_ASYNC_EXIT_CODE;
4919
5962
  if (envValue !== void 0 && envValue !== "") {
4920
- const n = Number(envValue);
4921
- if (Number.isFinite(n) && n >= 0 && n <= 255)
4922
- return Math.floor(n);
5963
+ const n2 = Number(envValue);
5964
+ if (Number.isFinite(n2) && n2 >= 0 && n2 <= 255)
5965
+ return Math.floor(n2);
4923
5966
  }
4924
5967
  const cfg = loadConfig();
4925
5968
  const cfgValue = cfg.defaults?.async_exit_code;
4926
5969
  if (cfgValue !== void 0 && cfgValue !== "") {
4927
- const n = Number(cfgValue);
4928
- if (Number.isFinite(n) && n >= 0 && n <= 255)
4929
- return Math.floor(n);
5970
+ const n2 = Number(cfgValue);
5971
+ if (Number.isFinite(n2) && n2 >= 0 && n2 <= 255)
5972
+ return Math.floor(n2);
4930
5973
  }
4931
5974
  return 75;
4932
5975
  }
@@ -4936,7 +5979,7 @@ function printPendingGrantInfo(grant, idp) {
4936
5979
  const statusCmd = `apes grants status ${grant.id}`;
4937
5980
  const executeCmd = `apes grants run ${grant.id}`;
4938
5981
  if (mode === "human") {
4939
- consola34.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
5982
+ consola38.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
4940
5983
  console.log(` Approve in browser: ${approveUrl}`);
4941
5984
  console.log(` Check status: ${statusCmd}`);
4942
5985
  console.log(` Run after approval: ${executeCmd}`);
@@ -4946,7 +5989,7 @@ function printPendingGrantInfo(grant, idp) {
4946
5989
  return;
4947
5990
  }
4948
5991
  const maxMin = getPollMaxMinutes();
4949
- consola34.success(`Grant ${grant.id} created (pending approval)`);
5992
+ consola38.success(`Grant ${grant.id} created (pending approval)`);
4950
5993
  console.log(` Approve: ${approveUrl}`);
4951
5994
  console.log(` Status: ${statusCmd} [--json]`);
4952
5995
  console.log(` Execute: ${executeCmd} --wait`);
@@ -4968,7 +6011,7 @@ function printPendingGrantInfo(grant, idp) {
4968
6011
  console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
4969
6012
  console.log(" grant be reused on subsequent invocations without re-approval.");
4970
6013
  }
4971
- var runCommand = defineCommand40({
6014
+ var runCommand = defineCommand43({
4972
6015
  meta: {
4973
6016
  name: "run",
4974
6017
  description: "Execute a grant-secured command"
@@ -5057,7 +6100,7 @@ async function runShellMode(command, args) {
5057
6100
  const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
5058
6101
  if (adapterHandled) return;
5059
6102
  const grantsUrl = await getGrantsEndpoint(idp);
5060
- const targetHost = args.host || hostname5();
6103
+ const targetHost = args.host || hostname6();
5061
6104
  try {
5062
6105
  const grants = await apiFetch(
5063
6106
  `${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
@@ -5071,7 +6114,7 @@ async function runShellMode(command, args) {
5071
6114
  }
5072
6115
  } catch {
5073
6116
  }
5074
- consola34.info(`Requesting ape-shell session grant on ${targetHost}`);
6117
+ consola38.info(`Requesting ape-shell session grant on ${targetHost}`);
5075
6118
  const grant = await apiFetch(grantsUrl, {
5076
6119
  method: "POST",
5077
6120
  body: {
@@ -5091,8 +6134,8 @@ async function runShellMode(command, args) {
5091
6134
  host: targetHost
5092
6135
  });
5093
6136
  if (shouldWaitForGrant(args)) {
5094
- consola34.info(`Grant requested: ${grant.id}`);
5095
- consola34.info("Waiting for approval...");
6137
+ consola38.info(`Grant requested: ${grant.id}`);
6138
+ consola38.info("Waiting for approval...");
5096
6139
  const maxWait = 3e5;
5097
6140
  const interval = 3e3;
5098
6141
  const start = Date.now();
@@ -5102,7 +6145,7 @@ async function runShellMode(command, args) {
5102
6145
  break;
5103
6146
  if (status.status === "denied" || status.status === "revoked")
5104
6147
  throw new CliError(`Grant ${status.status}.`);
5105
- await new Promise((r) => setTimeout(r, interval));
6148
+ await new Promise((r3) => setTimeout(r3, interval));
5106
6149
  }
5107
6150
  execShellCommand(command);
5108
6151
  return;
@@ -5123,13 +6166,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
5123
6166
  try {
5124
6167
  resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
5125
6168
  } catch (err) {
5126
- consola34.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
6169
+ consola38.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
5127
6170
  return false;
5128
6171
  }
5129
6172
  try {
5130
6173
  const existingGrantId = await findExistingGrant(resolved, idp);
5131
6174
  if (existingGrantId) {
5132
- consola34.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
6175
+ consola38.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
5133
6176
  const token = await fetchGrantToken(idp, existingGrantId);
5134
6177
  await verifyAndExecute(token, resolved, existingGrantId);
5135
6178
  return true;
@@ -5137,27 +6180,27 @@ async function tryAdapterModeFromShell(command, idp, args) {
5137
6180
  } catch {
5138
6181
  }
5139
6182
  const approval = args.approval ?? "once";
5140
- consola34.info(`Requesting grant for: ${resolved.detail.display}`);
6183
+ consola38.info(`Requesting grant for: ${resolved.detail.display}`);
5141
6184
  const grant = await createShapesGrant(resolved, {
5142
6185
  idp,
5143
6186
  approval,
5144
6187
  reason: args.reason || `ape-shell: ${resolved.detail.display}`
5145
6188
  });
5146
6189
  if (grant.similar_grants?.similar_grants?.length) {
5147
- const n = grant.similar_grants.similar_grants.length;
5148
- consola34.info("");
5149
- consola34.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
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.`);
5150
6193
  }
5151
6194
  notifyGrantPending({
5152
6195
  grantId: grant.id,
5153
6196
  approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
5154
6197
  command: resolved.detail?.display || parsed?.raw || "unknown",
5155
6198
  audience: resolved.adapter?.cli?.audience ?? "shapes",
5156
- host: args.host || hostname5()
6199
+ host: args.host || hostname6()
5157
6200
  });
5158
6201
  if (shouldWaitForGrant(args)) {
5159
- consola34.info(`Grant requested: ${grant.id}`);
5160
- consola34.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
6202
+ consola38.info(`Grant requested: ${grant.id}`);
6203
+ consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5161
6204
  const status = await waitForGrantStatus(idp, grant.id);
5162
6205
  if (status !== "approved")
5163
6206
  throw new CliError(`Grant ${status}`);
@@ -5231,7 +6274,7 @@ async function runAdapterMode(command, rawArgs, args) {
5231
6274
  try {
5232
6275
  const existingGrantId = await findExistingGrant(resolved, idp);
5233
6276
  if (existingGrantId) {
5234
- consola34.info(`Reusing existing grant: ${existingGrantId}`);
6277
+ consola38.info(`Reusing existing grant: ${existingGrantId}`);
5235
6278
  const token = await fetchGrantToken(idp, existingGrantId);
5236
6279
  await verifyAndExecute(token, resolved, existingGrantId);
5237
6280
  return;
@@ -5244,18 +6287,18 @@ async function runAdapterMode(command, rawArgs, args) {
5244
6287
  ...args.reason ? { reason: args.reason } : {}
5245
6288
  });
5246
6289
  if (grant.similar_grants?.similar_grants?.length) {
5247
- const n = grant.similar_grants.similar_grants.length;
5248
- consola34.info("");
5249
- consola34.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
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.`);
5250
6293
  if (grant.similar_grants.widened_details?.length) {
5251
6294
  const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
5252
- consola34.info(` Broader scope: ${wider}`);
6295
+ consola38.info(` Broader scope: ${wider}`);
5253
6296
  }
5254
- consola34.info("");
6297
+ consola38.info("");
5255
6298
  }
5256
6299
  if (shouldWaitForGrant(args)) {
5257
- consola34.info(`Grant requested: ${grant.id}`);
5258
- consola34.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
6300
+ consola38.info(`Grant requested: ${grant.id}`);
6301
+ consola38.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5259
6302
  const status = await waitForGrantStatus(idp, grant.id);
5260
6303
  if (status !== "approved")
5261
6304
  throw new Error(`Grant ${status}`);
@@ -5274,8 +6317,8 @@ async function runAudienceMode(audience, action, args) {
5274
6317
  const idp = getIdpUrl(args.idp);
5275
6318
  const grantsUrl = await getGrantsEndpoint(idp);
5276
6319
  const command = action.split(" ");
5277
- const targetHost = args.host || hostname5();
5278
- consola34.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
6320
+ const targetHost = args.host || hostname6();
6321
+ consola38.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
5279
6322
  const grant = await apiFetch(grantsUrl, {
5280
6323
  method: "POST",
5281
6324
  body: {
@@ -5292,9 +6335,9 @@ async function runAudienceMode(audience, action, args) {
5292
6335
  printPendingGrantInfo(grant, idp);
5293
6336
  throw new CliExit(getAsyncExitCode());
5294
6337
  }
5295
- consola34.success(`Grant requested: ${grant.id}`);
5296
- consola34.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
5297
- consola34.info("Waiting for approval...");
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...");
5298
6341
  const maxWait = 15 * 60 * 1e3;
5299
6342
  const interval = 3e3;
5300
6343
  const start = Date.now();
@@ -5302,14 +6345,14 @@ async function runAudienceMode(audience, action, args) {
5302
6345
  while (Date.now() - start < maxWait) {
5303
6346
  const status = await apiFetch(`${grantsUrl}/${grant.id}`);
5304
6347
  if (status.status === "approved") {
5305
- consola34.success("Grant approved!");
6348
+ consola38.success("Grant approved!");
5306
6349
  approved = true;
5307
6350
  break;
5308
6351
  }
5309
6352
  if (status.status === "denied" || status.status === "revoked") {
5310
6353
  throw new CliError(`Grant ${status.status}.`);
5311
6354
  }
5312
- await new Promise((r) => setTimeout(r, interval));
6355
+ await new Promise((r3) => setTimeout(r3, interval));
5313
6356
  }
5314
6357
  if (!approved) {
5315
6358
  const minutes = Math.round(maxWait / 6e4);
@@ -5317,12 +6360,12 @@ async function runAudienceMode(audience, action, args) {
5317
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.`
5318
6361
  );
5319
6362
  }
5320
- consola34.info("Fetching grant token...");
6363
+ consola38.info("Fetching grant token...");
5321
6364
  const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
5322
6365
  method: "POST"
5323
6366
  });
5324
6367
  if (audience === "escapes") {
5325
- consola34.info(`Executing: ${command.join(" ")}`);
6368
+ consola38.info(`Executing: ${command.join(" ")}`);
5326
6369
  try {
5327
6370
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
5328
6371
  execFileSync12(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
@@ -5340,8 +6383,8 @@ async function runAudienceMode(audience, action, args) {
5340
6383
 
5341
6384
  // src/commands/proxy.ts
5342
6385
  import { spawn as spawn2 } from "child_process";
5343
- import { defineCommand as defineCommand41 } from "citty";
5344
- import consola35 from "consola";
6386
+ import { defineCommand as defineCommand44 } from "citty";
6387
+ import consola39 from "consola";
5345
6388
 
5346
6389
  // src/proxy/config.ts
5347
6390
  function buildDefaultProxyConfigToml(opts) {
@@ -5484,10 +6527,10 @@ function resolveProxyConfigOptions() {
5484
6527
  77
5485
6528
  );
5486
6529
  }
5487
- consola35.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
6530
+ consola39.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
5488
6531
  return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
5489
6532
  }
5490
- var proxyCommand = defineCommand41({
6533
+ var proxyCommand = defineCommand44({
5491
6534
  meta: {
5492
6535
  name: "proxy",
5493
6536
  description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
@@ -5509,12 +6552,12 @@ var proxyCommand = defineCommand41({
5509
6552
  let close = null;
5510
6553
  if (reuseUrl) {
5511
6554
  proxyUrl = reuseUrl;
5512
- consola35.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
6555
+ consola39.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
5513
6556
  } else {
5514
6557
  const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
5515
6558
  proxyUrl = ephemeral.url;
5516
6559
  close = ephemeral.close;
5517
- consola35.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
6560
+ consola39.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
5518
6561
  }
5519
6562
  const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
5520
6563
  const childEnv = {
@@ -5546,7 +6589,7 @@ var proxyCommand = defineCommand41({
5546
6589
  else resolveExit(code ?? 0);
5547
6590
  });
5548
6591
  child.once("error", (err) => {
5549
- consola35.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
6592
+ consola39.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
5550
6593
  resolveExit(127);
5551
6594
  });
5552
6595
  });
@@ -5560,8 +6603,8 @@ function signalNumber(signal) {
5560
6603
  }
5561
6604
 
5562
6605
  // src/commands/explain.ts
5563
- import { defineCommand as defineCommand42 } from "citty";
5564
- var explainCommand = defineCommand42({
6606
+ import { defineCommand as defineCommand45 } from "citty";
6607
+ var explainCommand = defineCommand45({
5565
6608
  meta: {
5566
6609
  name: "explain",
5567
6610
  description: "Show what permission a command would need"
@@ -5599,9 +6642,9 @@ var explainCommand = defineCommand42({
5599
6642
  });
5600
6643
 
5601
6644
  // src/commands/config/get.ts
5602
- import { defineCommand as defineCommand43 } from "citty";
5603
- import consola36 from "consola";
5604
- var configGetCommand = defineCommand43({
6645
+ import { defineCommand as defineCommand46 } from "citty";
6646
+ import consola40 from "consola";
6647
+ var configGetCommand = defineCommand46({
5605
6648
  meta: {
5606
6649
  name: "get",
5607
6650
  description: "Get a configuration value"
@@ -5621,7 +6664,7 @@ var configGetCommand = defineCommand43({
5621
6664
  if (idp)
5622
6665
  console.log(idp);
5623
6666
  else
5624
- consola36.info("No IdP configured.");
6667
+ consola40.info("No IdP configured.");
5625
6668
  break;
5626
6669
  }
5627
6670
  case "email": {
@@ -5629,7 +6672,7 @@ var configGetCommand = defineCommand43({
5629
6672
  if (auth?.email)
5630
6673
  console.log(auth.email);
5631
6674
  else
5632
- consola36.info("Not logged in.");
6675
+ consola40.info("Not logged in.");
5633
6676
  break;
5634
6677
  }
5635
6678
  default: {
@@ -5642,7 +6685,7 @@ var configGetCommand = defineCommand43({
5642
6685
  if (sectionObj && field in sectionObj) {
5643
6686
  console.log(sectionObj[field]);
5644
6687
  } else {
5645
- consola36.info(`Key "${key}" not set.`);
6688
+ consola40.info(`Key "${key}" not set.`);
5646
6689
  }
5647
6690
  } else {
5648
6691
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -5653,9 +6696,9 @@ var configGetCommand = defineCommand43({
5653
6696
  });
5654
6697
 
5655
6698
  // src/commands/config/set.ts
5656
- import { defineCommand as defineCommand44 } from "citty";
5657
- import consola37 from "consola";
5658
- var configSetCommand = defineCommand44({
6699
+ import { defineCommand as defineCommand47 } from "citty";
6700
+ import consola41 from "consola";
6701
+ var configSetCommand = defineCommand47({
5659
6702
  meta: {
5660
6703
  name: "set",
5661
6704
  description: "Set a configuration value"
@@ -5691,12 +6734,12 @@ var configSetCommand = defineCommand44({
5691
6734
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
5692
6735
  }
5693
6736
  saveConfig(config);
5694
- consola37.success(`Set ${key} = ${value}`);
6737
+ consola41.success(`Set ${key} = ${value}`);
5695
6738
  }
5696
6739
  });
5697
6740
 
5698
6741
  // src/commands/fetch/index.ts
5699
- import { defineCommand as defineCommand45 } from "citty";
6742
+ import { defineCommand as defineCommand48 } from "citty";
5700
6743
  async function doRequest(method, url, body, contentType, raw, showHeaders) {
5701
6744
  const token = getAuthToken();
5702
6745
  if (!token) {
@@ -5732,13 +6775,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
5732
6775
  throw new CliError(`HTTP ${response.status} ${response.statusText}`);
5733
6776
  }
5734
6777
  }
5735
- var fetchCommand = defineCommand45({
6778
+ var fetchCommand = defineCommand48({
5736
6779
  meta: {
5737
6780
  name: "fetch",
5738
6781
  description: "Make authenticated HTTP requests"
5739
6782
  },
5740
6783
  subCommands: {
5741
- get: defineCommand45({
6784
+ get: defineCommand48({
5742
6785
  meta: {
5743
6786
  name: "get",
5744
6787
  description: "GET request with auth token"
@@ -5764,7 +6807,7 @@ var fetchCommand = defineCommand45({
5764
6807
  await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
5765
6808
  }
5766
6809
  }),
5767
- post: defineCommand45({
6810
+ post: defineCommand48({
5768
6811
  meta: {
5769
6812
  name: "post",
5770
6813
  description: "POST request with auth token"
@@ -5803,8 +6846,8 @@ var fetchCommand = defineCommand45({
5803
6846
  });
5804
6847
 
5805
6848
  // src/commands/mcp/index.ts
5806
- import { defineCommand as defineCommand46 } from "citty";
5807
- var mcpCommand = defineCommand46({
6849
+ import { defineCommand as defineCommand49 } from "citty";
6850
+ var mcpCommand = defineCommand49({
5808
6851
  meta: {
5809
6852
  name: "mcp",
5810
6853
  description: "Start MCP server for AI agents"
@@ -5827,7 +6870,7 @@ var mcpCommand = defineCommand46({
5827
6870
  if (transport !== "stdio" && transport !== "sse") {
5828
6871
  throw new Error('Transport must be "stdio" or "sse"');
5829
6872
  }
5830
- const { startMcpServer } = await import("./server-JBBJXDDS.js");
6873
+ const { startMcpServer } = await import("./server-6B26NNLZ.js");
5831
6874
  await startMcpServer(transport, port);
5832
6875
  }
5833
6876
  });
@@ -5837,8 +6880,8 @@ import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSyn
5837
6880
  import { randomBytes } from "crypto";
5838
6881
  import { execFileSync as execFileSync13 } from "child_process";
5839
6882
  import { join as join13 } from "path";
5840
- import { defineCommand as defineCommand47 } from "citty";
5841
- import consola38 from "consola";
6883
+ import { defineCommand as defineCommand50 } from "citty";
6884
+ import consola42 from "consola";
5842
6885
  var DEFAULT_IDP_URL = "https://id.openape.at";
5843
6886
  async function downloadTemplate(repo, targetDir) {
5844
6887
  const { downloadTemplate: gigetDownload } = await import("giget");
@@ -5855,20 +6898,20 @@ function installDeps(dir) {
5855
6898
  }
5856
6899
  }
5857
6900
  async function promptChoice(message, choices) {
5858
- const result = await consola38.prompt(message, { type: "select", options: choices });
6901
+ const result = await consola42.prompt(message, { type: "select", options: choices });
5859
6902
  if (typeof result === "symbol") {
5860
6903
  throw new CliExit(0);
5861
6904
  }
5862
6905
  return result;
5863
6906
  }
5864
6907
  async function promptText(message, defaultValue) {
5865
- const result = await consola38.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
6908
+ const result = await consola42.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
5866
6909
  if (typeof result === "symbol") {
5867
6910
  throw new CliExit(0);
5868
6911
  }
5869
6912
  return result || defaultValue || "";
5870
6913
  }
5871
- var initCommand = defineCommand47({
6914
+ var initCommand = defineCommand50({
5872
6915
  meta: {
5873
6916
  name: "init",
5874
6917
  description: "Scaffold a new OpenApe project"
@@ -5913,20 +6956,20 @@ async function initSP(targetDir) {
5913
6956
  if (existsSync14(join13(dir, "package.json"))) {
5914
6957
  throw new CliError(`Directory "${dir}" already contains a project.`);
5915
6958
  }
5916
- consola38.start("Scaffolding SP starter...");
6959
+ consola42.start("Scaffolding SP starter...");
5917
6960
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
5918
- consola38.success("Scaffolded from openape-sp-starter");
5919
- consola38.start("Installing dependencies...");
6961
+ consola42.success("Scaffolded from openape-sp-starter");
6962
+ consola42.start("Installing dependencies...");
5920
6963
  installDeps(dir);
5921
- consola38.success("Dependencies installed");
6964
+ consola42.success("Dependencies installed");
5922
6965
  const envExample = join13(dir, ".env.example");
5923
6966
  const envFile = join13(dir, ".env");
5924
6967
  if (existsSync14(envExample) && !existsSync14(envFile)) {
5925
6968
  copyFileSync(envExample, envFile);
5926
- consola38.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
6969
+ consola42.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
5927
6970
  }
5928
6971
  console.log("");
5929
- consola38.box([
6972
+ consola42.box([
5930
6973
  `cd ${dir}`,
5931
6974
  "npm run dev",
5932
6975
  "",
@@ -5945,15 +6988,15 @@ async function initIdP(targetDir) {
5945
6988
  "s3 (S3-compatible)"
5946
6989
  ]);
5947
6990
  const adminEmail = await promptText("Admin email");
5948
- consola38.start("Scaffolding IdP starter...");
6991
+ consola42.start("Scaffolding IdP starter...");
5949
6992
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
5950
- consola38.success("Scaffolded from openape-idp-starter");
5951
- consola38.start("Installing dependencies...");
6993
+ consola42.success("Scaffolded from openape-idp-starter");
6994
+ consola42.start("Installing dependencies...");
5952
6995
  installDeps(dir);
5953
- consola38.success("Dependencies installed");
6996
+ consola42.success("Dependencies installed");
5954
6997
  const sessionSecret = randomBytes(32).toString("hex");
5955
6998
  const managementToken = randomBytes(32).toString("hex");
5956
- consola38.success("Secrets generated");
6999
+ consola42.success("Secrets generated");
5957
7000
  const isLocalhost = domain === "localhost";
5958
7001
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
5959
7002
  const envContent = [
@@ -5969,9 +7012,9 @@ async function initIdP(targetDir) {
5969
7012
  ].join("\n");
5970
7013
  writeFileSync9(join13(dir, ".env"), `${envContent}
5971
7014
  `, { mode: 384 });
5972
- consola38.success(".env created");
7015
+ consola42.success(".env created");
5973
7016
  console.log("");
5974
- consola38.box([
7017
+ consola42.box([
5975
7018
  `cd ${dir}`,
5976
7019
  "npm run dev",
5977
7020
  "",
@@ -5991,8 +7034,8 @@ import { Buffer as Buffer5 } from "buffer";
5991
7034
  import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
5992
7035
  import { execFile as execFile2 } from "child_process";
5993
7036
  import { sign as sign2 } from "crypto";
5994
- import { defineCommand as defineCommand48 } from "citty";
5995
- import consola39 from "consola";
7037
+ import { defineCommand as defineCommand51 } from "citty";
7038
+ import consola43 from "consola";
5996
7039
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
5997
7040
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
5998
7041
  var POLL_INTERVAL = 3e3;
@@ -6035,7 +7078,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
6035
7078
  }
6036
7079
  throw new Error("Enrollment timed out. Please check the browser and try again.");
6037
7080
  }
6038
- var enrollCommand = defineCommand48({
7081
+ var enrollCommand = defineCommand51({
6039
7082
  meta: {
6040
7083
  name: "enroll",
6041
7084
  description: "Enroll an agent with an Identity Provider"
@@ -6055,48 +7098,48 @@ var enrollCommand = defineCommand48({
6055
7098
  }
6056
7099
  },
6057
7100
  async run({ args }) {
6058
- const idp = args.idp || await consola39.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
6059
- if (typeof r === "symbol") throw new CliExit(0);
6060
- return r;
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;
6061
7104
  }) || DEFAULT_IDP_URL2;
6062
- const agentName = args.name || await consola39.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
6063
- if (typeof r === "symbol") throw new CliExit(0);
6064
- return r;
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;
6065
7108
  });
6066
7109
  if (!agentName) {
6067
7110
  throw new CliError("Agent name is required.");
6068
7111
  }
6069
- const keyPath = args.key || await consola39.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
6070
- if (typeof r === "symbol") throw new CliExit(0);
6071
- return r;
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;
6072
7115
  }) || DEFAULT_KEY_PATH;
6073
7116
  const resolvedKey = resolveKeyPath(keyPath);
6074
7117
  let publicKey;
6075
7118
  if (existsSync15(resolvedKey)) {
6076
7119
  publicKey = readPublicKey(resolvedKey);
6077
- consola39.success(`Using existing key ${keyPath}`);
7120
+ consola43.success(`Using existing key ${keyPath}`);
6078
7121
  } else {
6079
- consola39.start(`Generating Ed25519 key pair at ${keyPath}...`);
7122
+ consola43.start(`Generating Ed25519 key pair at ${keyPath}...`);
6080
7123
  publicKey = generateAndSaveKey(keyPath);
6081
- consola39.success(`Key pair generated at ${keyPath}`);
7124
+ consola43.success(`Key pair generated at ${keyPath}`);
6082
7125
  }
6083
7126
  const encodedKey = encodeURIComponent(publicKey);
6084
7127
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
6085
- consola39.info("Opening browser for enrollment...");
6086
- consola39.info(`\u2192 ${idp}/enroll`);
7128
+ consola43.info("Opening browser for enrollment...");
7129
+ consola43.info(`\u2192 ${idp}/enroll`);
6087
7130
  openBrowser2(enrollUrl);
6088
7131
  console.log("");
6089
- const agentEmail = await consola39.prompt(
7132
+ const agentEmail = await consola43.prompt(
6090
7133
  "Agent email (shown in browser after enrollment)",
6091
7134
  { type: "text", placeholder: `agent+${agentName}@...` }
6092
- ).then((r) => {
6093
- if (typeof r === "symbol") throw new CliExit(0);
6094
- return r;
7135
+ ).then((r3) => {
7136
+ if (typeof r3 === "symbol") throw new CliExit(0);
7137
+ return r3;
6095
7138
  });
6096
7139
  if (!agentEmail) {
6097
7140
  throw new CliError("Agent email is required to verify enrollment.");
6098
7141
  }
6099
- consola39.start("Verifying enrollment...");
7142
+ consola43.start("Verifying enrollment...");
6100
7143
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
6101
7144
  saveAuth({
6102
7145
  idp,
@@ -6108,18 +7151,18 @@ var enrollCommand = defineCommand48({
6108
7151
  config.defaults = { ...config.defaults, idp };
6109
7152
  config.agent = { key: keyPath, email: agentEmail };
6110
7153
  saveConfig(config);
6111
- consola39.success(`Agent enrolled as ${agentEmail}`);
6112
- consola39.success("Config saved to ~/.config/apes/");
7154
+ consola43.success(`Agent enrolled as ${agentEmail}`);
7155
+ consola43.success("Config saved to ~/.config/apes/");
6113
7156
  console.log("");
6114
- consola39.info("Verify with: apes whoami");
7157
+ consola43.info("Verify with: apes whoami");
6115
7158
  }
6116
7159
  });
6117
7160
 
6118
7161
  // src/commands/register-user.ts
6119
7162
  import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
6120
- import { defineCommand as defineCommand49 } from "citty";
6121
- import consola40 from "consola";
6122
- var registerUserCommand = defineCommand49({
7163
+ import { defineCommand as defineCommand52 } from "citty";
7164
+ import consola44 from "consola";
7165
+ var registerUserCommand = defineCommand52({
6123
7166
  meta: {
6124
7167
  name: "register-user",
6125
7168
  description: "Register a sub-user with SSH key"
@@ -6174,18 +7217,18 @@ var registerUserCommand = defineCommand49({
6174
7217
  ...userType ? { type: userType } : {}
6175
7218
  }
6176
7219
  });
6177
- consola40.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
7220
+ consola44.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
6178
7221
  }
6179
7222
  });
6180
7223
 
6181
7224
  // src/commands/utils/index.ts
6182
- import { defineCommand as defineCommand51 } from "citty";
7225
+ import { defineCommand as defineCommand54 } from "citty";
6183
7226
 
6184
7227
  // src/commands/utils/dig.ts
6185
- import { defineCommand as defineCommand50 } from "citty";
6186
- import consola41 from "consola";
7228
+ import { defineCommand as defineCommand53 } from "citty";
7229
+ import consola45 from "consola";
6187
7230
  import { resolveDDISA as resolveDDISA2 } from "@openape/core";
6188
- var digCommand = defineCommand50({
7231
+ var digCommand = defineCommand53({
6189
7232
  meta: {
6190
7233
  name: "dig",
6191
7234
  description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
@@ -6258,12 +7301,12 @@ var digCommand = defineCommand50({
6258
7301
  console.log(` domain: ${domain}`);
6259
7302
  console.log("");
6260
7303
  if (!result.ddisa.found) {
6261
- consola41.warn(`No DDISA record at _ddisa.${domain}`);
7304
+ consola45.warn(`No DDISA record at _ddisa.${domain}`);
6262
7305
  if (result.hint) console.log(`
6263
7306
  ${result.hint}`);
6264
7307
  throw new CliError(`No DDISA record found for ${domain}`);
6265
7308
  }
6266
- consola41.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
7309
+ consola45.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
6267
7310
  console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
6268
7311
  console.log(` IdP URL: ${result.ddisa.idp}`);
6269
7312
  if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
@@ -6273,13 +7316,13 @@ ${result.hint}`);
6273
7316
  return;
6274
7317
  }
6275
7318
  if (result.idpDiscovery.ok) {
6276
- consola41.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
7319
+ consola45.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
6277
7320
  if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
6278
7321
  if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
6279
7322
  if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
6280
7323
  if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
6281
7324
  } else {
6282
- consola41.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
7325
+ consola45.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
6283
7326
  if (result.hint) console.log(`
6284
7327
  ${result.hint}`);
6285
7328
  throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
@@ -6288,7 +7331,7 @@ ${result.hint}`);
6288
7331
  });
6289
7332
 
6290
7333
  // src/commands/utils/index.ts
6291
- var utilsCommand = defineCommand51({
7334
+ var utilsCommand = defineCommand54({
6292
7335
  meta: {
6293
7336
  name: "utils",
6294
7337
  description: "Admin/diagnostic utilities (dig, \u2026)"
@@ -6299,12 +7342,12 @@ var utilsCommand = defineCommand51({
6299
7342
  });
6300
7343
 
6301
7344
  // src/commands/sessions/index.ts
6302
- import { defineCommand as defineCommand54 } from "citty";
7345
+ import { defineCommand as defineCommand57 } from "citty";
6303
7346
 
6304
7347
  // src/commands/sessions/list.ts
6305
- import { defineCommand as defineCommand52 } from "citty";
6306
- import consola42 from "consola";
6307
- var sessionsListCommand = defineCommand52({
7348
+ import { defineCommand as defineCommand55 } from "citty";
7349
+ import consola46 from "consola";
7350
+ var sessionsListCommand = defineCommand55({
6308
7351
  meta: {
6309
7352
  name: "list",
6310
7353
  description: "List your active refresh-token families (one per logged-in device)."
@@ -6322,7 +7365,7 @@ var sessionsListCommand = defineCommand52({
6322
7365
  return;
6323
7366
  }
6324
7367
  if (result.data.length === 0) {
6325
- consola42.info("No active sessions.");
7368
+ consola46.info("No active sessions.");
6326
7369
  return;
6327
7370
  }
6328
7371
  for (const f of result.data) {
@@ -6334,9 +7377,9 @@ var sessionsListCommand = defineCommand52({
6334
7377
  });
6335
7378
 
6336
7379
  // src/commands/sessions/remove.ts
6337
- import { defineCommand as defineCommand53 } from "citty";
6338
- import consola43 from "consola";
6339
- var sessionsRemoveCommand = defineCommand53({
7380
+ import { defineCommand as defineCommand56 } from "citty";
7381
+ import consola47 from "consola";
7382
+ var sessionsRemoveCommand = defineCommand56({
6340
7383
  meta: {
6341
7384
  name: "remove",
6342
7385
  description: "Revoke one of your active refresh-token families by id."
@@ -6352,12 +7395,12 @@ var sessionsRemoveCommand = defineCommand53({
6352
7395
  const id = String(args.familyId).trim();
6353
7396
  if (!id) throw new CliError("familyId required");
6354
7397
  await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
6355
- consola43.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
7398
+ consola47.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
6356
7399
  }
6357
7400
  });
6358
7401
 
6359
7402
  // src/commands/sessions/index.ts
6360
- var sessionsCommand = defineCommand54({
7403
+ var sessionsCommand = defineCommand57({
6361
7404
  meta: {
6362
7405
  name: "sessions",
6363
7406
  description: "Manage your active refresh-token sessions across devices"
@@ -6369,10 +7412,10 @@ var sessionsCommand = defineCommand54({
6369
7412
  });
6370
7413
 
6371
7414
  // src/commands/dns-check.ts
6372
- import { defineCommand as defineCommand55 } from "citty";
6373
- import consola44 from "consola";
7415
+ import { defineCommand as defineCommand58 } from "citty";
7416
+ import consola48 from "consola";
6374
7417
  import { resolveDDISA as resolveDDISA3 } from "@openape/core";
6375
- var dnsCheckCommand = defineCommand55({
7418
+ var dnsCheckCommand = defineCommand58({
6376
7419
  meta: {
6377
7420
  name: "dns-check",
6378
7421
  description: "Validate DDISA DNS TXT records for a domain"
@@ -6386,7 +7429,7 @@ var dnsCheckCommand = defineCommand55({
6386
7429
  },
6387
7430
  async run({ args }) {
6388
7431
  const domain = args.domain;
6389
- consola44.start(`Checking _ddisa.${domain}...`);
7432
+ consola48.start(`Checking _ddisa.${domain}...`);
6390
7433
  try {
6391
7434
  const result = await resolveDDISA3(domain);
6392
7435
  if (!result) {
@@ -6395,7 +7438,7 @@ var dnsCheckCommand = defineCommand55({
6395
7438
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
6396
7439
  throw new CliError(`No DDISA record found for ${domain}`);
6397
7440
  }
6398
- consola44.success(`_ddisa.${domain} \u2192 ${result.idp}`);
7441
+ consola48.success(`_ddisa.${domain} \u2192 ${result.idp}`);
6399
7442
  console.log("");
6400
7443
  console.log(` Version: ${result.version || "ddisa1"}`);
6401
7444
  console.log(` IdP URL: ${result.idp}`);
@@ -6404,14 +7447,14 @@ var dnsCheckCommand = defineCommand55({
6404
7447
  if (result.priority !== void 0)
6405
7448
  console.log(` Priority: ${result.priority}`);
6406
7449
  console.log("");
6407
- consola44.start(`Verifying IdP at ${result.idp}...`);
7450
+ consola48.start(`Verifying IdP at ${result.idp}...`);
6408
7451
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
6409
7452
  if (!discoResp.ok) {
6410
- consola44.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
7453
+ consola48.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
6411
7454
  return;
6412
7455
  }
6413
7456
  const disco = await discoResp.json();
6414
- consola44.success(`IdP is reachable`);
7457
+ consola48.success(`IdP is reachable`);
6415
7458
  console.log(` Issuer: ${disco.issuer}`);
6416
7459
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
6417
7460
  if (disco.ddisa_auth_methods_supported) {
@@ -6429,7 +7472,7 @@ var dnsCheckCommand = defineCommand55({
6429
7472
  // src/commands/health.ts
6430
7473
  import { exec } from "child_process";
6431
7474
  import { promisify } from "util";
6432
- import { defineCommand as defineCommand56 } from "citty";
7475
+ import { defineCommand as defineCommand59 } from "citty";
6433
7476
  var execAsync = promisify(exec);
6434
7477
  async function resolveApeShellPath() {
6435
7478
  try {
@@ -6465,7 +7508,7 @@ async function bestEffortGrantCount(idp) {
6465
7508
  }
6466
7509
  }
6467
7510
  async function runHealth(args) {
6468
- const version = true ? "1.7.1" : "0.0.0";
7511
+ const version = true ? "1.8.0" : "0.0.0";
6469
7512
  const auth = loadAuth();
6470
7513
  if (!auth) {
6471
7514
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -6528,7 +7571,7 @@ async function runHealth(args) {
6528
7571
  throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
6529
7572
  }
6530
7573
  }
6531
- var healthCommand = defineCommand56({
7574
+ var healthCommand = defineCommand59({
6532
7575
  meta: {
6533
7576
  name: "health",
6534
7577
  description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
@@ -6546,8 +7589,8 @@ var healthCommand = defineCommand56({
6546
7589
  });
6547
7590
 
6548
7591
  // src/commands/workflows.ts
6549
- import { defineCommand as defineCommand57 } from "citty";
6550
- import consola45 from "consola";
7592
+ import { defineCommand as defineCommand60 } from "citty";
7593
+ import consola49 from "consola";
6551
7594
 
6552
7595
  // src/guides/index.ts
6553
7596
  var guides = [
@@ -6597,7 +7640,7 @@ var guides = [
6597
7640
  ];
6598
7641
 
6599
7642
  // src/commands/workflows.ts
6600
- var workflowsCommand = defineCommand57({
7643
+ var workflowsCommand = defineCommand60({
6601
7644
  meta: {
6602
7645
  name: "workflows",
6603
7646
  description: "Discover workflow guides"
@@ -6618,7 +7661,7 @@ var workflowsCommand = defineCommand57({
6618
7661
  if (args.id) {
6619
7662
  const guide = guides.find((g) => g.id === String(args.id));
6620
7663
  if (!guide) {
6621
- consola45.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
7664
+ consola49.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
6622
7665
  throw new CliError(`Guide not found: ${args.id}`);
6623
7666
  }
6624
7667
  if (args.json) {
@@ -6661,7 +7704,7 @@ var workflowsCommand = defineCommand57({
6661
7704
  import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
6662
7705
  import { homedir as homedir13 } from "os";
6663
7706
  import { join as join14 } from "path";
6664
- import consola46 from "consola";
7707
+ import consola50 from "consola";
6665
7708
  var PACKAGE_NAME = "@openape/apes";
6666
7709
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
6667
7710
  var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
@@ -6706,7 +7749,7 @@ async function fetchLatestVersion() {
6706
7749
  }
6707
7750
  function warnIfBehind(currentVersion, latest) {
6708
7751
  if (compareSemver(currentVersion, latest) < 0) {
6709
- consola46.warn(
7752
+ consola50.warn(
6710
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.)`
6711
7754
  );
6712
7755
  }
@@ -6738,10 +7781,10 @@ if (shellRewrite) {
6738
7781
  if (shellRewrite.action === "rewrite") {
6739
7782
  process.argv = shellRewrite.argv;
6740
7783
  } else if (shellRewrite.action === "version") {
6741
- console.log(`ape-shell ${"1.7.1"} (OpenApe DDISA shell wrapper)`);
7784
+ console.log(`ape-shell ${"1.8.0"} (OpenApe DDISA shell wrapper)`);
6742
7785
  process.exit(0);
6743
7786
  } else if (shellRewrite.action === "help") {
6744
- console.log(`ape-shell ${"1.7.1"} \u2014 OpenApe DDISA shell wrapper`);
7787
+ console.log(`ape-shell ${"1.8.0"} \u2014 OpenApe DDISA shell wrapper`);
6745
7788
  console.log("");
6746
7789
  console.log("Usage:");
6747
7790
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -6756,7 +7799,7 @@ if (shellRewrite) {
6756
7799
  console.log(" --help, -h Show this help message");
6757
7800
  process.exit(0);
6758
7801
  } else if (shellRewrite.action === "interactive") {
6759
- const { runInteractiveShell } = await import("./orchestrator-EH6V5ATG.js");
7802
+ const { runInteractiveShell } = await import("./orchestrator-2QS5KXFL.js");
6760
7803
  await runInteractiveShell();
6761
7804
  process.exit(0);
6762
7805
  } else {
@@ -6765,7 +7808,7 @@ if (shellRewrite) {
6765
7808
  }
6766
7809
  }
6767
7810
  var debug = process.argv.includes("--debug");
6768
- var grantsCommand = defineCommand58({
7811
+ var grantsCommand = defineCommand61({
6769
7812
  meta: {
6770
7813
  name: "grants",
6771
7814
  description: "Grant management"
@@ -6786,7 +7829,7 @@ var grantsCommand = defineCommand58({
6786
7829
  "delegation-revoke": delegationRevokeCommand
6787
7830
  }
6788
7831
  });
6789
- var configCommand = defineCommand58({
7832
+ var configCommand = defineCommand61({
6790
7833
  meta: {
6791
7834
  name: "config",
6792
7835
  description: "Configuration management"
@@ -6796,10 +7839,10 @@ var configCommand = defineCommand58({
6796
7839
  set: configSetCommand
6797
7840
  }
6798
7841
  });
6799
- var main = defineCommand58({
7842
+ var main = defineCommand61({
6800
7843
  meta: {
6801
7844
  name: "apes",
6802
- version: "1.7.1",
7845
+ version: "1.8.0",
6803
7846
  description: "Unified CLI for OpenApe"
6804
7847
  },
6805
7848
  subCommands: {
@@ -6847,29 +7890,29 @@ var NO_REFRESH_COMMANDS = /* @__PURE__ */ new Set([
6847
7890
  async function maybeRefreshAuth() {
6848
7891
  const sub = process.argv[2];
6849
7892
  if (!sub || NO_REFRESH_COMMANDS.has(sub)) return;
6850
- const { loadAuth: loadAuth2 } = await import("./config-MOB5DJ6H.js");
7893
+ const { loadAuth: loadAuth2 } = await import("./config-DA2L3XOV.js");
6851
7894
  if (!loadAuth2()) return;
6852
7895
  try {
6853
- const { ensureFreshToken } = await import("./http-5F7FX4V7.js");
7896
+ const { ensureFreshToken } = await import("./http-JWS5LV2U.js");
6854
7897
  await ensureFreshToken();
6855
7898
  } catch {
6856
7899
  }
6857
7900
  }
6858
7901
  await maybeRefreshAuth();
6859
- await maybeWarnStaleVersion("1.7.1").catch(() => {
7902
+ await maybeWarnStaleVersion("1.8.0").catch(() => {
6860
7903
  });
6861
7904
  runMain(main).catch((err) => {
6862
7905
  if (err instanceof CliExit) {
6863
7906
  process.exit(err.exitCode);
6864
7907
  }
6865
7908
  if (err instanceof CliError) {
6866
- consola47.error(err.message);
7909
+ consola51.error(err.message);
6867
7910
  process.exit(err.exitCode);
6868
7911
  }
6869
7912
  if (debug) {
6870
- consola47.error(err);
7913
+ consola51.error(err);
6871
7914
  } else {
6872
- consola47.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
7915
+ consola51.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
6873
7916
  }
6874
7917
  process.exit(1);
6875
7918
  });