@openape/apes 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-TDSCDH5P.js +518 -0
- package/dist/chunk-TDSCDH5P.js.map +1 -0
- package/dist/cli.js +452 -1077
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +89 -1
- package/dist/index.js +10 -2
- package/dist/{server-AZOEKT55.js → server-TGGXHP4H.js} +2 -2
- package/package.json +3 -3
- package/dist/chunk-ZSJU7IXE.js +0 -45
- package/dist/chunk-ZSJU7IXE.js.map +0 -1
- /package/dist/{server-AZOEKT55.js.map → server-TGGXHP4H.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -7,8 +7,11 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
CliError,
|
|
9
9
|
CliExit,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
RpcSessionMap,
|
|
11
|
+
parseDuration,
|
|
12
|
+
runLoop,
|
|
13
|
+
taskTools
|
|
14
|
+
} from "./chunk-TDSCDH5P.js";
|
|
12
15
|
import {
|
|
13
16
|
loadEd25519PrivateKey,
|
|
14
17
|
readPublicKeyComment
|
|
@@ -74,7 +77,7 @@ import {
|
|
|
74
77
|
import "./chunk-7OCVIDC7.js";
|
|
75
78
|
|
|
76
79
|
// src/cli.ts
|
|
77
|
-
import
|
|
80
|
+
import consola49 from "consola";
|
|
78
81
|
|
|
79
82
|
// src/ape-shell.ts
|
|
80
83
|
import path from "path";
|
|
@@ -104,7 +107,7 @@ function rewriteApeShellArgs(argv, argv0) {
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
// src/cli.ts
|
|
107
|
-
import { defineCommand as
|
|
110
|
+
import { defineCommand as defineCommand60, runMain } from "citty";
|
|
108
111
|
|
|
109
112
|
// src/commands/auth/login.ts
|
|
110
113
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -317,7 +320,7 @@ async function loginWithPKCE(idp) {
|
|
|
317
320
|
authUrl.searchParams.set("state", state);
|
|
318
321
|
authUrl.searchParams.set("nonce", nonce);
|
|
319
322
|
authUrl.searchParams.set("scope", "openid email profile offline_access");
|
|
320
|
-
const code = await new Promise((
|
|
323
|
+
const code = await new Promise((resolve4, reject) => {
|
|
321
324
|
const server = createServer((req, res) => {
|
|
322
325
|
const url = new URL(req.url, `http://localhost:${CALLBACK_PORT}`);
|
|
323
326
|
if (url.pathname === "/callback") {
|
|
@@ -334,7 +337,7 @@ async function loginWithPKCE(idp) {
|
|
|
334
337
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
335
338
|
res.end("<h1>Login successful!</h1><p>You can close this window.</p>");
|
|
336
339
|
server.close();
|
|
337
|
-
|
|
340
|
+
resolve4(authCode);
|
|
338
341
|
return;
|
|
339
342
|
}
|
|
340
343
|
res.writeHead(400);
|
|
@@ -895,7 +898,7 @@ async function waitForApproval2(grantsUrl, grantId) {
|
|
|
895
898
|
if (grant.status === "revoked") {
|
|
896
899
|
throw new CliError("Grant revoked.");
|
|
897
900
|
}
|
|
898
|
-
await new Promise((
|
|
901
|
+
await new Promise((resolve4) => setTimeout(resolve4, interval));
|
|
899
902
|
}
|
|
900
903
|
throw new CliError("Timed out waiting for approval.");
|
|
901
904
|
}
|
|
@@ -2262,7 +2265,7 @@ function createFetch(globalOptions = {}) {
|
|
|
2262
2265
|
if (retries > 0 && (Array.isArray(context.options.retryStatusCodes) ? context.options.retryStatusCodes.includes(responseCode) : retryStatusCodes.has(responseCode))) {
|
|
2263
2266
|
const retryDelay = typeof context.options.retryDelay === "function" ? context.options.retryDelay(context) : context.options.retryDelay || 0;
|
|
2264
2267
|
if (retryDelay > 0) {
|
|
2265
|
-
await new Promise((
|
|
2268
|
+
await new Promise((resolve4) => setTimeout(resolve4, retryDelay));
|
|
2266
2269
|
}
|
|
2267
2270
|
return $fetchRaw(context.request, {
|
|
2268
2271
|
...context.options,
|
|
@@ -2690,46 +2693,19 @@ function buildBridgeBlock(bridge) {
|
|
|
2690
2693
|
return `
|
|
2691
2694
|
mkdir -p "$HOME_DIR/Library/Application Support/openape/bridge" "$HOME_DIR/Library/Logs"
|
|
2692
2695
|
cat > "$HOME_DIR/Library/Application Support/openape/bridge/.env" ${shHeredoc(bridge.envFile)}
|
|
2693
|
-
cat > "$HOME_DIR/Library/Application Support/openape/bridge/start.sh" ${shHeredoc(bridge.startScript)}
|
|
2694
|
-
chmod 755 "$HOME_DIR/Library/Application Support/openape/bridge/start.sh"
|
|
2695
2696
|
chmod 600 "$HOME_DIR/Library/Application Support/openape/bridge/.env"
|
|
2696
|
-
|
|
2697
|
-
# System-wide LaunchDaemon \u2014 root-owned, mode 644 (launchd refuses
|
|
2698
|
-
# group/world-writable plists). UserName in the plist makes launchd run
|
|
2699
|
-
# the binary as the agent, not root.
|
|
2700
|
-
cat > ${shQuote(bridge.plistPath)} ${shHeredoc(bridge.plistContent)}
|
|
2701
|
-
chown root:wheel ${shQuote(bridge.plistPath)}
|
|
2702
|
-
chmod 644 ${shQuote(bridge.plistPath)}
|
|
2703
2697
|
`;
|
|
2704
2698
|
}
|
|
2705
|
-
function buildBridgeBootstrapBlock(
|
|
2706
|
-
|
|
2707
|
-
return `
|
|
2708
|
-
launchctl bootout "system/${bridge.plistLabel}" 2>/dev/null || true
|
|
2709
|
-
launchctl bootstrap system ${shQuote(bridge.plistPath)} || \\
|
|
2710
|
-
echo "warn: bridge bootstrap into system domain failed; check ${bridge.plistPath}"
|
|
2711
|
-
`;
|
|
2699
|
+
function buildBridgeBootstrapBlock(_bridge, _name) {
|
|
2700
|
+
return "";
|
|
2712
2701
|
}
|
|
2713
|
-
function buildTroopBlock(
|
|
2714
|
-
if (!troop) return "";
|
|
2702
|
+
function buildTroopBlock(_troop) {
|
|
2715
2703
|
return `
|
|
2716
|
-
mkdir -p "$HOME_DIR/Library/
|
|
2717
|
-
cat > ${shQuote(troop.plistPath)} ${shHeredoc(troop.plistContent)}
|
|
2718
|
-
chown root:wheel ${shQuote(troop.plistPath)}
|
|
2719
|
-
chmod 644 ${shQuote(troop.plistPath)}
|
|
2704
|
+
mkdir -p "$HOME_DIR/Library/Logs" "$HOME_DIR/.openape/agent/tasks"
|
|
2720
2705
|
`;
|
|
2721
2706
|
}
|
|
2722
|
-
function buildTroopBootstrapBlock(
|
|
2723
|
-
|
|
2724
|
-
return `
|
|
2725
|
-
# Bootstrap the troop sync launchd in the system domain. setup.sh runs
|
|
2726
|
-
# as root via \`apes run --as root\`, so we have permission. Stale label
|
|
2727
|
-
# is bootouted first to make re-spawn idempotent.
|
|
2728
|
-
echo "==> Installing troop sync launchd as ${name}\u2026"
|
|
2729
|
-
launchctl bootout "system/${troop.plistLabel}" 2>/dev/null || true
|
|
2730
|
-
launchctl bootstrap system ${shQuote(troop.plistPath)} || \\
|
|
2731
|
-
echo "warn: troop sync bootstrap failed; check ${troop.plistPath}"
|
|
2732
|
-
`;
|
|
2707
|
+
function buildTroopBootstrapBlock(_troop, _name) {
|
|
2708
|
+
return "";
|
|
2733
2709
|
}
|
|
2734
2710
|
function buildDestroyTeardownScript(input) {
|
|
2735
2711
|
const { name, homeDir, adminUser } = input;
|
|
@@ -2992,7 +2968,7 @@ function readPasswordSilent(prompt) {
|
|
|
2992
2968
|
"No TTY available for the silent password prompt. Set APES_ADMIN_PASSWORD in the environment instead."
|
|
2993
2969
|
));
|
|
2994
2970
|
}
|
|
2995
|
-
return new Promise((
|
|
2971
|
+
return new Promise((resolve4, reject) => {
|
|
2996
2972
|
process.stdout.write(prompt);
|
|
2997
2973
|
const wasRaw = process.stdin.isRaw ?? false;
|
|
2998
2974
|
process.stdin.setRawMode(true);
|
|
@@ -3007,7 +2983,7 @@ function readPasswordSilent(prompt) {
|
|
|
3007
2983
|
if (ch === "\r" || ch === "\n") {
|
|
3008
2984
|
cleanup();
|
|
3009
2985
|
process.stdout.write("\n");
|
|
3010
|
-
|
|
2986
|
+
resolve4(buf);
|
|
3011
2987
|
return;
|
|
3012
2988
|
}
|
|
3013
2989
|
if (code === 3) {
|
|
@@ -3303,481 +3279,12 @@ var registerAgentCommand = defineCommand23({
|
|
|
3303
3279
|
});
|
|
3304
3280
|
|
|
3305
3281
|
// src/commands/agents/run.ts
|
|
3306
|
-
import { existsSync as existsSync5, readFileSync as
|
|
3307
|
-
import { homedir as
|
|
3282
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
3283
|
+
import { homedir as homedir4 } from "os";
|
|
3308
3284
|
import { join as join3 } from "path";
|
|
3309
3285
|
import { defineCommand as defineCommand24 } from "citty";
|
|
3310
3286
|
import consola22 from "consola";
|
|
3311
3287
|
|
|
3312
|
-
// src/lib/agent-tools/file.ts
|
|
3313
|
-
import { mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
3314
|
-
import { homedir as homedir4 } from "os";
|
|
3315
|
-
import { dirname, normalize, resolve as resolve2 } from "path";
|
|
3316
|
-
var MAX_BYTES = 1024 * 1024;
|
|
3317
|
-
function jailPath(input) {
|
|
3318
|
-
if (typeof input !== "string" || input === "") {
|
|
3319
|
-
throw new Error("path must be a non-empty string");
|
|
3320
|
-
}
|
|
3321
|
-
const home = homedir4();
|
|
3322
|
-
const candidate = input.startsWith("~/") ? resolve2(home, input.slice(2)) : input.startsWith("/") ? normalize(input) : resolve2(home, input);
|
|
3323
|
-
if (candidate !== home && !candidate.startsWith(`${home}/`)) {
|
|
3324
|
-
throw new Error(`path "${input}" resolves outside the agent's home`);
|
|
3325
|
-
}
|
|
3326
|
-
return candidate;
|
|
3327
|
-
}
|
|
3328
|
-
var fileTools = [
|
|
3329
|
-
{
|
|
3330
|
-
name: "file.read",
|
|
3331
|
-
description: "Read a UTF-8 file from the agent's home directory ($HOME). Capped at 1MB. Path traversal blocked.",
|
|
3332
|
-
parameters: {
|
|
3333
|
-
type: "object",
|
|
3334
|
-
properties: {
|
|
3335
|
-
path: { type: "string", description: "Path relative to $HOME (or absolute under $HOME). `..` segments are rejected." }
|
|
3336
|
-
},
|
|
3337
|
-
required: ["path"]
|
|
3338
|
-
},
|
|
3339
|
-
execute: async (args) => {
|
|
3340
|
-
const a = args;
|
|
3341
|
-
const p2 = jailPath(a.path);
|
|
3342
|
-
const content = readFileSync4(p2, "utf8");
|
|
3343
|
-
if (Buffer.byteLength(content, "utf8") > MAX_BYTES) {
|
|
3344
|
-
return { path: p2, truncated: true, content: content.slice(0, MAX_BYTES) };
|
|
3345
|
-
}
|
|
3346
|
-
return { path: p2, truncated: false, content };
|
|
3347
|
-
}
|
|
3348
|
-
},
|
|
3349
|
-
{
|
|
3350
|
-
name: "file.write",
|
|
3351
|
-
description: "Write a UTF-8 file under the agent's home directory. Creates parent dirs as needed. 1MB max.",
|
|
3352
|
-
parameters: {
|
|
3353
|
-
type: "object",
|
|
3354
|
-
properties: {
|
|
3355
|
-
path: { type: "string", description: "Path relative to $HOME (or absolute under $HOME)." },
|
|
3356
|
-
content: { type: "string", description: "File body. Existing files are overwritten." }
|
|
3357
|
-
},
|
|
3358
|
-
required: ["path", "content"]
|
|
3359
|
-
},
|
|
3360
|
-
execute: async (args) => {
|
|
3361
|
-
const a = args;
|
|
3362
|
-
if (typeof a.content !== "string") throw new Error("content must be a string");
|
|
3363
|
-
if (Buffer.byteLength(a.content, "utf8") > MAX_BYTES) {
|
|
3364
|
-
throw new Error(`content exceeds ${MAX_BYTES} byte cap`);
|
|
3365
|
-
}
|
|
3366
|
-
const p2 = jailPath(a.path);
|
|
3367
|
-
mkdirSync(dirname(p2), { recursive: true });
|
|
3368
|
-
writeFileSync2(p2, a.content, { encoding: "utf8" });
|
|
3369
|
-
return { path: p2, bytes: Buffer.byteLength(a.content, "utf8") };
|
|
3370
|
-
}
|
|
3371
|
-
}
|
|
3372
|
-
];
|
|
3373
|
-
|
|
3374
|
-
// src/lib/agent-tools/http.ts
|
|
3375
|
-
var MAX_BYTES2 = 1024 * 1024;
|
|
3376
|
-
var FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
|
|
3377
|
-
"host",
|
|
3378
|
-
"authorization",
|
|
3379
|
-
"cookie",
|
|
3380
|
-
"connection",
|
|
3381
|
-
"transfer-encoding",
|
|
3382
|
-
"upgrade",
|
|
3383
|
-
"proxy-authorization"
|
|
3384
|
-
]);
|
|
3385
|
-
function sanitizeHeaders(input) {
|
|
3386
|
-
if (!input || typeof input !== "object") return {};
|
|
3387
|
-
const out = {};
|
|
3388
|
-
for (const [k, v] of Object.entries(input)) {
|
|
3389
|
-
if (typeof v !== "string") continue;
|
|
3390
|
-
if (FORBIDDEN_HEADERS.has(k.toLowerCase())) continue;
|
|
3391
|
-
out[k] = v;
|
|
3392
|
-
}
|
|
3393
|
-
return out;
|
|
3394
|
-
}
|
|
3395
|
-
async function readCappedBody(res) {
|
|
3396
|
-
const buf = new Uint8Array(MAX_BYTES2 + 1);
|
|
3397
|
-
let written = 0;
|
|
3398
|
-
const reader = res.body?.getReader();
|
|
3399
|
-
if (!reader) return await res.text();
|
|
3400
|
-
while (true) {
|
|
3401
|
-
const { value, done } = await reader.read();
|
|
3402
|
-
if (done) break;
|
|
3403
|
-
if (written + value.byteLength > MAX_BYTES2) {
|
|
3404
|
-
buf.set(value.subarray(0, MAX_BYTES2 - written), written);
|
|
3405
|
-
written = MAX_BYTES2;
|
|
3406
|
-
try {
|
|
3407
|
-
await reader.cancel();
|
|
3408
|
-
} catch {
|
|
3409
|
-
}
|
|
3410
|
-
break;
|
|
3411
|
-
}
|
|
3412
|
-
buf.set(value, written);
|
|
3413
|
-
written += value.byteLength;
|
|
3414
|
-
}
|
|
3415
|
-
return new TextDecoder().decode(buf.subarray(0, written));
|
|
3416
|
-
}
|
|
3417
|
-
var httpTools = [
|
|
3418
|
-
{
|
|
3419
|
-
name: "http.get",
|
|
3420
|
-
description: "GET an HTTPS URL and return the response body (capped at 1MB). Useful for reading public APIs, RSS feeds, web pages.",
|
|
3421
|
-
parameters: {
|
|
3422
|
-
type: "object",
|
|
3423
|
-
properties: {
|
|
3424
|
-
url: { type: "string", description: "Absolute HTTPS URL." },
|
|
3425
|
-
headers: { type: "object", description: "Optional headers (Host, Authorization, Cookie are stripped)." }
|
|
3426
|
-
},
|
|
3427
|
-
required: ["url"]
|
|
3428
|
-
},
|
|
3429
|
-
execute: async (args) => {
|
|
3430
|
-
const a = args;
|
|
3431
|
-
if (typeof a.url !== "string" || !a.url.startsWith("http")) {
|
|
3432
|
-
throw new Error("url must be an http(s) URL");
|
|
3433
|
-
}
|
|
3434
|
-
const res = await fetch(a.url, { method: "GET", headers: sanitizeHeaders(a.headers) });
|
|
3435
|
-
const body = await readCappedBody(res);
|
|
3436
|
-
return { status: res.status, headers: Object.fromEntries(res.headers), body };
|
|
3437
|
-
}
|
|
3438
|
-
},
|
|
3439
|
-
{
|
|
3440
|
-
name: "http.post",
|
|
3441
|
-
description: "POST JSON to an HTTPS URL and return the response body (capped at 1MB).",
|
|
3442
|
-
parameters: {
|
|
3443
|
-
type: "object",
|
|
3444
|
-
properties: {
|
|
3445
|
-
url: { type: "string", description: "Absolute HTTPS URL." },
|
|
3446
|
-
body: { description: "JSON-serialisable payload." },
|
|
3447
|
-
headers: { type: "object", description: "Optional headers (Host, Authorization, Cookie are stripped)." }
|
|
3448
|
-
},
|
|
3449
|
-
required: ["url", "body"]
|
|
3450
|
-
},
|
|
3451
|
-
execute: async (args) => {
|
|
3452
|
-
const a = args;
|
|
3453
|
-
if (typeof a.url !== "string" || !a.url.startsWith("http")) {
|
|
3454
|
-
throw new Error("url must be an http(s) URL");
|
|
3455
|
-
}
|
|
3456
|
-
const res = await fetch(a.url, {
|
|
3457
|
-
method: "POST",
|
|
3458
|
-
headers: { "content-type": "application/json", ...sanitizeHeaders(a.headers) },
|
|
3459
|
-
body: JSON.stringify(a.body)
|
|
3460
|
-
});
|
|
3461
|
-
const body = await readCappedBody(res);
|
|
3462
|
-
return { status: res.status, headers: Object.fromEntries(res.headers), body };
|
|
3463
|
-
}
|
|
3464
|
-
}
|
|
3465
|
-
];
|
|
3466
|
-
|
|
3467
|
-
// src/lib/agent-tools/mail.ts
|
|
3468
|
-
import { execFileSync as execFileSync5 } from "child_process";
|
|
3469
|
-
function o365(args) {
|
|
3470
|
-
try {
|
|
3471
|
-
return execFileSync5("o365-cli", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
3472
|
-
} catch (err) {
|
|
3473
|
-
const e = err;
|
|
3474
|
-
if (e.code === "ENOENT") {
|
|
3475
|
-
throw new Error("o365-cli is not installed on this agent host");
|
|
3476
|
-
}
|
|
3477
|
-
const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString("utf8");
|
|
3478
|
-
throw new Error(`o365-cli failed: ${stderr ?? e.message ?? err}`);
|
|
3479
|
-
}
|
|
3480
|
-
}
|
|
3481
|
-
var mailTools = [
|
|
3482
|
-
{
|
|
3483
|
-
name: "mail.list",
|
|
3484
|
-
description: "List recent inbox messages via o365-cli. Optional `unread_only` and `limit`.",
|
|
3485
|
-
parameters: {
|
|
3486
|
-
type: "object",
|
|
3487
|
-
properties: {
|
|
3488
|
-
limit: { type: "integer", minimum: 1, maximum: 100, default: 20 },
|
|
3489
|
-
unread_only: { type: "boolean", default: false }
|
|
3490
|
-
},
|
|
3491
|
-
required: []
|
|
3492
|
-
},
|
|
3493
|
-
execute: async (args) => {
|
|
3494
|
-
const a = args ?? {};
|
|
3495
|
-
const argv = ["mail", "list", "--json", "--limit", String(a.limit ?? 20)];
|
|
3496
|
-
if (a.unread_only) argv.push("--unread");
|
|
3497
|
-
const out = o365(argv);
|
|
3498
|
-
try {
|
|
3499
|
-
return JSON.parse(out);
|
|
3500
|
-
} catch {
|
|
3501
|
-
return { raw: out };
|
|
3502
|
-
}
|
|
3503
|
-
}
|
|
3504
|
-
},
|
|
3505
|
-
{
|
|
3506
|
-
name: "mail.search",
|
|
3507
|
-
description: "Search the inbox via o365-cli using a free-form query string.",
|
|
3508
|
-
parameters: {
|
|
3509
|
-
type: "object",
|
|
3510
|
-
properties: {
|
|
3511
|
-
q: { type: "string" },
|
|
3512
|
-
limit: { type: "integer", minimum: 1, maximum: 100, default: 20 }
|
|
3513
|
-
},
|
|
3514
|
-
required: ["q"]
|
|
3515
|
-
},
|
|
3516
|
-
execute: async (args) => {
|
|
3517
|
-
const a = args;
|
|
3518
|
-
if (typeof a.q !== "string" || a.q.length === 0) throw new Error("q is required");
|
|
3519
|
-
const argv = ["mail", "search", a.q, "--json", "--limit", String(a.limit ?? 20)];
|
|
3520
|
-
const out = o365(argv);
|
|
3521
|
-
try {
|
|
3522
|
-
return JSON.parse(out);
|
|
3523
|
-
} catch {
|
|
3524
|
-
return { raw: out };
|
|
3525
|
-
}
|
|
3526
|
-
}
|
|
3527
|
-
}
|
|
3528
|
-
];
|
|
3529
|
-
|
|
3530
|
-
// src/lib/agent-tools/tasks.ts
|
|
3531
|
-
import { execFileSync as execFileSync6 } from "child_process";
|
|
3532
|
-
function ape(args) {
|
|
3533
|
-
try {
|
|
3534
|
-
return execFileSync6("ape-tasks", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
3535
|
-
} catch (err) {
|
|
3536
|
-
const e = err;
|
|
3537
|
-
const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString("utf8");
|
|
3538
|
-
throw new Error(`ape-tasks failed: ${stderr ?? e.message ?? err}`);
|
|
3539
|
-
}
|
|
3540
|
-
}
|
|
3541
|
-
var tasksTools = [
|
|
3542
|
-
{
|
|
3543
|
-
name: "tasks.list",
|
|
3544
|
-
description: "List the owner's open ape-tasks (the user's personal task list at tasks.openape.ai).",
|
|
3545
|
-
parameters: {
|
|
3546
|
-
type: "object",
|
|
3547
|
-
properties: {
|
|
3548
|
-
status: { type: "string", enum: ["open", "doing", "done", "archived"] },
|
|
3549
|
-
team_id: { type: "string" }
|
|
3550
|
-
},
|
|
3551
|
-
required: []
|
|
3552
|
-
},
|
|
3553
|
-
execute: async (args) => {
|
|
3554
|
-
const a = args ?? {};
|
|
3555
|
-
const argv = ["list", "--json"];
|
|
3556
|
-
if (a.status) argv.push("--status", a.status);
|
|
3557
|
-
if (a.team_id) argv.push("--team", a.team_id);
|
|
3558
|
-
const out = ape(argv);
|
|
3559
|
-
try {
|
|
3560
|
-
return JSON.parse(out);
|
|
3561
|
-
} catch {
|
|
3562
|
-
return { raw: out };
|
|
3563
|
-
}
|
|
3564
|
-
}
|
|
3565
|
-
},
|
|
3566
|
-
{
|
|
3567
|
-
name: "tasks.create",
|
|
3568
|
-
description: "Create a new ape-task on the owner's task list at tasks.openape.ai.",
|
|
3569
|
-
parameters: {
|
|
3570
|
-
type: "object",
|
|
3571
|
-
properties: {
|
|
3572
|
-
title: { type: "string" },
|
|
3573
|
-
notes: { type: "string" },
|
|
3574
|
-
priority: { type: "string", enum: ["low", "med", "high"] },
|
|
3575
|
-
due_at: { type: "string", description: "ISO date or +Nh/+Nd shorthand." }
|
|
3576
|
-
},
|
|
3577
|
-
required: ["title"]
|
|
3578
|
-
},
|
|
3579
|
-
execute: async (args) => {
|
|
3580
|
-
const a = args;
|
|
3581
|
-
const argv = ["new", "--title", a.title, "--json"];
|
|
3582
|
-
if (a.notes) argv.push("--notes", a.notes);
|
|
3583
|
-
if (a.priority) argv.push("--priority", a.priority);
|
|
3584
|
-
if (a.due_at) argv.push("--due", a.due_at);
|
|
3585
|
-
const out = ape(argv);
|
|
3586
|
-
try {
|
|
3587
|
-
return JSON.parse(out);
|
|
3588
|
-
} catch {
|
|
3589
|
-
return { raw: out };
|
|
3590
|
-
}
|
|
3591
|
-
}
|
|
3592
|
-
}
|
|
3593
|
-
];
|
|
3594
|
-
|
|
3595
|
-
// src/lib/agent-tools/time.ts
|
|
3596
|
-
var timeTools = [
|
|
3597
|
-
{
|
|
3598
|
-
name: "time.now",
|
|
3599
|
-
description: "Returns the current UTC date and time as ISO 8601 plus epoch seconds. No inputs.",
|
|
3600
|
-
parameters: { type: "object", properties: {}, required: [] },
|
|
3601
|
-
execute: async () => {
|
|
3602
|
-
const now = /* @__PURE__ */ new Date();
|
|
3603
|
-
return {
|
|
3604
|
-
iso: now.toISOString(),
|
|
3605
|
-
epoch_seconds: Math.floor(now.getTime() / 1e3),
|
|
3606
|
-
timezone_offset_minutes: -now.getTimezoneOffset()
|
|
3607
|
-
};
|
|
3608
|
-
}
|
|
3609
|
-
}
|
|
3610
|
-
];
|
|
3611
|
-
|
|
3612
|
-
// src/lib/agent-tools/index.ts
|
|
3613
|
-
var ALL_TOOLS = [
|
|
3614
|
-
...timeTools,
|
|
3615
|
-
...httpTools,
|
|
3616
|
-
...fileTools,
|
|
3617
|
-
...tasksTools,
|
|
3618
|
-
...mailTools
|
|
3619
|
-
];
|
|
3620
|
-
var TOOLS = Object.fromEntries(
|
|
3621
|
-
ALL_TOOLS.map((t) => [t.name, t])
|
|
3622
|
-
);
|
|
3623
|
-
function taskTools(names) {
|
|
3624
|
-
const out = [];
|
|
3625
|
-
const missing = [];
|
|
3626
|
-
for (const name of names) {
|
|
3627
|
-
const tool = TOOLS[name];
|
|
3628
|
-
if (!tool) missing.push(name);
|
|
3629
|
-
else out.push(tool);
|
|
3630
|
-
}
|
|
3631
|
-
if (missing.length > 0) {
|
|
3632
|
-
throw new Error(`unknown tool(s): ${missing.join(", ")}`);
|
|
3633
|
-
}
|
|
3634
|
-
return out;
|
|
3635
|
-
}
|
|
3636
|
-
function asOpenAiTools(tools) {
|
|
3637
|
-
return tools.map((t) => ({
|
|
3638
|
-
type: "function",
|
|
3639
|
-
function: { name: wireToolName(t.name), description: t.description, parameters: t.parameters }
|
|
3640
|
-
}));
|
|
3641
|
-
}
|
|
3642
|
-
function wireToolName(local) {
|
|
3643
|
-
return local.replace(/\./g, "_");
|
|
3644
|
-
}
|
|
3645
|
-
function localToolName(wire) {
|
|
3646
|
-
for (const t of Object.values(TOOLS)) {
|
|
3647
|
-
if (wireToolName(t.name) === wire) return t.name;
|
|
3648
|
-
}
|
|
3649
|
-
return wire;
|
|
3650
|
-
}
|
|
3651
|
-
|
|
3652
|
-
// src/lib/agent-runtime.ts
|
|
3653
|
-
function previewJson(value, max = 500) {
|
|
3654
|
-
let s;
|
|
3655
|
-
try {
|
|
3656
|
-
s = JSON.stringify(value);
|
|
3657
|
-
} catch {
|
|
3658
|
-
s = String(value);
|
|
3659
|
-
}
|
|
3660
|
-
return s.length > max ? `${s.slice(0, max)}\u2026` : s;
|
|
3661
|
-
}
|
|
3662
|
-
async function runLoop(opts) {
|
|
3663
|
-
const fetchFn = opts.fetchImpl ?? fetch;
|
|
3664
|
-
const trace = [];
|
|
3665
|
-
const messages = [
|
|
3666
|
-
{ role: "system", content: opts.systemPrompt },
|
|
3667
|
-
...opts.history ?? [],
|
|
3668
|
-
{ role: "user", content: opts.userMessage }
|
|
3669
|
-
];
|
|
3670
|
-
const tools = asOpenAiTools(opts.tools);
|
|
3671
|
-
for (let step = 1; step <= opts.maxSteps; step++) {
|
|
3672
|
-
const res = await fetchFn(`${opts.config.apiBase}/chat/completions`, {
|
|
3673
|
-
method: "POST",
|
|
3674
|
-
headers: {
|
|
3675
|
-
"authorization": `Bearer ${opts.config.apiKey}`,
|
|
3676
|
-
"content-type": "application/json"
|
|
3677
|
-
},
|
|
3678
|
-
body: JSON.stringify({
|
|
3679
|
-
model: opts.config.model,
|
|
3680
|
-
messages,
|
|
3681
|
-
...tools.length > 0 ? { tools, tool_choice: "auto" } : {}
|
|
3682
|
-
})
|
|
3683
|
-
});
|
|
3684
|
-
if (!res.ok) {
|
|
3685
|
-
const text = await res.text().catch(() => "");
|
|
3686
|
-
throw new Error(`LiteLLM ${res.status}: ${text.slice(0, 500)}`);
|
|
3687
|
-
}
|
|
3688
|
-
const data = await res.json();
|
|
3689
|
-
const choice = data.choices?.[0];
|
|
3690
|
-
if (!choice) throw new Error("LiteLLM response had no choices");
|
|
3691
|
-
const assistant = choice.message;
|
|
3692
|
-
messages.push(assistant);
|
|
3693
|
-
if (assistant.content) opts.handlers?.onTextDelta?.(assistant.content);
|
|
3694
|
-
trace.push({
|
|
3695
|
-
step,
|
|
3696
|
-
type: "assistant",
|
|
3697
|
-
preview: previewJson({ content: assistant.content, tool_calls: assistant.tool_calls?.length ?? 0 })
|
|
3698
|
-
});
|
|
3699
|
-
if (!assistant.tool_calls || assistant.tool_calls.length === 0) {
|
|
3700
|
-
const result2 = {
|
|
3701
|
-
status: "ok",
|
|
3702
|
-
finalMessage: assistant.content,
|
|
3703
|
-
stepCount: step,
|
|
3704
|
-
trace
|
|
3705
|
-
};
|
|
3706
|
-
opts.handlers?.onDone?.(result2);
|
|
3707
|
-
return result2;
|
|
3708
|
-
}
|
|
3709
|
-
for (const call of assistant.tool_calls) {
|
|
3710
|
-
const wireName = call.function.name;
|
|
3711
|
-
const localName = localToolName(wireName);
|
|
3712
|
-
const tool = opts.tools.find((t) => t.name === localName);
|
|
3713
|
-
let parsedArgs;
|
|
3714
|
-
try {
|
|
3715
|
-
parsedArgs = JSON.parse(call.function.arguments);
|
|
3716
|
-
} catch {
|
|
3717
|
-
parsedArgs = {};
|
|
3718
|
-
}
|
|
3719
|
-
opts.handlers?.onToolCall?.({ name: localName, args: parsedArgs });
|
|
3720
|
-
trace.push({ step, type: "tool_call", tool: localName, preview: previewJson(parsedArgs) });
|
|
3721
|
-
let result2;
|
|
3722
|
-
let isError = false;
|
|
3723
|
-
if (!tool) {
|
|
3724
|
-
result2 = `unknown tool: ${localName}`;
|
|
3725
|
-
isError = true;
|
|
3726
|
-
} else {
|
|
3727
|
-
try {
|
|
3728
|
-
result2 = await tool.execute(parsedArgs);
|
|
3729
|
-
} catch (err) {
|
|
3730
|
-
result2 = err?.message ?? String(err);
|
|
3731
|
-
isError = true;
|
|
3732
|
-
}
|
|
3733
|
-
}
|
|
3734
|
-
if (isError) {
|
|
3735
|
-
opts.handlers?.onToolError?.({ name: localName, error: String(result2) });
|
|
3736
|
-
trace.push({ step, type: "tool_error", tool: localName, preview: previewJson(result2) });
|
|
3737
|
-
} else {
|
|
3738
|
-
opts.handlers?.onToolResult?.({ name: localName, result: result2 });
|
|
3739
|
-
trace.push({ step, type: "tool_result", tool: localName, preview: previewJson(result2) });
|
|
3740
|
-
}
|
|
3741
|
-
messages.push({
|
|
3742
|
-
role: "tool",
|
|
3743
|
-
tool_call_id: call.id,
|
|
3744
|
-
name: wireToolName(localName),
|
|
3745
|
-
content: typeof result2 === "string" ? result2 : JSON.stringify(result2)
|
|
3746
|
-
});
|
|
3747
|
-
}
|
|
3748
|
-
}
|
|
3749
|
-
const result = {
|
|
3750
|
-
status: "error",
|
|
3751
|
-
finalMessage: `max_steps (${opts.maxSteps}) reached without completion`,
|
|
3752
|
-
stepCount: opts.maxSteps,
|
|
3753
|
-
trace
|
|
3754
|
-
};
|
|
3755
|
-
opts.handlers?.onDone?.(result);
|
|
3756
|
-
return result;
|
|
3757
|
-
}
|
|
3758
|
-
var RPC_SESSION_TTL_MS = 60 * 60 * 1e3;
|
|
3759
|
-
var RpcSessionMap = class {
|
|
3760
|
-
sessions = /* @__PURE__ */ new Map();
|
|
3761
|
-
get(id) {
|
|
3762
|
-
const s = this.sessions.get(id);
|
|
3763
|
-
if (s) s.lastTouched = Date.now();
|
|
3764
|
-
return s;
|
|
3765
|
-
}
|
|
3766
|
-
put(id, s) {
|
|
3767
|
-
s.lastTouched = Date.now();
|
|
3768
|
-
this.sessions.set(id, s);
|
|
3769
|
-
}
|
|
3770
|
-
evictStale() {
|
|
3771
|
-
const cutoff = Date.now() - RPC_SESSION_TTL_MS;
|
|
3772
|
-
for (const [k, v] of this.sessions) {
|
|
3773
|
-
if (v.lastTouched < cutoff) this.sessions.delete(k);
|
|
3774
|
-
}
|
|
3775
|
-
}
|
|
3776
|
-
size() {
|
|
3777
|
-
return this.sessions.size;
|
|
3778
|
-
}
|
|
3779
|
-
};
|
|
3780
|
-
|
|
3781
3288
|
// src/lib/troop-client.ts
|
|
3782
3289
|
var DEFAULT_TROOP_URL = "https://troop.openape.ai";
|
|
3783
3290
|
var TroopClient = class {
|
|
@@ -3836,13 +3343,13 @@ function resolveTroopUrl(override) {
|
|
|
3836
3343
|
}
|
|
3837
3344
|
|
|
3838
3345
|
// src/commands/agents/run.ts
|
|
3839
|
-
var AUTH_PATH = join3(
|
|
3840
|
-
var TASK_CACHE_DIR = join3(
|
|
3346
|
+
var AUTH_PATH = join3(homedir4(), ".config", "apes", "auth.json");
|
|
3347
|
+
var TASK_CACHE_DIR = join3(homedir4(), ".openape", "agent", "tasks");
|
|
3841
3348
|
function readAuth() {
|
|
3842
3349
|
if (!existsSync5(AUTH_PATH)) {
|
|
3843
3350
|
throw new CliError(`No agent auth found at ${AUTH_PATH}. Run \`apes agents spawn <name>\` first.`);
|
|
3844
3351
|
}
|
|
3845
|
-
const parsed = JSON.parse(
|
|
3352
|
+
const parsed = JSON.parse(readFileSync4(AUTH_PATH, "utf8"));
|
|
3846
3353
|
if (!parsed.access_token) throw new CliError("auth.json missing access_token");
|
|
3847
3354
|
return parsed;
|
|
3848
3355
|
}
|
|
@@ -3882,22 +3389,22 @@ function readTaskSpec(taskId) {
|
|
|
3882
3389
|
if (!existsSync5(path2)) {
|
|
3883
3390
|
throw new CliError(`No cached task spec at ${path2}. Run \`apes agents sync\` first to pull the task list from troop.`);
|
|
3884
3391
|
}
|
|
3885
|
-
return JSON.parse(
|
|
3392
|
+
return JSON.parse(readFileSync4(path2, "utf8"));
|
|
3886
3393
|
}
|
|
3887
|
-
var AGENT_CONFIG_PATH = join3(
|
|
3394
|
+
var AGENT_CONFIG_PATH = join3(homedir4(), ".openape", "agent", "agent.json");
|
|
3888
3395
|
function readAgentConfig() {
|
|
3889
3396
|
if (!existsSync5(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
|
|
3890
3397
|
try {
|
|
3891
|
-
return JSON.parse(
|
|
3398
|
+
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf8"));
|
|
3892
3399
|
} catch {
|
|
3893
3400
|
return { systemPrompt: "" };
|
|
3894
3401
|
}
|
|
3895
3402
|
}
|
|
3896
3403
|
function readLitellmConfig(model) {
|
|
3897
|
-
const envPath = join3(
|
|
3404
|
+
const envPath = join3(homedir4(), "litellm", ".env");
|
|
3898
3405
|
const env = {};
|
|
3899
3406
|
if (existsSync5(envPath)) {
|
|
3900
|
-
for (const line of
|
|
3407
|
+
for (const line of readFileSync4(envPath, "utf8").split(/\r?\n/)) {
|
|
3901
3408
|
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
3902
3409
|
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
3903
3410
|
}
|
|
@@ -4002,17 +3509,17 @@ var runAgentCommand = defineCommand24({
|
|
|
4002
3509
|
});
|
|
4003
3510
|
|
|
4004
3511
|
// src/commands/agents/serve.ts
|
|
4005
|
-
import { existsSync as existsSync6, readFileSync as
|
|
4006
|
-
import { homedir as
|
|
3512
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
3513
|
+
import { homedir as homedir5 } from "os";
|
|
4007
3514
|
import { join as join4 } from "path";
|
|
4008
3515
|
import { createInterface } from "readline";
|
|
4009
3516
|
import { defineCommand as defineCommand25 } from "citty";
|
|
4010
|
-
var AUTH_PATH2 = join4(
|
|
3517
|
+
var AUTH_PATH2 = join4(homedir5(), ".config", "apes", "auth.json");
|
|
4011
3518
|
function readLitellmConfig2(model) {
|
|
4012
|
-
const envPath = join4(
|
|
3519
|
+
const envPath = join4(homedir5(), "litellm", ".env");
|
|
4013
3520
|
const env = {};
|
|
4014
3521
|
if (existsSync6(envPath)) {
|
|
4015
|
-
for (const line of
|
|
3522
|
+
for (const line of readFileSync5(envPath, "utf8").split(/\r?\n/)) {
|
|
4016
3523
|
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
4017
3524
|
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
4018
3525
|
}
|
|
@@ -4046,7 +3553,7 @@ var serveAgentCommand = defineCommand25({
|
|
|
4046
3553
|
}
|
|
4047
3554
|
if (existsSync6(AUTH_PATH2)) {
|
|
4048
3555
|
try {
|
|
4049
|
-
JSON.parse(
|
|
3556
|
+
JSON.parse(readFileSync5(AUTH_PATH2, "utf8"));
|
|
4050
3557
|
} catch {
|
|
4051
3558
|
}
|
|
4052
3559
|
}
|
|
@@ -4121,8 +3628,8 @@ async function handleInbound(msg, sessions) {
|
|
|
4121
3628
|
}
|
|
4122
3629
|
|
|
4123
3630
|
// src/commands/agents/spawn.ts
|
|
4124
|
-
import { execFileSync as
|
|
4125
|
-
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as
|
|
3631
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
3632
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
4126
3633
|
import { tmpdir as tmpdir2 } from "os";
|
|
4127
3634
|
import { join as join6 } from "path";
|
|
4128
3635
|
import { defineCommand as defineCommand26 } from "citty";
|
|
@@ -4182,12 +3689,12 @@ ${envBlock} <key>StartInterval</key>
|
|
|
4182
3689
|
|
|
4183
3690
|
// src/lib/keygen.ts
|
|
4184
3691
|
import { Buffer as Buffer4 } from "buffer";
|
|
4185
|
-
import { existsSync as existsSync7, mkdirSync
|
|
3692
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
4186
3693
|
import { generateKeyPairSync } from "crypto";
|
|
4187
|
-
import { homedir as
|
|
4188
|
-
import { dirname
|
|
3694
|
+
import { homedir as homedir6 } from "os";
|
|
3695
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
4189
3696
|
function resolveKeyPath(p2) {
|
|
4190
|
-
return
|
|
3697
|
+
return resolve2(p2.replace(/^~/, homedir6()));
|
|
4191
3698
|
}
|
|
4192
3699
|
function buildSshEd25519Line(rawPub) {
|
|
4193
3700
|
const keyTypeStr = "ssh-ed25519";
|
|
@@ -4201,9 +3708,9 @@ function buildSshEd25519Line(rawPub) {
|
|
|
4201
3708
|
function readPublicKey(keyPath) {
|
|
4202
3709
|
const pubPath = `${keyPath}.pub`;
|
|
4203
3710
|
if (existsSync7(pubPath)) {
|
|
4204
|
-
return
|
|
3711
|
+
return readFileSync6(pubPath, "utf-8").trim();
|
|
4205
3712
|
}
|
|
4206
|
-
const keyContent =
|
|
3713
|
+
const keyContent = readFileSync6(keyPath, "utf-8");
|
|
4207
3714
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
4208
3715
|
const jwk = privateKey.export({ format: "jwk" });
|
|
4209
3716
|
const pubBytes = Buffer4.from(jwk.x, "base64url");
|
|
@@ -4211,17 +3718,17 @@ function readPublicKey(keyPath) {
|
|
|
4211
3718
|
}
|
|
4212
3719
|
function generateAndSaveKey(keyPath) {
|
|
4213
3720
|
const resolved = resolveKeyPath(keyPath);
|
|
4214
|
-
const dir =
|
|
3721
|
+
const dir = dirname(resolved);
|
|
4215
3722
|
if (!existsSync7(dir)) {
|
|
4216
|
-
|
|
3723
|
+
mkdirSync(dir, { recursive: true });
|
|
4217
3724
|
}
|
|
4218
3725
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
4219
3726
|
const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
|
|
4220
|
-
|
|
3727
|
+
writeFileSync2(resolved, privatePem, { mode: 384 });
|
|
4221
3728
|
const jwk = publicKey.export({ format: "jwk" });
|
|
4222
3729
|
const pubBytes = Buffer4.from(jwk.x, "base64url");
|
|
4223
3730
|
const pubKeyStr = buildSshEd25519Line(pubBytes);
|
|
4224
|
-
|
|
3731
|
+
writeFileSync2(`${resolved}.pub`, `${pubKeyStr}
|
|
4225
3732
|
`, { mode: 420 });
|
|
4226
3733
|
return pubKeyStr;
|
|
4227
3734
|
}
|
|
@@ -4237,15 +3744,15 @@ function generateKeyPairInMemory() {
|
|
|
4237
3744
|
}
|
|
4238
3745
|
|
|
4239
3746
|
// src/lib/llm-bridge.ts
|
|
4240
|
-
import { execFileSync as
|
|
4241
|
-
import { existsSync as existsSync8, readFileSync as
|
|
4242
|
-
import { homedir as
|
|
4243
|
-
import { dirname as
|
|
3747
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
3748
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
3749
|
+
import { homedir as homedir7 } from "os";
|
|
3750
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
4244
3751
|
var PLIST_LABEL_PREFIX = "eco.hofmann.apes.bridge";
|
|
4245
|
-
function readLitellmEnv(envPath = join5(
|
|
3752
|
+
function readLitellmEnv(envPath = join5(homedir7(), "litellm", ".env")) {
|
|
4246
3753
|
if (!existsSync8(envPath)) return null;
|
|
4247
3754
|
try {
|
|
4248
|
-
const text =
|
|
3755
|
+
const text = readFileSync7(envPath, "utf8");
|
|
4249
3756
|
const out = {};
|
|
4250
3757
|
for (const line of text.split("\n")) {
|
|
4251
3758
|
const trimmed = line.trim();
|
|
@@ -4281,12 +3788,12 @@ function captureHostBinDirs() {
|
|
|
4281
3788
|
for (const bin of ["node", "openape-chat-bridge", "apes"]) {
|
|
4282
3789
|
let resolved;
|
|
4283
3790
|
try {
|
|
4284
|
-
resolved =
|
|
3791
|
+
resolved = execFileSync5("/usr/bin/which", [bin], { encoding: "utf8" }).trim();
|
|
4285
3792
|
} catch {
|
|
4286
3793
|
const installCmd = bin === "openape-chat-bridge" ? "npm i -g @openape/chat-bridge" : bin === "apes" ? "npm i -g @openape/apes" : "install Node.js (e.g. brew install node)";
|
|
4287
3794
|
throw new Error(`'${bin}' not found on host PATH. ${installCmd} before spawning agents \u2014 the bridge runtime resolves these at spawn time and bakes the dir into the agent's launchd plist.`);
|
|
4288
3795
|
}
|
|
4289
|
-
const dir =
|
|
3796
|
+
const dir = dirname2(resolved);
|
|
4290
3797
|
if (!seen.has(dir)) {
|
|
4291
3798
|
seen.add(dir);
|
|
4292
3799
|
dirs.push(dir);
|
|
@@ -4532,10 +4039,10 @@ and try again.`
|
|
|
4532
4039
|
bridge,
|
|
4533
4040
|
troop
|
|
4534
4041
|
});
|
|
4535
|
-
|
|
4042
|
+
writeFileSync3(scriptPath, script, { mode: 448 });
|
|
4536
4043
|
consola23.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
|
|
4537
4044
|
consola23.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
|
|
4538
|
-
|
|
4045
|
+
execFileSync6(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
|
|
4539
4046
|
consola23.success(`Agent ${name} spawned.`);
|
|
4540
4047
|
consola23.info(`\u{1F517} Troop: https://troop.openape.ai/agents/${name}`);
|
|
4541
4048
|
if (args.bridge) {
|
|
@@ -4574,18 +4081,18 @@ async function resolveClaudeToken(opts) {
|
|
|
4574
4081
|
}
|
|
4575
4082
|
|
|
4576
4083
|
// src/commands/agents/sync.ts
|
|
4577
|
-
import { chownSync, existsSync as existsSync9, mkdirSync as
|
|
4578
|
-
import { homedir as
|
|
4084
|
+
import { chownSync, existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, statSync, writeFileSync as writeFileSync4 } from "fs";
|
|
4085
|
+
import { homedir as homedir8 } from "os";
|
|
4579
4086
|
import { join as join7 } from "path";
|
|
4580
4087
|
import { defineCommand as defineCommand27 } from "citty";
|
|
4581
4088
|
import consola24 from "consola";
|
|
4582
4089
|
|
|
4583
4090
|
// src/lib/macos-host.ts
|
|
4584
|
-
import { execFileSync as
|
|
4091
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
4585
4092
|
import { hostname as hostname3 } from "os";
|
|
4586
4093
|
function getHostId() {
|
|
4587
4094
|
try {
|
|
4588
|
-
const output =
|
|
4095
|
+
const output = execFileSync7(
|
|
4589
4096
|
"/usr/sbin/ioreg",
|
|
4590
4097
|
["-d2", "-c", "IOPlatformExpertDevice"],
|
|
4591
4098
|
{ encoding: "utf8", timeout: 2e3 }
|
|
@@ -4605,15 +4112,15 @@ function getHostname() {
|
|
|
4605
4112
|
}
|
|
4606
4113
|
|
|
4607
4114
|
// src/commands/agents/sync.ts
|
|
4608
|
-
var AUTH_PATH3 = join7(
|
|
4609
|
-
var TASK_CACHE_DIR2 = join7(
|
|
4115
|
+
var AUTH_PATH3 = join7(homedir8(), ".config", "apes", "auth.json");
|
|
4116
|
+
var TASK_CACHE_DIR2 = join7(homedir8(), ".openape", "agent", "tasks");
|
|
4610
4117
|
function readAuthJson() {
|
|
4611
4118
|
if (!existsSync9(AUTH_PATH3)) {
|
|
4612
4119
|
throw new CliError(
|
|
4613
4120
|
`No agent auth found at ${AUTH_PATH3}. Run \`apes agents spawn <name>\` to provision an agent first.`
|
|
4614
4121
|
);
|
|
4615
4122
|
}
|
|
4616
|
-
const raw =
|
|
4123
|
+
const raw = readFileSync8(AUTH_PATH3, "utf8");
|
|
4617
4124
|
let parsed;
|
|
4618
4125
|
try {
|
|
4619
4126
|
parsed = JSON.parse(raw);
|
|
@@ -4674,7 +4181,7 @@ var syncAgentCommand = defineCommand27({
|
|
|
4674
4181
|
let agentGid = null;
|
|
4675
4182
|
if (process.geteuid?.() === 0) {
|
|
4676
4183
|
try {
|
|
4677
|
-
const homeStat = statSync(
|
|
4184
|
+
const homeStat = statSync(homedir8());
|
|
4678
4185
|
agentUid = homeStat.uid;
|
|
4679
4186
|
agentGid = homeStat.gid;
|
|
4680
4187
|
} catch {
|
|
@@ -4688,23 +4195,23 @@ var syncAgentCommand = defineCommand27({
|
|
|
4688
4195
|
}
|
|
4689
4196
|
}
|
|
4690
4197
|
}
|
|
4691
|
-
const agentDir = join7(
|
|
4692
|
-
|
|
4693
|
-
chownToAgent(join7(
|
|
4198
|
+
const agentDir = join7(homedir8(), ".openape", "agent");
|
|
4199
|
+
mkdirSync2(agentDir, { recursive: true });
|
|
4200
|
+
chownToAgent(join7(homedir8(), ".openape"));
|
|
4694
4201
|
chownToAgent(agentDir);
|
|
4695
4202
|
const agentJsonPath = join7(agentDir, "agent.json");
|
|
4696
|
-
|
|
4203
|
+
writeFileSync4(
|
|
4697
4204
|
agentJsonPath,
|
|
4698
4205
|
`${JSON.stringify({ systemPrompt }, null, 2)}
|
|
4699
4206
|
`,
|
|
4700
4207
|
{ mode: 384 }
|
|
4701
4208
|
);
|
|
4702
4209
|
chownToAgent(agentJsonPath);
|
|
4703
|
-
|
|
4210
|
+
mkdirSync2(TASK_CACHE_DIR2, { recursive: true });
|
|
4704
4211
|
chownToAgent(TASK_CACHE_DIR2);
|
|
4705
4212
|
for (const task of tasks) {
|
|
4706
4213
|
const path2 = join7(TASK_CACHE_DIR2, `${task.taskId}.json`);
|
|
4707
|
-
|
|
4214
|
+
writeFileSync4(path2, `${JSON.stringify(task, null, 2)}
|
|
4708
4215
|
`, { mode: 384 });
|
|
4709
4216
|
chownToAgent(path2);
|
|
4710
4217
|
}
|
|
@@ -4731,22 +4238,22 @@ var agentsCommand = defineCommand28({
|
|
|
4731
4238
|
});
|
|
4732
4239
|
|
|
4733
4240
|
// src/commands/nest/index.ts
|
|
4734
|
-
import { defineCommand as
|
|
4241
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
4735
4242
|
|
|
4736
4243
|
// src/commands/nest/authorize.ts
|
|
4737
|
-
import { execFileSync as
|
|
4738
|
-
import { existsSync as existsSync11, readFileSync as
|
|
4244
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
4245
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
4739
4246
|
import { join as join9 } from "path";
|
|
4740
4247
|
import { defineCommand as defineCommand30 } from "citty";
|
|
4741
4248
|
import consola26 from "consola";
|
|
4742
4249
|
|
|
4743
4250
|
// src/commands/nest/enroll.ts
|
|
4744
|
-
import { hostname as hostname4, homedir as
|
|
4745
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
4251
|
+
import { hostname as hostname4, homedir as homedir9 } from "os";
|
|
4252
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5, chmodSync } from "fs";
|
|
4746
4253
|
import { join as join8 } from "path";
|
|
4747
4254
|
import { defineCommand as defineCommand29 } from "citty";
|
|
4748
4255
|
import consola25 from "consola";
|
|
4749
|
-
var NEST_DATA_DIR = join8(
|
|
4256
|
+
var NEST_DATA_DIR = join8(homedir9(), ".openape", "nest");
|
|
4750
4257
|
function nestAgentName() {
|
|
4751
4258
|
const raw = hostname4().toLowerCase();
|
|
4752
4259
|
const head = raw.split(".")[0] ?? raw;
|
|
@@ -4785,13 +4292,13 @@ var enrollNestCommand = defineCommand29({
|
|
|
4785
4292
|
}
|
|
4786
4293
|
const sshDir = join8(NEST_DATA_DIR, ".ssh");
|
|
4787
4294
|
const configDir = join8(NEST_DATA_DIR, ".config", "apes");
|
|
4788
|
-
|
|
4789
|
-
|
|
4295
|
+
mkdirSync3(sshDir, { recursive: true });
|
|
4296
|
+
mkdirSync3(configDir, { recursive: true });
|
|
4790
4297
|
consola25.start(`Generating keypair for ${name}\u2026`);
|
|
4791
4298
|
const { privatePem, publicSshLine } = generateKeyPairInMemory();
|
|
4792
|
-
|
|
4299
|
+
writeFileSync5(join8(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
|
|
4793
4300
|
`, { mode: 384 });
|
|
4794
|
-
|
|
4301
|
+
writeFileSync5(join8(sshDir, "id_ed25519.pub"), `${publicSshLine}
|
|
4795
4302
|
`, { mode: 420 });
|
|
4796
4303
|
chmodSync(sshDir, 448);
|
|
4797
4304
|
consola25.start(`Registering nest at ${idp}\u2026`);
|
|
@@ -4811,7 +4318,7 @@ var enrollNestCommand = defineCommand29({
|
|
|
4811
4318
|
keyPath: join8(sshDir, "id_ed25519"),
|
|
4812
4319
|
ownerEmail: ownerAuth.email
|
|
4813
4320
|
});
|
|
4814
|
-
|
|
4321
|
+
writeFileSync5(authPath, authJson, { mode: 384 });
|
|
4815
4322
|
chmodSync(configDir, 448);
|
|
4816
4323
|
consola25.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
|
|
4817
4324
|
consola25.info("");
|
|
@@ -4873,7 +4380,7 @@ var authorizeNestCommand = defineCommand30({
|
|
|
4873
4380
|
if (!existsSync11(nestAuthPath)) {
|
|
4874
4381
|
throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
|
|
4875
4382
|
}
|
|
4876
|
-
const nestAuth = JSON.parse(
|
|
4383
|
+
const nestAuth = JSON.parse(readFileSync9(nestAuthPath, "utf8"));
|
|
4877
4384
|
if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
|
|
4878
4385
|
const allow = args.allow ?? DEFAULT_ALLOW_PATTERNS.join(",");
|
|
4879
4386
|
consola26.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
|
|
@@ -4890,7 +4397,7 @@ var authorizeNestCommand = defineCommand30({
|
|
|
4890
4397
|
cmdArgs.push("--expires-in", args["expires-in"]);
|
|
4891
4398
|
}
|
|
4892
4399
|
try {
|
|
4893
|
-
|
|
4400
|
+
execFileSync8("apes", cmdArgs, { stdio: "inherit" });
|
|
4894
4401
|
} catch (err) {
|
|
4895
4402
|
throw new CliError(err instanceof Error ? err.message : String(err));
|
|
4896
4403
|
}
|
|
@@ -4900,147 +4407,107 @@ var authorizeNestCommand = defineCommand30({
|
|
|
4900
4407
|
});
|
|
4901
4408
|
|
|
4902
4409
|
// src/commands/nest/destroy.ts
|
|
4903
|
-
import process2 from "process";
|
|
4904
4410
|
import { defineCommand as defineCommand31 } from "citty";
|
|
4905
|
-
import consola28 from "consola";
|
|
4906
|
-
|
|
4907
|
-
// src/lib/nest-grant-flow.ts
|
|
4908
|
-
import { hostname as hostname5 } from "os";
|
|
4909
4411
|
import consola27 from "consola";
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
grant_type: approval,
|
|
4943
|
-
command: opts.command,
|
|
4944
|
-
reason: opts.reason ?? opts.command.join(" ")
|
|
4945
|
-
}
|
|
4946
|
-
});
|
|
4947
|
-
let approved = grant.status === "approved";
|
|
4948
|
-
const maxWait = 15 * 60 * 1e3;
|
|
4949
|
-
const interval = 3e3;
|
|
4950
|
-
const start = Date.now();
|
|
4951
|
-
while (!approved && Date.now() - start < maxWait) {
|
|
4952
|
-
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
4953
|
-
if (status.status === "approved") {
|
|
4954
|
-
approved = true;
|
|
4955
|
-
break;
|
|
4956
|
-
}
|
|
4957
|
-
if (status.status === "denied" || status.status === "revoked") {
|
|
4958
|
-
throw new CliError(`Grant ${status.status}.`);
|
|
4959
|
-
}
|
|
4960
|
-
if (!approved) {
|
|
4961
|
-
consola27.info(`Waiting for approval: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4962
|
-
await new Promise((r3) => setTimeout(r3, interval));
|
|
4412
|
+
|
|
4413
|
+
// src/lib/nest-intent.ts
|
|
4414
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync10, statSync as statSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
|
|
4415
|
+
import { homedir as homedir10 } from "os";
|
|
4416
|
+
import { join as join10 } from "path";
|
|
4417
|
+
import { randomUUID } from "crypto";
|
|
4418
|
+
var POLL_INTERVAL_MS = 200;
|
|
4419
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4420
|
+
function resolveIntentDir() {
|
|
4421
|
+
if (process.env.OPENAPE_NEST_INTENT_DIR) return process.env.OPENAPE_NEST_INTENT_DIR;
|
|
4422
|
+
if (existsSync12("/var/openape/nest/intents")) return "/var/openape/nest/intents";
|
|
4423
|
+
return join10(homedir10(), ".openape", "nest", "intents");
|
|
4424
|
+
}
|
|
4425
|
+
async function dispatchIntent(intent, opts = {}) {
|
|
4426
|
+
const id = randomUUID();
|
|
4427
|
+
const dir = resolveIntentDir();
|
|
4428
|
+
if (!existsSync12(dir)) {
|
|
4429
|
+
throw new CliError(`Nest intent dir does not exist: ${dir}
|
|
4430
|
+
Is the nest daemon running? Try \`ps aux | grep openape-nest\`.`);
|
|
4431
|
+
}
|
|
4432
|
+
const intentPath = join10(dir, `${id}.json`);
|
|
4433
|
+
const responsePath = join10(dir, `${id}.response`);
|
|
4434
|
+
const tmpPath = `${intentPath}.tmp`;
|
|
4435
|
+
writeFileSync6(tmpPath, `${JSON.stringify({ id, ...intent })}
|
|
4436
|
+
`, { mode: 432 });
|
|
4437
|
+
try {
|
|
4438
|
+
const fs = await import("fs");
|
|
4439
|
+
fs.renameSync(tmpPath, intentPath);
|
|
4440
|
+
} catch (err) {
|
|
4441
|
+
try {
|
|
4442
|
+
unlinkSync(tmpPath);
|
|
4443
|
+
} catch {
|
|
4963
4444
|
}
|
|
4445
|
+
throw err;
|
|
4964
4446
|
}
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4447
|
+
const deadline = Date.now() + (opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
4448
|
+
while (Date.now() < deadline) {
|
|
4449
|
+
if (existsSync12(responsePath)) {
|
|
4450
|
+
let raw;
|
|
4451
|
+
try {
|
|
4452
|
+
const st = statSync2(responsePath);
|
|
4453
|
+
if (Date.now() - st.mtimeMs < 50) {
|
|
4454
|
+
await sleep(50);
|
|
4455
|
+
}
|
|
4456
|
+
raw = readFileSync10(responsePath, "utf8");
|
|
4457
|
+
} catch {
|
|
4458
|
+
await sleep(POLL_INTERVAL_MS);
|
|
4459
|
+
continue;
|
|
4460
|
+
}
|
|
4461
|
+
try {
|
|
4462
|
+
unlinkSync(responsePath);
|
|
4463
|
+
} catch {
|
|
4464
|
+
}
|
|
4465
|
+
let parsed;
|
|
4466
|
+
try {
|
|
4467
|
+
parsed = JSON.parse(raw);
|
|
4468
|
+
} catch (err) {
|
|
4469
|
+
throw new CliError(`malformed nest response: ${err instanceof Error ? err.message : String(err)}`);
|
|
4470
|
+
}
|
|
4471
|
+
if (!parsed.ok) {
|
|
4472
|
+
throw new CliError(`nest: ${parsed.error}`);
|
|
4473
|
+
}
|
|
4474
|
+
return parsed.result;
|
|
4475
|
+
}
|
|
4476
|
+
await sleep(POLL_INTERVAL_MS);
|
|
4969
4477
|
}
|
|
4970
|
-
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
4971
|
-
method: "POST"
|
|
4972
|
-
});
|
|
4973
|
-
return authz_jwt;
|
|
4974
|
-
}
|
|
4975
|
-
function nestBaseUrl(port) {
|
|
4976
|
-
const p2 = port ?? Number(process.env.OPENAPE_NEST_PORT ?? 9091);
|
|
4977
|
-
return `http://127.0.0.1:${p2}`;
|
|
4978
|
-
}
|
|
4979
|
-
async function findReusableNestGrant(opts) {
|
|
4980
4478
|
try {
|
|
4981
|
-
|
|
4982
|
-
`${opts.grantsUrl}?requester=${encodeURIComponent(opts.requester)}&status=approved&limit=50`
|
|
4983
|
-
);
|
|
4984
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
4985
|
-
const match = grants.data.find((g) => {
|
|
4986
|
-
const r3 = g.request;
|
|
4987
|
-
if (r3.audience !== NEST_AUDIENCE) return false;
|
|
4988
|
-
if (r3.target_host !== opts.targetHost) return false;
|
|
4989
|
-
if (r3.grant_type === "once") return false;
|
|
4990
|
-
if (r3.grant_type === "timed" && g.expires_at && g.expires_at <= now) return false;
|
|
4991
|
-
const cmd = r3.command ?? [];
|
|
4992
|
-
if (cmd.length !== opts.command.length) return false;
|
|
4993
|
-
return cmd.every((c2, i) => c2 === opts.command[i]);
|
|
4994
|
-
});
|
|
4995
|
-
return match?.id ?? null;
|
|
4479
|
+
unlinkSync(intentPath);
|
|
4996
4480
|
} catch {
|
|
4997
|
-
return null;
|
|
4998
4481
|
}
|
|
4482
|
+
throw new CliError(`nest intent timeout (${(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS) / 1e3}s) \u2014 Nest daemon may not be running or is stuck.`);
|
|
4483
|
+
}
|
|
4484
|
+
function sleep(ms) {
|
|
4485
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
4999
4486
|
}
|
|
5000
4487
|
|
|
5001
4488
|
// src/commands/nest/destroy.ts
|
|
5002
4489
|
var destroyNestCommand = defineCommand31({
|
|
5003
4490
|
meta: {
|
|
5004
4491
|
name: "destroy",
|
|
5005
|
-
description: "Tear down an agent on the local nest
|
|
4492
|
+
description: "Tear down an agent on the local nest. Drops an intent file the nest daemon picks up."
|
|
5006
4493
|
},
|
|
5007
4494
|
args: {
|
|
5008
|
-
name: { type: "positional", required: true, description: "Agent name to destroy" }
|
|
5009
|
-
port: { type: "string", description: "Override nest port (default: 9091)" }
|
|
4495
|
+
name: { type: "positional", required: true, description: "Agent name to destroy" }
|
|
5010
4496
|
},
|
|
5011
4497
|
async run({ args }) {
|
|
5012
4498
|
const name = String(args.name);
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
try {
|
|
5016
|
-
const res = await fetch(`${base}/agents/${encodeURIComponent(name)}`, {
|
|
5017
|
-
method: "DELETE",
|
|
5018
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
5019
|
-
});
|
|
5020
|
-
if (!res.ok) {
|
|
5021
|
-
const text = await res.text().catch(() => "");
|
|
5022
|
-
throw new CliError(`nest DELETE /agents/${name} failed: ${res.status} ${text}`);
|
|
5023
|
-
}
|
|
5024
|
-
} catch (err) {
|
|
5025
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
5026
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5027
|
-
consola28.error(`Nest daemon is not running at ${base}`);
|
|
5028
|
-
consola28.info(" Run: apes nest install");
|
|
5029
|
-
process2.exit(2);
|
|
5030
|
-
}
|
|
5031
|
-
throw err;
|
|
5032
|
-
}
|
|
5033
|
-
consola28.success(`Destroyed ${name}`);
|
|
4499
|
+
await dispatchIntent({ action: "destroy", name });
|
|
4500
|
+
consola27.success(`Destroyed ${name}`);
|
|
5034
4501
|
}
|
|
5035
4502
|
});
|
|
5036
4503
|
|
|
5037
4504
|
// src/commands/nest/install.ts
|
|
5038
|
-
import { execFileSync as
|
|
5039
|
-
import { existsSync as
|
|
4505
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
4506
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
5040
4507
|
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
5041
|
-
import { dirname as
|
|
4508
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
5042
4509
|
import { defineCommand as defineCommand32 } from "citty";
|
|
5043
|
-
import
|
|
4510
|
+
import consola28 from "consola";
|
|
5044
4511
|
|
|
5045
4512
|
// src/commands/nest/apes-agents-adapter.ts
|
|
5046
4513
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -5107,13 +4574,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
5107
4574
|
// src/commands/nest/install.ts
|
|
5108
4575
|
var PLIST_LABEL = "ai.openape.nest";
|
|
5109
4576
|
function plistPath() {
|
|
5110
|
-
return
|
|
4577
|
+
return join11(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
5111
4578
|
}
|
|
5112
4579
|
function escape2(s) {
|
|
5113
4580
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
5114
4581
|
}
|
|
5115
4582
|
function buildPlist(args) {
|
|
5116
|
-
const logsDir =
|
|
4583
|
+
const logsDir = join11(args.userHome, "Library", "Logs");
|
|
5117
4584
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
5118
4585
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
5119
4586
|
<plist version="1.0">
|
|
@@ -5148,8 +4615,8 @@ function buildPlist(args) {
|
|
|
5148
4615
|
`;
|
|
5149
4616
|
}
|
|
5150
4617
|
function installAdapter2() {
|
|
5151
|
-
const target =
|
|
5152
|
-
mkdirSync5(
|
|
4618
|
+
const target = join11(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4619
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
5153
4620
|
let existing = "";
|
|
5154
4621
|
try {
|
|
5155
4622
|
existing = readFileSync11(target, "utf8");
|
|
@@ -5157,15 +4624,15 @@ function installAdapter2() {
|
|
|
5157
4624
|
}
|
|
5158
4625
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
5159
4626
|
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
5160
|
-
|
|
4627
|
+
consola28.success(`Wrote shapes adapter ${target}`);
|
|
5161
4628
|
return true;
|
|
5162
4629
|
}
|
|
5163
4630
|
function writeBridgeModelDefault(model) {
|
|
5164
|
-
const envDir =
|
|
5165
|
-
const envFile =
|
|
4631
|
+
const envDir = join11(homedir11(), "litellm");
|
|
4632
|
+
const envFile = join11(envDir, ".env");
|
|
5166
4633
|
mkdirSync5(envDir, { recursive: true });
|
|
5167
4634
|
let lines = [];
|
|
5168
|
-
if (
|
|
4635
|
+
if (existsSync13(envFile)) {
|
|
5169
4636
|
lines = readFileSync11(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
|
|
5170
4637
|
}
|
|
5171
4638
|
lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
|
|
@@ -5175,13 +4642,13 @@ function writeBridgeModelDefault(model) {
|
|
|
5175
4642
|
}
|
|
5176
4643
|
function findBinary(name) {
|
|
5177
4644
|
for (const dir of [
|
|
5178
|
-
|
|
4645
|
+
join11(homedir11(), ".bun", "bin"),
|
|
5179
4646
|
"/opt/homebrew/bin",
|
|
5180
4647
|
"/usr/local/bin",
|
|
5181
4648
|
"/usr/bin"
|
|
5182
4649
|
]) {
|
|
5183
|
-
const p2 =
|
|
5184
|
-
if (
|
|
4650
|
+
const p2 = join11(dir, name);
|
|
4651
|
+
if (existsSync13(p2)) return p2;
|
|
5185
4652
|
}
|
|
5186
4653
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
5187
4654
|
}
|
|
@@ -5208,16 +4675,16 @@ var installNestCommand = defineCommand32({
|
|
|
5208
4675
|
}
|
|
5209
4676
|
const nestBin = findBinary("openape-nest");
|
|
5210
4677
|
const apesBin = findBinary("apes");
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
4678
|
+
consola28.info(`Installing nest at ${plistPath()}`);
|
|
4679
|
+
consola28.info(` nest binary: ${nestBin}`);
|
|
4680
|
+
consola28.info(` apes binary: ${apesBin}`);
|
|
4681
|
+
consola28.info(` HTTP port: ${port}`);
|
|
5215
4682
|
if (typeof args["bridge-model"] === "string" && args["bridge-model"]) {
|
|
5216
4683
|
writeBridgeModelDefault(args["bridge-model"]);
|
|
5217
|
-
|
|
4684
|
+
consola28.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
|
|
5218
4685
|
}
|
|
5219
4686
|
installAdapter2();
|
|
5220
|
-
mkdirSync5(
|
|
4687
|
+
mkdirSync5(join11(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
5221
4688
|
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
5222
4689
|
const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
|
|
5223
4690
|
let existing = "";
|
|
@@ -5227,218 +4694,126 @@ var installNestCommand = defineCommand32({
|
|
|
5227
4694
|
}
|
|
5228
4695
|
if (existing !== desired) {
|
|
5229
4696
|
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
5230
|
-
|
|
4697
|
+
consola28.success("Wrote launchd plist");
|
|
5231
4698
|
} else {
|
|
5232
|
-
|
|
4699
|
+
consola28.info("plist already up to date");
|
|
5233
4700
|
}
|
|
5234
4701
|
const uid = userInfo2().uid;
|
|
5235
4702
|
try {
|
|
5236
|
-
|
|
4703
|
+
execFileSync9("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
|
|
5237
4704
|
} catch {
|
|
5238
4705
|
}
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
4706
|
+
execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4707
|
+
consola28.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4708
|
+
consola28.info("");
|
|
4709
|
+
consola28.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
4710
|
+
consola28.info("");
|
|
4711
|
+
consola28.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
4712
|
+
consola28.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
4713
|
+
consola28.info("");
|
|
4714
|
+
consola28.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
5248
4715
|
}
|
|
5249
4716
|
});
|
|
5250
4717
|
|
|
5251
4718
|
// src/commands/nest/list.ts
|
|
5252
|
-
import process3 from "process";
|
|
5253
4719
|
import { defineCommand as defineCommand33 } from "citty";
|
|
5254
|
-
import
|
|
4720
|
+
import consola29 from "consola";
|
|
5255
4721
|
var listNestCommand = defineCommand33({
|
|
5256
4722
|
meta: {
|
|
5257
4723
|
name: "list",
|
|
5258
|
-
description: "List agents registered with the local nest.
|
|
4724
|
+
description: "List agents registered with the local nest. File-based intent."
|
|
5259
4725
|
},
|
|
5260
4726
|
args: {
|
|
5261
|
-
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
5262
4727
|
json: { type: "boolean", description: "JSON output for scripts" }
|
|
5263
4728
|
},
|
|
5264
4729
|
async run({ args }) {
|
|
5265
|
-
const
|
|
5266
|
-
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5267
|
-
let resp;
|
|
5268
|
-
try {
|
|
5269
|
-
const res = await fetch(`${base}/agents`, {
|
|
5270
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
5271
|
-
});
|
|
5272
|
-
if (!res.ok) {
|
|
5273
|
-
const text = await res.text().catch(() => "");
|
|
5274
|
-
throw new CliError(`nest GET /agents failed: ${res.status} ${text}`);
|
|
5275
|
-
}
|
|
5276
|
-
resp = await res.json();
|
|
5277
|
-
} catch (err) {
|
|
5278
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
5279
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5280
|
-
consola30.error(`Nest daemon is not running at ${base}`);
|
|
5281
|
-
consola30.info(" Run: apes nest install");
|
|
5282
|
-
process3.exit(2);
|
|
5283
|
-
}
|
|
5284
|
-
throw err;
|
|
5285
|
-
}
|
|
4730
|
+
const result = await dispatchIntent({ action: "list" });
|
|
5286
4731
|
if (args.json) {
|
|
5287
|
-
console.log(JSON.stringify(
|
|
4732
|
+
console.log(JSON.stringify(result, null, 2));
|
|
5288
4733
|
return;
|
|
5289
4734
|
}
|
|
5290
|
-
if (
|
|
5291
|
-
|
|
4735
|
+
if (result.agents.length === 0) {
|
|
4736
|
+
consola29.info("(no agents registered with this nest)");
|
|
5292
4737
|
return;
|
|
5293
4738
|
}
|
|
5294
|
-
|
|
5295
|
-
for (const a of
|
|
4739
|
+
consola29.info(`${result.agents.length} agent(s) registered with this nest:`);
|
|
4740
|
+
for (const a of result.agents) {
|
|
5296
4741
|
const bridge = a.bridge ? " bridge=on" : "";
|
|
5297
|
-
|
|
4742
|
+
consola29.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
5298
4743
|
}
|
|
5299
4744
|
}
|
|
5300
4745
|
});
|
|
5301
4746
|
|
|
5302
4747
|
// src/commands/nest/spawn.ts
|
|
5303
|
-
import process4 from "process";
|
|
5304
4748
|
import { defineCommand as defineCommand34 } from "citty";
|
|
5305
|
-
import
|
|
4749
|
+
import consola30 from "consola";
|
|
5306
4750
|
var spawnNestCommand = defineCommand34({
|
|
5307
4751
|
meta: {
|
|
5308
4752
|
name: "spawn",
|
|
5309
|
-
description: "Spawn a new agent on the local nest.
|
|
4753
|
+
description: "Spawn a new agent on the local nest. Drops an intent file the nest daemon picks up; UNIX permissions on the intents dir gate access."
|
|
5310
4754
|
},
|
|
5311
4755
|
args: {
|
|
5312
4756
|
name: { type: "positional", required: true, description: "Agent name (lowercase, [a-z0-9-], max 24 chars)" },
|
|
5313
4757
|
"no-bridge": { type: "boolean", description: "Skip installing the chat-bridge daemon (default: install it)" },
|
|
5314
4758
|
"bridge-key": { type: "string", description: "Override LITELLM_API_KEY (default: read from ~/litellm/.env)" },
|
|
5315
4759
|
"bridge-base-url": { type: "string", description: "Override LITELLM_BASE_URL (default: read from ~/litellm/.env)" },
|
|
5316
|
-
"bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" }
|
|
5317
|
-
"port": { type: "string", description: "Override nest port (default: 9091)" }
|
|
4760
|
+
"bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" }
|
|
5318
4761
|
},
|
|
5319
4762
|
async run({ args }) {
|
|
5320
4763
|
const name = String(args.name);
|
|
5321
|
-
const
|
|
5322
|
-
|
|
5323
|
-
const reqBody = {
|
|
4764
|
+
const intent = {
|
|
4765
|
+
action: "spawn",
|
|
5324
4766
|
name,
|
|
5325
4767
|
bridge: !args["no-bridge"]
|
|
5326
4768
|
};
|
|
5327
|
-
if (typeof args["bridge-key"] === "string")
|
|
5328
|
-
if (typeof args["bridge-base-url"] === "string")
|
|
5329
|
-
if (typeof args["bridge-model"] === "string")
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
const res = await fetch(`${base}/agents`, {
|
|
5333
|
-
method: "POST",
|
|
5334
|
-
headers: {
|
|
5335
|
-
"Authorization": `Bearer ${token}`,
|
|
5336
|
-
"Content-Type": "application/json"
|
|
5337
|
-
},
|
|
5338
|
-
body: JSON.stringify(reqBody)
|
|
5339
|
-
});
|
|
5340
|
-
if (!res.ok) {
|
|
5341
|
-
const text = await res.text().catch(() => "");
|
|
5342
|
-
throw new CliError(`nest POST /agents failed: ${res.status} ${text}`);
|
|
5343
|
-
}
|
|
5344
|
-
resp = await res.json();
|
|
5345
|
-
} catch (err) {
|
|
5346
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
5347
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5348
|
-
consola31.error(`Nest daemon is not running at ${base}`);
|
|
5349
|
-
consola31.info(" Run: apes nest install");
|
|
5350
|
-
process4.exit(2);
|
|
5351
|
-
}
|
|
5352
|
-
throw err;
|
|
5353
|
-
}
|
|
5354
|
-
consola31.success(`Spawned ${resp.name} (uid=${resp.uid}, home=${resp.home})`);
|
|
5355
|
-
}
|
|
5356
|
-
});
|
|
5357
|
-
|
|
5358
|
-
// src/commands/nest/status.ts
|
|
5359
|
-
import process5 from "process";
|
|
5360
|
-
import { defineCommand as defineCommand35 } from "citty";
|
|
5361
|
-
import consola32 from "consola";
|
|
5362
|
-
var statusNestCommand = defineCommand35({
|
|
5363
|
-
meta: {
|
|
5364
|
-
name: "status",
|
|
5365
|
-
description: "Print health of the local nest-daemon (agents registered). Goes through DDISA grants."
|
|
5366
|
-
},
|
|
5367
|
-
args: {
|
|
5368
|
-
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
5369
|
-
json: { type: "boolean", description: "JSON output for scripts" }
|
|
5370
|
-
},
|
|
5371
|
-
async run({ args }) {
|
|
5372
|
-
const token = await requestNestGrant({ command: ["nest", "status"] });
|
|
5373
|
-
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
5374
|
-
let status;
|
|
5375
|
-
try {
|
|
5376
|
-
const res = await fetch(`${base}/status`, {
|
|
5377
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
5378
|
-
});
|
|
5379
|
-
if (!res.ok) {
|
|
5380
|
-
const text = await res.text().catch(() => "");
|
|
5381
|
-
throw new CliError(`nest GET /status failed: ${res.status} ${text}`);
|
|
5382
|
-
}
|
|
5383
|
-
status = await res.json();
|
|
5384
|
-
} catch (err) {
|
|
5385
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
5386
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
5387
|
-
consola32.error(`Nest daemon is not running at ${base}`);
|
|
5388
|
-
consola32.info(" Run: apes nest install");
|
|
5389
|
-
process5.exit(2);
|
|
5390
|
-
}
|
|
5391
|
-
throw err;
|
|
5392
|
-
}
|
|
5393
|
-
if (args.json) {
|
|
5394
|
-
console.log(JSON.stringify(status, null, 2));
|
|
5395
|
-
return;
|
|
5396
|
-
}
|
|
5397
|
-
consola32.info(`Nest at ${base} \u2014 ${status.agents} agent(s) registered`);
|
|
4769
|
+
if (typeof args["bridge-key"] === "string") intent.bridgeKey = args["bridge-key"];
|
|
4770
|
+
if (typeof args["bridge-base-url"] === "string") intent.bridgeBaseUrl = args["bridge-base-url"];
|
|
4771
|
+
if (typeof args["bridge-model"] === "string") intent.bridgeModel = args["bridge-model"];
|
|
4772
|
+
const result = await dispatchIntent(intent);
|
|
4773
|
+
consola30.success(`Spawned ${result.name} (uid=${result.uid}, home=${result.home})`);
|
|
5398
4774
|
}
|
|
5399
4775
|
});
|
|
5400
4776
|
|
|
5401
4777
|
// src/commands/nest/uninstall.ts
|
|
5402
|
-
import { execFileSync as
|
|
5403
|
-
import { existsSync as
|
|
4778
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
4779
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync2 } from "fs";
|
|
5404
4780
|
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
5405
|
-
import { join as
|
|
5406
|
-
import { defineCommand as
|
|
5407
|
-
import
|
|
4781
|
+
import { join as join12 } from "path";
|
|
4782
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
4783
|
+
import consola31 from "consola";
|
|
5408
4784
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
5409
|
-
var uninstallNestCommand =
|
|
4785
|
+
var uninstallNestCommand = defineCommand35({
|
|
5410
4786
|
meta: {
|
|
5411
4787
|
name: "uninstall",
|
|
5412
4788
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
5413
4789
|
},
|
|
5414
4790
|
async run() {
|
|
5415
4791
|
const uid = userInfo3().uid;
|
|
5416
|
-
const path2 =
|
|
4792
|
+
const path2 = join12(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
5417
4793
|
try {
|
|
5418
|
-
|
|
5419
|
-
|
|
4794
|
+
execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4795
|
+
consola31.success("Nest daemon stopped");
|
|
5420
4796
|
} catch {
|
|
5421
|
-
|
|
4797
|
+
consola31.info("Nest daemon was not loaded");
|
|
5422
4798
|
}
|
|
5423
|
-
if (
|
|
5424
|
-
|
|
5425
|
-
|
|
4799
|
+
if (existsSync14(path2)) {
|
|
4800
|
+
unlinkSync2(path2);
|
|
4801
|
+
consola31.success(`Removed ${path2}`);
|
|
5426
4802
|
}
|
|
5427
|
-
|
|
4803
|
+
consola31.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
5428
4804
|
}
|
|
5429
4805
|
});
|
|
5430
4806
|
|
|
5431
4807
|
// src/commands/nest/index.ts
|
|
5432
|
-
var nestCommand =
|
|
4808
|
+
var nestCommand = defineCommand36({
|
|
5433
4809
|
meta: {
|
|
5434
4810
|
name: "nest",
|
|
5435
|
-
description: "Manage the local Nest control-plane daemon. One-time setup: `install`
|
|
4811
|
+
description: "Manage the local Nest control-plane daemon. One-time setup: `install` \u2192 `enroll` \u2192 `authorize`. Day-to-day: `list` / `spawn` / `destroy`. As of Phase D the Nest is a long-running CLIENT \u2014 commands talk to it via filesystem intent files in $NEST_HOME/intents (mode 770, group _openape_nest) instead of HTTP."
|
|
5436
4812
|
},
|
|
5437
4813
|
subCommands: {
|
|
5438
4814
|
install: installNestCommand,
|
|
5439
4815
|
enroll: enrollNestCommand,
|
|
5440
4816
|
authorize: authorizeNestCommand,
|
|
5441
|
-
status: statusNestCommand,
|
|
5442
4817
|
list: listNestCommand,
|
|
5443
4818
|
spawn: spawnNestCommand,
|
|
5444
4819
|
destroy: destroyNestCommand,
|
|
@@ -5447,12 +4822,12 @@ var nestCommand = defineCommand37({
|
|
|
5447
4822
|
});
|
|
5448
4823
|
|
|
5449
4824
|
// src/commands/yolo/index.ts
|
|
5450
|
-
import { defineCommand as
|
|
4825
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
5451
4826
|
|
|
5452
4827
|
// src/commands/yolo/clear.ts
|
|
5453
|
-
import { defineCommand as
|
|
5454
|
-
import
|
|
5455
|
-
var yoloClearCommand =
|
|
4828
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
4829
|
+
import consola32 from "consola";
|
|
4830
|
+
var yoloClearCommand = defineCommand37({
|
|
5456
4831
|
meta: {
|
|
5457
4832
|
name: "clear",
|
|
5458
4833
|
description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
|
|
@@ -5481,15 +4856,15 @@ var yoloClearCommand = defineCommand38({
|
|
|
5481
4856
|
const text = await res.text().catch(() => "");
|
|
5482
4857
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
5483
4858
|
}
|
|
5484
|
-
|
|
4859
|
+
consola32.success(`YOLO-policy cleared on ${email}`);
|
|
5485
4860
|
}
|
|
5486
4861
|
});
|
|
5487
4862
|
|
|
5488
4863
|
// src/commands/yolo/set.ts
|
|
5489
|
-
import { defineCommand as
|
|
5490
|
-
import
|
|
4864
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
4865
|
+
import consola33 from "consola";
|
|
5491
4866
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
5492
|
-
var yoloSetCommand =
|
|
4867
|
+
var yoloSetCommand = defineCommand38({
|
|
5493
4868
|
meta: {
|
|
5494
4869
|
name: "set",
|
|
5495
4870
|
description: "Write a YOLO-policy on a DDISA agent you own"
|
|
@@ -5537,12 +4912,12 @@ var yoloSetCommand = defineCommand39({
|
|
|
5537
4912
|
const denyPatterns = parseList(args.deny);
|
|
5538
4913
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
5539
4914
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
if (allowPatterns.length)
|
|
5543
|
-
if (denyPatterns.length)
|
|
5544
|
-
if (denyRiskThreshold)
|
|
5545
|
-
if (expiresAt)
|
|
4915
|
+
consola33.info(`Setting YOLO-policy on ${email}`);
|
|
4916
|
+
consola33.info(` mode: ${mode}`);
|
|
4917
|
+
if (allowPatterns.length) consola33.info(` allow_patterns: ${allowPatterns.join(", ")}`);
|
|
4918
|
+
if (denyPatterns.length) consola33.info(` deny_patterns: ${denyPatterns.join(", ")}`);
|
|
4919
|
+
if (denyRiskThreshold) consola33.info(` deny_risk: ${denyRiskThreshold}`);
|
|
4920
|
+
if (expiresAt) consola33.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
5546
4921
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
5547
4922
|
const res = await fetch(url, {
|
|
5548
4923
|
method: "PUT",
|
|
@@ -5562,7 +4937,7 @@ var yoloSetCommand = defineCommand39({
|
|
|
5562
4937
|
const text = await res.text().catch(() => "");
|
|
5563
4938
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
5564
4939
|
}
|
|
5565
|
-
|
|
4940
|
+
consola33.success(`YOLO-policy applied to ${email}`);
|
|
5566
4941
|
}
|
|
5567
4942
|
});
|
|
5568
4943
|
function parseList(s) {
|
|
@@ -5580,9 +4955,9 @@ function parseExpiresIn(s) {
|
|
|
5580
4955
|
}
|
|
5581
4956
|
|
|
5582
4957
|
// src/commands/yolo/show.ts
|
|
5583
|
-
import { defineCommand as
|
|
5584
|
-
import
|
|
5585
|
-
var yoloShowCommand =
|
|
4958
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
4959
|
+
import consola34 from "consola";
|
|
4960
|
+
var yoloShowCommand = defineCommand39({
|
|
5586
4961
|
meta: {
|
|
5587
4962
|
name: "show",
|
|
5588
4963
|
description: "Print the YOLO-policy currently set on a DDISA agent"
|
|
@@ -5619,17 +4994,17 @@ var yoloShowCommand = defineCommand40({
|
|
|
5619
4994
|
console.log(JSON.stringify(policy, null, 2));
|
|
5620
4995
|
return;
|
|
5621
4996
|
}
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
4997
|
+
consola34.info(`YOLO-policy for ${email}`);
|
|
4998
|
+
consola34.info(` mode: ${policy.mode}`);
|
|
4999
|
+
consola34.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
|
|
5000
|
+
consola34.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
|
|
5001
|
+
consola34.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
|
|
5002
|
+
consola34.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
|
|
5628
5003
|
}
|
|
5629
5004
|
});
|
|
5630
5005
|
|
|
5631
5006
|
// src/commands/yolo/index.ts
|
|
5632
|
-
var yoloCommand =
|
|
5007
|
+
var yoloCommand = defineCommand40({
|
|
5633
5008
|
meta: {
|
|
5634
5009
|
name: "yolo",
|
|
5635
5010
|
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)."
|
|
@@ -5642,15 +5017,15 @@ var yoloCommand = defineCommand41({
|
|
|
5642
5017
|
});
|
|
5643
5018
|
|
|
5644
5019
|
// src/commands/adapter/index.ts
|
|
5645
|
-
import { defineCommand as
|
|
5646
|
-
import
|
|
5647
|
-
var adapterCommand =
|
|
5020
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
5021
|
+
import consola35 from "consola";
|
|
5022
|
+
var adapterCommand = defineCommand41({
|
|
5648
5023
|
meta: {
|
|
5649
5024
|
name: "adapter",
|
|
5650
5025
|
description: "Manage CLI adapters"
|
|
5651
5026
|
},
|
|
5652
5027
|
subCommands: {
|
|
5653
|
-
list:
|
|
5028
|
+
list: defineCommand41({
|
|
5654
5029
|
meta: {
|
|
5655
5030
|
name: "list",
|
|
5656
5031
|
description: "List available adapters"
|
|
@@ -5681,7 +5056,7 @@ var adapterCommand = defineCommand42({
|
|
|
5681
5056
|
`);
|
|
5682
5057
|
return;
|
|
5683
5058
|
}
|
|
5684
|
-
|
|
5059
|
+
consola35.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
5685
5060
|
for (const a of index2.adapters) {
|
|
5686
5061
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
5687
5062
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -5703,7 +5078,7 @@ var adapterCommand = defineCommand42({
|
|
|
5703
5078
|
return;
|
|
5704
5079
|
}
|
|
5705
5080
|
if (local.length === 0) {
|
|
5706
|
-
|
|
5081
|
+
consola35.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
5707
5082
|
return;
|
|
5708
5083
|
}
|
|
5709
5084
|
for (const a of local) {
|
|
@@ -5711,7 +5086,7 @@ var adapterCommand = defineCommand42({
|
|
|
5711
5086
|
}
|
|
5712
5087
|
}
|
|
5713
5088
|
}),
|
|
5714
|
-
install:
|
|
5089
|
+
install: defineCommand41({
|
|
5715
5090
|
meta: {
|
|
5716
5091
|
name: "install",
|
|
5717
5092
|
description: "Install an adapter from the registry"
|
|
@@ -5740,24 +5115,24 @@ var adapterCommand = defineCommand42({
|
|
|
5740
5115
|
for (const id of ids) {
|
|
5741
5116
|
const entry = findAdapter(index, id);
|
|
5742
5117
|
if (!entry) {
|
|
5743
|
-
|
|
5118
|
+
consola35.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
5744
5119
|
continue;
|
|
5745
5120
|
}
|
|
5746
5121
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
5747
5122
|
if (conflicts.length > 0) {
|
|
5748
5123
|
for (const c2 of conflicts) {
|
|
5749
|
-
|
|
5750
|
-
|
|
5124
|
+
consola35.warn(`Conflicting adapter found: ${c2.path} (id: ${c2.adapterId}, executable: ${c2.executable})`);
|
|
5125
|
+
consola35.warn(` Remove it with: apes adapter remove ${c2.adapterId}`);
|
|
5751
5126
|
}
|
|
5752
5127
|
}
|
|
5753
5128
|
const result = await installAdapter(entry, { local });
|
|
5754
5129
|
const verb = result.updated ? "Updated" : "Installed";
|
|
5755
|
-
|
|
5756
|
-
|
|
5130
|
+
consola35.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
5131
|
+
consola35.info(`Digest: ${result.digest}`);
|
|
5757
5132
|
}
|
|
5758
5133
|
}
|
|
5759
5134
|
}),
|
|
5760
|
-
remove:
|
|
5135
|
+
remove: defineCommand41({
|
|
5761
5136
|
meta: {
|
|
5762
5137
|
name: "remove",
|
|
5763
5138
|
description: "Remove an installed adapter"
|
|
@@ -5780,9 +5155,9 @@ var adapterCommand = defineCommand42({
|
|
|
5780
5155
|
let failed = false;
|
|
5781
5156
|
for (const id of ids) {
|
|
5782
5157
|
if (removeAdapter(id, local)) {
|
|
5783
|
-
|
|
5158
|
+
consola35.success(`Removed adapter: ${id}`);
|
|
5784
5159
|
} else {
|
|
5785
|
-
|
|
5160
|
+
consola35.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
5786
5161
|
failed = true;
|
|
5787
5162
|
}
|
|
5788
5163
|
}
|
|
@@ -5790,7 +5165,7 @@ var adapterCommand = defineCommand42({
|
|
|
5790
5165
|
throw new CliError("Some adapters could not be removed");
|
|
5791
5166
|
}
|
|
5792
5167
|
}),
|
|
5793
|
-
info:
|
|
5168
|
+
info: defineCommand41({
|
|
5794
5169
|
meta: {
|
|
5795
5170
|
name: "info",
|
|
5796
5171
|
description: "Show detailed adapter information"
|
|
@@ -5832,7 +5207,7 @@ var adapterCommand = defineCommand42({
|
|
|
5832
5207
|
}
|
|
5833
5208
|
}
|
|
5834
5209
|
}),
|
|
5835
|
-
search:
|
|
5210
|
+
search: defineCommand41({
|
|
5836
5211
|
meta: {
|
|
5837
5212
|
name: "search",
|
|
5838
5213
|
description: "Search adapters in the registry"
|
|
@@ -5864,7 +5239,7 @@ var adapterCommand = defineCommand42({
|
|
|
5864
5239
|
return;
|
|
5865
5240
|
}
|
|
5866
5241
|
if (results.length === 0) {
|
|
5867
|
-
|
|
5242
|
+
consola35.info(`No adapters matching "${query}"`);
|
|
5868
5243
|
return;
|
|
5869
5244
|
}
|
|
5870
5245
|
for (const a of results) {
|
|
@@ -5873,7 +5248,7 @@ var adapterCommand = defineCommand42({
|
|
|
5873
5248
|
}
|
|
5874
5249
|
}
|
|
5875
5250
|
}),
|
|
5876
|
-
update:
|
|
5251
|
+
update: defineCommand41({
|
|
5877
5252
|
meta: {
|
|
5878
5253
|
name: "update",
|
|
5879
5254
|
description: "Update installed adapters"
|
|
@@ -5899,33 +5274,33 @@ var adapterCommand = defineCommand42({
|
|
|
5899
5274
|
const targetId = args.id ? String(args.id) : void 0;
|
|
5900
5275
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
5901
5276
|
if (targets.length === 0) {
|
|
5902
|
-
|
|
5277
|
+
consola35.info("No adapters installed to update.");
|
|
5903
5278
|
return;
|
|
5904
5279
|
}
|
|
5905
5280
|
for (const id of targets) {
|
|
5906
5281
|
const entry = findAdapter(index, id);
|
|
5907
5282
|
if (!entry) {
|
|
5908
|
-
|
|
5283
|
+
consola35.warn(`${id}: not found in registry, skipping`);
|
|
5909
5284
|
continue;
|
|
5910
5285
|
}
|
|
5911
5286
|
const localDigest = getInstalledDigest(id, false);
|
|
5912
5287
|
if (localDigest === entry.digest) {
|
|
5913
|
-
|
|
5288
|
+
consola35.info(`${id}: already up to date`);
|
|
5914
5289
|
continue;
|
|
5915
5290
|
}
|
|
5916
5291
|
if (localDigest && !args.yes) {
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5292
|
+
consola35.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
5293
|
+
consola35.info(` Old: ${localDigest}`);
|
|
5294
|
+
consola35.info(` New: ${entry.digest}`);
|
|
5295
|
+
consola35.info(" Use --yes to confirm");
|
|
5921
5296
|
continue;
|
|
5922
5297
|
}
|
|
5923
5298
|
const result = await installAdapter(entry);
|
|
5924
|
-
|
|
5299
|
+
consola35.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
5925
5300
|
}
|
|
5926
5301
|
}
|
|
5927
5302
|
}),
|
|
5928
|
-
verify:
|
|
5303
|
+
verify: defineCommand41({
|
|
5929
5304
|
meta: {
|
|
5930
5305
|
name: "verify",
|
|
5931
5306
|
description: "Verify installed adapter against registry digest"
|
|
@@ -5958,7 +5333,7 @@ var adapterCommand = defineCommand42({
|
|
|
5958
5333
|
if (!localDigest)
|
|
5959
5334
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
5960
5335
|
if (localDigest === entry.digest) {
|
|
5961
|
-
|
|
5336
|
+
consola35.success(`${id}: digest matches registry`);
|
|
5962
5337
|
} else {
|
|
5963
5338
|
console.log(` Local: ${localDigest}`);
|
|
5964
5339
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -5970,11 +5345,11 @@ var adapterCommand = defineCommand42({
|
|
|
5970
5345
|
});
|
|
5971
5346
|
|
|
5972
5347
|
// src/commands/run.ts
|
|
5973
|
-
import { execFileSync as
|
|
5974
|
-
import { hostname as
|
|
5348
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
5349
|
+
import { hostname as hostname5 } from "os";
|
|
5975
5350
|
import { basename } from "path";
|
|
5976
|
-
import { defineCommand as
|
|
5977
|
-
import
|
|
5351
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5352
|
+
import consola36 from "consola";
|
|
5978
5353
|
function shouldWaitForGrant(args) {
|
|
5979
5354
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
5980
5355
|
}
|
|
@@ -6011,7 +5386,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
6011
5386
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
6012
5387
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
6013
5388
|
if (mode === "human") {
|
|
6014
|
-
|
|
5389
|
+
consola36.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
6015
5390
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
6016
5391
|
console.log(` Check status: ${statusCmd}`);
|
|
6017
5392
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -6021,7 +5396,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
6021
5396
|
return;
|
|
6022
5397
|
}
|
|
6023
5398
|
const maxMin = getPollMaxMinutes();
|
|
6024
|
-
|
|
5399
|
+
consola36.success(`Grant ${grant.id} created (pending approval)`);
|
|
6025
5400
|
console.log(` Approve: ${approveUrl}`);
|
|
6026
5401
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
6027
5402
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -6043,7 +5418,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
6043
5418
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
6044
5419
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
6045
5420
|
}
|
|
6046
|
-
var runCommand =
|
|
5421
|
+
var runCommand = defineCommand42({
|
|
6047
5422
|
meta: {
|
|
6048
5423
|
name: "run",
|
|
6049
5424
|
description: "Execute a grant-secured command"
|
|
@@ -6132,7 +5507,7 @@ async function runShellMode(command, args) {
|
|
|
6132
5507
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
6133
5508
|
if (adapterHandled) return;
|
|
6134
5509
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
6135
|
-
const targetHost = args.host ||
|
|
5510
|
+
const targetHost = args.host || hostname5();
|
|
6136
5511
|
try {
|
|
6137
5512
|
const grants = await apiFetch(
|
|
6138
5513
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -6146,7 +5521,7 @@ async function runShellMode(command, args) {
|
|
|
6146
5521
|
}
|
|
6147
5522
|
} catch {
|
|
6148
5523
|
}
|
|
6149
|
-
|
|
5524
|
+
consola36.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
6150
5525
|
const grant = await apiFetch(grantsUrl, {
|
|
6151
5526
|
method: "POST",
|
|
6152
5527
|
body: {
|
|
@@ -6166,8 +5541,8 @@ async function runShellMode(command, args) {
|
|
|
6166
5541
|
host: targetHost
|
|
6167
5542
|
});
|
|
6168
5543
|
if (shouldWaitForGrant(args)) {
|
|
6169
|
-
|
|
6170
|
-
|
|
5544
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5545
|
+
consola36.info("Waiting for approval...");
|
|
6171
5546
|
const maxWait = 3e5;
|
|
6172
5547
|
const interval = 3e3;
|
|
6173
5548
|
const start = Date.now();
|
|
@@ -6198,13 +5573,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
6198
5573
|
try {
|
|
6199
5574
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
6200
5575
|
} catch (err) {
|
|
6201
|
-
|
|
5576
|
+
consola36.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
6202
5577
|
return false;
|
|
6203
5578
|
}
|
|
6204
5579
|
try {
|
|
6205
5580
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
6206
5581
|
if (existingGrantId) {
|
|
6207
|
-
|
|
5582
|
+
consola36.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
6208
5583
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
6209
5584
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
6210
5585
|
return true;
|
|
@@ -6212,7 +5587,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
6212
5587
|
} catch {
|
|
6213
5588
|
}
|
|
6214
5589
|
const approval = args.approval ?? "once";
|
|
6215
|
-
|
|
5590
|
+
consola36.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
6216
5591
|
const grant = await createShapesGrant(resolved, {
|
|
6217
5592
|
idp,
|
|
6218
5593
|
approval,
|
|
@@ -6220,19 +5595,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
6220
5595
|
});
|
|
6221
5596
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
6222
5597
|
const n2 = grant.similar_grants.similar_grants.length;
|
|
6223
|
-
|
|
6224
|
-
|
|
5598
|
+
consola36.info("");
|
|
5599
|
+
consola36.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
6225
5600
|
}
|
|
6226
5601
|
notifyGrantPending({
|
|
6227
5602
|
grantId: grant.id,
|
|
6228
5603
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
6229
5604
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
6230
5605
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
6231
|
-
host: args.host ||
|
|
5606
|
+
host: args.host || hostname5()
|
|
6232
5607
|
});
|
|
6233
5608
|
if (shouldWaitForGrant(args)) {
|
|
6234
|
-
|
|
6235
|
-
|
|
5609
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5610
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
6236
5611
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
6237
5612
|
if (status !== "approved")
|
|
6238
5613
|
throw new CliError(`Grant ${status}`);
|
|
@@ -6248,7 +5623,7 @@ function execShellCommand(command) {
|
|
|
6248
5623
|
throw new CliError("No command to execute");
|
|
6249
5624
|
try {
|
|
6250
5625
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
6251
|
-
|
|
5626
|
+
execFileSync11(command[0], command.slice(1), {
|
|
6252
5627
|
stdio: "inherit",
|
|
6253
5628
|
env: inheritedEnv
|
|
6254
5629
|
});
|
|
@@ -6306,7 +5681,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
6306
5681
|
try {
|
|
6307
5682
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
6308
5683
|
if (existingGrantId) {
|
|
6309
|
-
|
|
5684
|
+
consola36.info(`Reusing existing grant: ${existingGrantId}`);
|
|
6310
5685
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
6311
5686
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
6312
5687
|
return;
|
|
@@ -6320,17 +5695,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
6320
5695
|
});
|
|
6321
5696
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
6322
5697
|
const n2 = grant.similar_grants.similar_grants.length;
|
|
6323
|
-
|
|
6324
|
-
|
|
5698
|
+
consola36.info("");
|
|
5699
|
+
consola36.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
6325
5700
|
if (grant.similar_grants.widened_details?.length) {
|
|
6326
5701
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
6327
|
-
|
|
5702
|
+
consola36.info(` Broader scope: ${wider}`);
|
|
6328
5703
|
}
|
|
6329
|
-
|
|
5704
|
+
consola36.info("");
|
|
6330
5705
|
}
|
|
6331
5706
|
if (shouldWaitForGrant(args)) {
|
|
6332
|
-
|
|
6333
|
-
|
|
5707
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5708
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
6334
5709
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
6335
5710
|
if (status !== "approved")
|
|
6336
5711
|
throw new Error(`Grant ${status}`);
|
|
@@ -6349,8 +5724,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
6349
5724
|
const idp = getIdpUrl(args.idp);
|
|
6350
5725
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
6351
5726
|
const command = action.split(" ");
|
|
6352
|
-
const targetHost = args.host ||
|
|
6353
|
-
|
|
5727
|
+
const targetHost = args.host || hostname5();
|
|
5728
|
+
consola36.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
6354
5729
|
const grant = await apiFetch(grantsUrl, {
|
|
6355
5730
|
method: "POST",
|
|
6356
5731
|
body: {
|
|
@@ -6367,9 +5742,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
6367
5742
|
printPendingGrantInfo(grant, idp);
|
|
6368
5743
|
throw new CliExit(getAsyncExitCode());
|
|
6369
5744
|
}
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
5745
|
+
consola36.success(`Grant requested: ${grant.id}`);
|
|
5746
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5747
|
+
consola36.info("Waiting for approval...");
|
|
6373
5748
|
const maxWait = 15 * 60 * 1e3;
|
|
6374
5749
|
const interval = 3e3;
|
|
6375
5750
|
const start = Date.now();
|
|
@@ -6377,7 +5752,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
6377
5752
|
while (Date.now() - start < maxWait) {
|
|
6378
5753
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
6379
5754
|
if (status.status === "approved") {
|
|
6380
|
-
|
|
5755
|
+
consola36.success("Grant approved!");
|
|
6381
5756
|
approved = true;
|
|
6382
5757
|
break;
|
|
6383
5758
|
}
|
|
@@ -6392,15 +5767,15 @@ async function runAudienceMode(audience, action, args) {
|
|
|
6392
5767
|
`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.`
|
|
6393
5768
|
);
|
|
6394
5769
|
}
|
|
6395
|
-
|
|
5770
|
+
consola36.info("Fetching grant token...");
|
|
6396
5771
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
6397
5772
|
method: "POST"
|
|
6398
5773
|
});
|
|
6399
5774
|
if (audience === "escapes") {
|
|
6400
|
-
|
|
5775
|
+
consola36.info(`Executing: ${command.join(" ")}`);
|
|
6401
5776
|
try {
|
|
6402
5777
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
6403
|
-
|
|
5778
|
+
execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
6404
5779
|
stdio: "inherit",
|
|
6405
5780
|
env: inheritedEnv
|
|
6406
5781
|
});
|
|
@@ -6415,8 +5790,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
6415
5790
|
|
|
6416
5791
|
// src/commands/proxy.ts
|
|
6417
5792
|
import { spawn as spawn2 } from "child_process";
|
|
6418
|
-
import { defineCommand as
|
|
6419
|
-
import
|
|
5793
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5794
|
+
import consola37 from "consola";
|
|
6420
5795
|
|
|
6421
5796
|
// src/proxy/config.ts
|
|
6422
5797
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -6453,7 +5828,7 @@ import { spawn } from "child_process";
|
|
|
6453
5828
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
6454
5829
|
import { createRequire } from "module";
|
|
6455
5830
|
import { tmpdir as tmpdir3 } from "os";
|
|
6456
|
-
import { dirname as
|
|
5831
|
+
import { dirname as dirname4, join as join13, resolve as resolve3 } from "path";
|
|
6457
5832
|
var require2 = createRequire(import.meta.url);
|
|
6458
5833
|
function findProxyBin() {
|
|
6459
5834
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -6462,11 +5837,11 @@ function findProxyBin() {
|
|
|
6462
5837
|
if (!binRel) {
|
|
6463
5838
|
throw new Error("@openape/proxy is missing the openape-proxy bin entry");
|
|
6464
5839
|
}
|
|
6465
|
-
return
|
|
5840
|
+
return resolve3(dirname4(pkgPath), binRel);
|
|
6466
5841
|
}
|
|
6467
5842
|
async function startEphemeralProxy(configToml) {
|
|
6468
|
-
const tmpDir = mkdtempSync3(
|
|
6469
|
-
const configPath =
|
|
5843
|
+
const tmpDir = mkdtempSync3(join13(tmpdir3(), "openape-proxy-"));
|
|
5844
|
+
const configPath = join13(tmpDir, "config.toml");
|
|
6470
5845
|
writeFileSync8(configPath, configToml, { mode: 384 });
|
|
6471
5846
|
const binPath = findProxyBin();
|
|
6472
5847
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
@@ -6559,10 +5934,10 @@ function resolveProxyConfigOptions() {
|
|
|
6559
5934
|
77
|
|
6560
5935
|
);
|
|
6561
5936
|
}
|
|
6562
|
-
|
|
5937
|
+
consola37.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
6563
5938
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
6564
5939
|
}
|
|
6565
|
-
var proxyCommand =
|
|
5940
|
+
var proxyCommand = defineCommand43({
|
|
6566
5941
|
meta: {
|
|
6567
5942
|
name: "proxy",
|
|
6568
5943
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -6584,12 +5959,12 @@ var proxyCommand = defineCommand44({
|
|
|
6584
5959
|
let close = null;
|
|
6585
5960
|
if (reuseUrl) {
|
|
6586
5961
|
proxyUrl = reuseUrl;
|
|
6587
|
-
|
|
5962
|
+
consola37.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
6588
5963
|
} else {
|
|
6589
5964
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
6590
5965
|
proxyUrl = ephemeral.url;
|
|
6591
5966
|
close = ephemeral.close;
|
|
6592
|
-
|
|
5967
|
+
consola37.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
6593
5968
|
}
|
|
6594
5969
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
6595
5970
|
const childEnv = {
|
|
@@ -6621,7 +5996,7 @@ var proxyCommand = defineCommand44({
|
|
|
6621
5996
|
else resolveExit(code ?? 0);
|
|
6622
5997
|
});
|
|
6623
5998
|
child.once("error", (err) => {
|
|
6624
|
-
|
|
5999
|
+
consola37.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
6625
6000
|
resolveExit(127);
|
|
6626
6001
|
});
|
|
6627
6002
|
});
|
|
@@ -6635,8 +6010,8 @@ function signalNumber(signal) {
|
|
|
6635
6010
|
}
|
|
6636
6011
|
|
|
6637
6012
|
// src/commands/explain.ts
|
|
6638
|
-
import { defineCommand as
|
|
6639
|
-
var explainCommand =
|
|
6013
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
6014
|
+
var explainCommand = defineCommand44({
|
|
6640
6015
|
meta: {
|
|
6641
6016
|
name: "explain",
|
|
6642
6017
|
description: "Show what permission a command would need"
|
|
@@ -6674,9 +6049,9 @@ var explainCommand = defineCommand45({
|
|
|
6674
6049
|
});
|
|
6675
6050
|
|
|
6676
6051
|
// src/commands/config/get.ts
|
|
6677
|
-
import { defineCommand as
|
|
6678
|
-
import
|
|
6679
|
-
var configGetCommand =
|
|
6052
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
6053
|
+
import consola38 from "consola";
|
|
6054
|
+
var configGetCommand = defineCommand45({
|
|
6680
6055
|
meta: {
|
|
6681
6056
|
name: "get",
|
|
6682
6057
|
description: "Get a configuration value"
|
|
@@ -6696,7 +6071,7 @@ var configGetCommand = defineCommand46({
|
|
|
6696
6071
|
if (idp)
|
|
6697
6072
|
console.log(idp);
|
|
6698
6073
|
else
|
|
6699
|
-
|
|
6074
|
+
consola38.info("No IdP configured.");
|
|
6700
6075
|
break;
|
|
6701
6076
|
}
|
|
6702
6077
|
case "email": {
|
|
@@ -6704,7 +6079,7 @@ var configGetCommand = defineCommand46({
|
|
|
6704
6079
|
if (auth?.email)
|
|
6705
6080
|
console.log(auth.email);
|
|
6706
6081
|
else
|
|
6707
|
-
|
|
6082
|
+
consola38.info("Not logged in.");
|
|
6708
6083
|
break;
|
|
6709
6084
|
}
|
|
6710
6085
|
default: {
|
|
@@ -6717,7 +6092,7 @@ var configGetCommand = defineCommand46({
|
|
|
6717
6092
|
if (sectionObj && field in sectionObj) {
|
|
6718
6093
|
console.log(sectionObj[field]);
|
|
6719
6094
|
} else {
|
|
6720
|
-
|
|
6095
|
+
consola38.info(`Key "${key}" not set.`);
|
|
6721
6096
|
}
|
|
6722
6097
|
} else {
|
|
6723
6098
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -6728,9 +6103,9 @@ var configGetCommand = defineCommand46({
|
|
|
6728
6103
|
});
|
|
6729
6104
|
|
|
6730
6105
|
// src/commands/config/set.ts
|
|
6731
|
-
import { defineCommand as
|
|
6732
|
-
import
|
|
6733
|
-
var configSetCommand =
|
|
6106
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6107
|
+
import consola39 from "consola";
|
|
6108
|
+
var configSetCommand = defineCommand46({
|
|
6734
6109
|
meta: {
|
|
6735
6110
|
name: "set",
|
|
6736
6111
|
description: "Set a configuration value"
|
|
@@ -6766,12 +6141,12 @@ var configSetCommand = defineCommand47({
|
|
|
6766
6141
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
6767
6142
|
}
|
|
6768
6143
|
saveConfig(config);
|
|
6769
|
-
|
|
6144
|
+
consola39.success(`Set ${key} = ${value}`);
|
|
6770
6145
|
}
|
|
6771
6146
|
});
|
|
6772
6147
|
|
|
6773
6148
|
// src/commands/fetch/index.ts
|
|
6774
|
-
import { defineCommand as
|
|
6149
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
6775
6150
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
6776
6151
|
const token = getAuthToken();
|
|
6777
6152
|
if (!token) {
|
|
@@ -6807,13 +6182,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
6807
6182
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
6808
6183
|
}
|
|
6809
6184
|
}
|
|
6810
|
-
var fetchCommand =
|
|
6185
|
+
var fetchCommand = defineCommand47({
|
|
6811
6186
|
meta: {
|
|
6812
6187
|
name: "fetch",
|
|
6813
6188
|
description: "Make authenticated HTTP requests"
|
|
6814
6189
|
},
|
|
6815
6190
|
subCommands: {
|
|
6816
|
-
get:
|
|
6191
|
+
get: defineCommand47({
|
|
6817
6192
|
meta: {
|
|
6818
6193
|
name: "get",
|
|
6819
6194
|
description: "GET request with auth token"
|
|
@@ -6839,7 +6214,7 @@ var fetchCommand = defineCommand48({
|
|
|
6839
6214
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
6840
6215
|
}
|
|
6841
6216
|
}),
|
|
6842
|
-
post:
|
|
6217
|
+
post: defineCommand47({
|
|
6843
6218
|
meta: {
|
|
6844
6219
|
name: "post",
|
|
6845
6220
|
description: "POST request with auth token"
|
|
@@ -6878,8 +6253,8 @@ var fetchCommand = defineCommand48({
|
|
|
6878
6253
|
});
|
|
6879
6254
|
|
|
6880
6255
|
// src/commands/mcp/index.ts
|
|
6881
|
-
import { defineCommand as
|
|
6882
|
-
var mcpCommand =
|
|
6256
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
6257
|
+
var mcpCommand = defineCommand48({
|
|
6883
6258
|
meta: {
|
|
6884
6259
|
name: "mcp",
|
|
6885
6260
|
description: "Start MCP server for AI agents"
|
|
@@ -6902,48 +6277,48 @@ var mcpCommand = defineCommand49({
|
|
|
6902
6277
|
if (transport !== "stdio" && transport !== "sse") {
|
|
6903
6278
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
6904
6279
|
}
|
|
6905
|
-
const { startMcpServer } = await import("./server-
|
|
6280
|
+
const { startMcpServer } = await import("./server-TGGXHP4H.js");
|
|
6906
6281
|
await startMcpServer(transport, port);
|
|
6907
6282
|
}
|
|
6908
6283
|
});
|
|
6909
6284
|
|
|
6910
6285
|
// src/commands/init/index.ts
|
|
6911
|
-
import { existsSync as
|
|
6286
|
+
import { existsSync as existsSync15, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
|
|
6912
6287
|
import { randomBytes } from "crypto";
|
|
6913
|
-
import { execFileSync as
|
|
6914
|
-
import { join as
|
|
6915
|
-
import { defineCommand as
|
|
6916
|
-
import
|
|
6288
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
6289
|
+
import { join as join14 } from "path";
|
|
6290
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6291
|
+
import consola40 from "consola";
|
|
6917
6292
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
6918
6293
|
async function downloadTemplate(repo, targetDir) {
|
|
6919
6294
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
6920
6295
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
6921
6296
|
}
|
|
6922
6297
|
function installDeps(dir) {
|
|
6923
|
-
const hasLockFile = (name) =>
|
|
6298
|
+
const hasLockFile = (name) => existsSync15(join14(dir, name));
|
|
6924
6299
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
6925
|
-
|
|
6300
|
+
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
6926
6301
|
} else if (hasLockFile("bun.lockb")) {
|
|
6927
|
-
|
|
6302
|
+
execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
|
|
6928
6303
|
} else {
|
|
6929
|
-
|
|
6304
|
+
execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
6930
6305
|
}
|
|
6931
6306
|
}
|
|
6932
6307
|
async function promptChoice(message, choices) {
|
|
6933
|
-
const result = await
|
|
6308
|
+
const result = await consola40.prompt(message, { type: "select", options: choices });
|
|
6934
6309
|
if (typeof result === "symbol") {
|
|
6935
6310
|
throw new CliExit(0);
|
|
6936
6311
|
}
|
|
6937
6312
|
return result;
|
|
6938
6313
|
}
|
|
6939
6314
|
async function promptText(message, defaultValue) {
|
|
6940
|
-
const result = await
|
|
6315
|
+
const result = await consola40.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
6941
6316
|
if (typeof result === "symbol") {
|
|
6942
6317
|
throw new CliExit(0);
|
|
6943
6318
|
}
|
|
6944
6319
|
return result || defaultValue || "";
|
|
6945
6320
|
}
|
|
6946
|
-
var initCommand =
|
|
6321
|
+
var initCommand = defineCommand49({
|
|
6947
6322
|
meta: {
|
|
6948
6323
|
name: "init",
|
|
6949
6324
|
description: "Scaffold a new OpenApe project"
|
|
@@ -6985,23 +6360,23 @@ var initCommand = defineCommand50({
|
|
|
6985
6360
|
});
|
|
6986
6361
|
async function initSP(targetDir) {
|
|
6987
6362
|
const dir = targetDir || "my-app";
|
|
6988
|
-
if (
|
|
6363
|
+
if (existsSync15(join14(dir, "package.json"))) {
|
|
6989
6364
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
6990
6365
|
}
|
|
6991
|
-
|
|
6366
|
+
consola40.start("Scaffolding SP starter...");
|
|
6992
6367
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
6993
|
-
|
|
6994
|
-
|
|
6368
|
+
consola40.success("Scaffolded from openape-sp-starter");
|
|
6369
|
+
consola40.start("Installing dependencies...");
|
|
6995
6370
|
installDeps(dir);
|
|
6996
|
-
|
|
6997
|
-
const envExample =
|
|
6998
|
-
const envFile =
|
|
6999
|
-
if (
|
|
6371
|
+
consola40.success("Dependencies installed");
|
|
6372
|
+
const envExample = join14(dir, ".env.example");
|
|
6373
|
+
const envFile = join14(dir, ".env");
|
|
6374
|
+
if (existsSync15(envExample) && !existsSync15(envFile)) {
|
|
7000
6375
|
copyFileSync(envExample, envFile);
|
|
7001
|
-
|
|
6376
|
+
consola40.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
7002
6377
|
}
|
|
7003
6378
|
console.log("");
|
|
7004
|
-
|
|
6379
|
+
consola40.box([
|
|
7005
6380
|
`cd ${dir}`,
|
|
7006
6381
|
"npm run dev",
|
|
7007
6382
|
"",
|
|
@@ -7010,7 +6385,7 @@ async function initSP(targetDir) {
|
|
|
7010
6385
|
}
|
|
7011
6386
|
async function initIdP(targetDir) {
|
|
7012
6387
|
const dir = targetDir || "my-idp";
|
|
7013
|
-
if (
|
|
6388
|
+
if (existsSync15(join14(dir, "package.json"))) {
|
|
7014
6389
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
7015
6390
|
}
|
|
7016
6391
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -7020,15 +6395,15 @@ async function initIdP(targetDir) {
|
|
|
7020
6395
|
"s3 (S3-compatible)"
|
|
7021
6396
|
]);
|
|
7022
6397
|
const adminEmail = await promptText("Admin email");
|
|
7023
|
-
|
|
6398
|
+
consola40.start("Scaffolding IdP starter...");
|
|
7024
6399
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
7025
|
-
|
|
7026
|
-
|
|
6400
|
+
consola40.success("Scaffolded from openape-idp-starter");
|
|
6401
|
+
consola40.start("Installing dependencies...");
|
|
7027
6402
|
installDeps(dir);
|
|
7028
|
-
|
|
6403
|
+
consola40.success("Dependencies installed");
|
|
7029
6404
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
7030
6405
|
const managementToken = randomBytes(32).toString("hex");
|
|
7031
|
-
|
|
6406
|
+
consola40.success("Secrets generated");
|
|
7032
6407
|
const isLocalhost = domain === "localhost";
|
|
7033
6408
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
7034
6409
|
const envContent = [
|
|
@@ -7042,11 +6417,11 @@ async function initIdP(targetDir) {
|
|
|
7042
6417
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
7043
6418
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
7044
6419
|
].join("\n");
|
|
7045
|
-
writeFileSync9(
|
|
6420
|
+
writeFileSync9(join14(dir, ".env"), `${envContent}
|
|
7046
6421
|
`, { mode: 384 });
|
|
7047
|
-
|
|
6422
|
+
consola40.success(".env created");
|
|
7048
6423
|
console.log("");
|
|
7049
|
-
|
|
6424
|
+
consola40.box([
|
|
7050
6425
|
`cd ${dir}`,
|
|
7051
6426
|
"npm run dev",
|
|
7052
6427
|
"",
|
|
@@ -7063,11 +6438,11 @@ async function initIdP(targetDir) {
|
|
|
7063
6438
|
|
|
7064
6439
|
// src/commands/enroll.ts
|
|
7065
6440
|
import { Buffer as Buffer5 } from "buffer";
|
|
7066
|
-
import { existsSync as
|
|
6441
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
|
|
7067
6442
|
import { execFile as execFile2 } from "child_process";
|
|
7068
6443
|
import { sign as sign2 } from "crypto";
|
|
7069
|
-
import { defineCommand as
|
|
7070
|
-
import
|
|
6444
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6445
|
+
import consola41 from "consola";
|
|
7071
6446
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
7072
6447
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
7073
6448
|
var POLL_INTERVAL = 3e3;
|
|
@@ -7106,11 +6481,11 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
7106
6481
|
}
|
|
7107
6482
|
} catch {
|
|
7108
6483
|
}
|
|
7109
|
-
await new Promise((
|
|
6484
|
+
await new Promise((resolve4) => setTimeout(resolve4, POLL_INTERVAL));
|
|
7110
6485
|
}
|
|
7111
6486
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
7112
6487
|
}
|
|
7113
|
-
var enrollCommand =
|
|
6488
|
+
var enrollCommand = defineCommand50({
|
|
7114
6489
|
meta: {
|
|
7115
6490
|
name: "enroll",
|
|
7116
6491
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -7130,38 +6505,38 @@ var enrollCommand = defineCommand51({
|
|
|
7130
6505
|
}
|
|
7131
6506
|
},
|
|
7132
6507
|
async run({ args }) {
|
|
7133
|
-
const idp = args.idp || await
|
|
6508
|
+
const idp = args.idp || await consola41.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r3) => {
|
|
7134
6509
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7135
6510
|
return r3;
|
|
7136
6511
|
}) || DEFAULT_IDP_URL2;
|
|
7137
|
-
const agentName = args.name || await
|
|
6512
|
+
const agentName = args.name || await consola41.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r3) => {
|
|
7138
6513
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7139
6514
|
return r3;
|
|
7140
6515
|
});
|
|
7141
6516
|
if (!agentName) {
|
|
7142
6517
|
throw new CliError("Agent name is required.");
|
|
7143
6518
|
}
|
|
7144
|
-
const keyPath = args.key || await
|
|
6519
|
+
const keyPath = args.key || await consola41.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r3) => {
|
|
7145
6520
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
7146
6521
|
return r3;
|
|
7147
6522
|
}) || DEFAULT_KEY_PATH;
|
|
7148
6523
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
7149
6524
|
let publicKey;
|
|
7150
|
-
if (
|
|
6525
|
+
if (existsSync16(resolvedKey)) {
|
|
7151
6526
|
publicKey = readPublicKey(resolvedKey);
|
|
7152
|
-
|
|
6527
|
+
consola41.success(`Using existing key ${keyPath}`);
|
|
7153
6528
|
} else {
|
|
7154
|
-
|
|
6529
|
+
consola41.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
7155
6530
|
publicKey = generateAndSaveKey(keyPath);
|
|
7156
|
-
|
|
6531
|
+
consola41.success(`Key pair generated at ${keyPath}`);
|
|
7157
6532
|
}
|
|
7158
6533
|
const encodedKey = encodeURIComponent(publicKey);
|
|
7159
6534
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
7160
|
-
|
|
7161
|
-
|
|
6535
|
+
consola41.info("Opening browser for enrollment...");
|
|
6536
|
+
consola41.info(`\u2192 ${idp}/enroll`);
|
|
7162
6537
|
openBrowser2(enrollUrl);
|
|
7163
6538
|
console.log("");
|
|
7164
|
-
const agentEmail = await
|
|
6539
|
+
const agentEmail = await consola41.prompt(
|
|
7165
6540
|
"Agent email (shown in browser after enrollment)",
|
|
7166
6541
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
7167
6542
|
).then((r3) => {
|
|
@@ -7171,7 +6546,7 @@ var enrollCommand = defineCommand51({
|
|
|
7171
6546
|
if (!agentEmail) {
|
|
7172
6547
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
7173
6548
|
}
|
|
7174
|
-
|
|
6549
|
+
consola41.start("Verifying enrollment...");
|
|
7175
6550
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
7176
6551
|
saveAuth({
|
|
7177
6552
|
idp,
|
|
@@ -7183,18 +6558,18 @@ var enrollCommand = defineCommand51({
|
|
|
7183
6558
|
config.defaults = { ...config.defaults, idp };
|
|
7184
6559
|
config.agent = { key: keyPath, email: agentEmail };
|
|
7185
6560
|
saveConfig(config);
|
|
7186
|
-
|
|
7187
|
-
|
|
6561
|
+
consola41.success(`Agent enrolled as ${agentEmail}`);
|
|
6562
|
+
consola41.success("Config saved to ~/.config/apes/");
|
|
7188
6563
|
console.log("");
|
|
7189
|
-
|
|
6564
|
+
consola41.info("Verify with: apes whoami");
|
|
7190
6565
|
}
|
|
7191
6566
|
});
|
|
7192
6567
|
|
|
7193
6568
|
// src/commands/register-user.ts
|
|
7194
|
-
import { existsSync as
|
|
7195
|
-
import { defineCommand as
|
|
7196
|
-
import
|
|
7197
|
-
var registerUserCommand =
|
|
6569
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
6570
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
6571
|
+
import consola42 from "consola";
|
|
6572
|
+
var registerUserCommand = defineCommand51({
|
|
7198
6573
|
meta: {
|
|
7199
6574
|
name: "register-user",
|
|
7200
6575
|
description: "Register a sub-user with SSH key"
|
|
@@ -7230,7 +6605,7 @@ var registerUserCommand = defineCommand52({
|
|
|
7230
6605
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
7231
6606
|
}
|
|
7232
6607
|
let publicKey = args.key;
|
|
7233
|
-
if (
|
|
6608
|
+
if (existsSync17(args.key)) {
|
|
7234
6609
|
publicKey = readFileSync13(args.key, "utf-8").trim();
|
|
7235
6610
|
}
|
|
7236
6611
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
@@ -7249,18 +6624,18 @@ var registerUserCommand = defineCommand52({
|
|
|
7249
6624
|
...userType ? { type: userType } : {}
|
|
7250
6625
|
}
|
|
7251
6626
|
});
|
|
7252
|
-
|
|
6627
|
+
consola42.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
7253
6628
|
}
|
|
7254
6629
|
});
|
|
7255
6630
|
|
|
7256
6631
|
// src/commands/utils/index.ts
|
|
7257
|
-
import { defineCommand as
|
|
6632
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
7258
6633
|
|
|
7259
6634
|
// src/commands/utils/dig.ts
|
|
7260
|
-
import { defineCommand as
|
|
7261
|
-
import
|
|
6635
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
6636
|
+
import consola43 from "consola";
|
|
7262
6637
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
7263
|
-
var digCommand =
|
|
6638
|
+
var digCommand = defineCommand52({
|
|
7264
6639
|
meta: {
|
|
7265
6640
|
name: "dig",
|
|
7266
6641
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -7333,12 +6708,12 @@ var digCommand = defineCommand53({
|
|
|
7333
6708
|
console.log(` domain: ${domain}`);
|
|
7334
6709
|
console.log("");
|
|
7335
6710
|
if (!result.ddisa.found) {
|
|
7336
|
-
|
|
6711
|
+
consola43.warn(`No DDISA record at _ddisa.${domain}`);
|
|
7337
6712
|
if (result.hint) console.log(`
|
|
7338
6713
|
${result.hint}`);
|
|
7339
6714
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
7340
6715
|
}
|
|
7341
|
-
|
|
6716
|
+
consola43.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
7342
6717
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
7343
6718
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
7344
6719
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -7348,13 +6723,13 @@ ${result.hint}`);
|
|
|
7348
6723
|
return;
|
|
7349
6724
|
}
|
|
7350
6725
|
if (result.idpDiscovery.ok) {
|
|
7351
|
-
|
|
6726
|
+
consola43.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
7352
6727
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
7353
6728
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
7354
6729
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
7355
6730
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
7356
6731
|
} else {
|
|
7357
|
-
|
|
6732
|
+
consola43.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
7358
6733
|
if (result.hint) console.log(`
|
|
7359
6734
|
${result.hint}`);
|
|
7360
6735
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -7363,7 +6738,7 @@ ${result.hint}`);
|
|
|
7363
6738
|
});
|
|
7364
6739
|
|
|
7365
6740
|
// src/commands/utils/index.ts
|
|
7366
|
-
var utilsCommand =
|
|
6741
|
+
var utilsCommand = defineCommand53({
|
|
7367
6742
|
meta: {
|
|
7368
6743
|
name: "utils",
|
|
7369
6744
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -7374,12 +6749,12 @@ var utilsCommand = defineCommand54({
|
|
|
7374
6749
|
});
|
|
7375
6750
|
|
|
7376
6751
|
// src/commands/sessions/index.ts
|
|
7377
|
-
import { defineCommand as
|
|
6752
|
+
import { defineCommand as defineCommand56 } from "citty";
|
|
7378
6753
|
|
|
7379
6754
|
// src/commands/sessions/list.ts
|
|
7380
|
-
import { defineCommand as
|
|
7381
|
-
import
|
|
7382
|
-
var sessionsListCommand =
|
|
6755
|
+
import { defineCommand as defineCommand54 } from "citty";
|
|
6756
|
+
import consola44 from "consola";
|
|
6757
|
+
var sessionsListCommand = defineCommand54({
|
|
7383
6758
|
meta: {
|
|
7384
6759
|
name: "list",
|
|
7385
6760
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -7397,7 +6772,7 @@ var sessionsListCommand = defineCommand55({
|
|
|
7397
6772
|
return;
|
|
7398
6773
|
}
|
|
7399
6774
|
if (result.data.length === 0) {
|
|
7400
|
-
|
|
6775
|
+
consola44.info("No active sessions.");
|
|
7401
6776
|
return;
|
|
7402
6777
|
}
|
|
7403
6778
|
for (const f of result.data) {
|
|
@@ -7409,9 +6784,9 @@ var sessionsListCommand = defineCommand55({
|
|
|
7409
6784
|
});
|
|
7410
6785
|
|
|
7411
6786
|
// src/commands/sessions/remove.ts
|
|
7412
|
-
import { defineCommand as
|
|
7413
|
-
import
|
|
7414
|
-
var sessionsRemoveCommand =
|
|
6787
|
+
import { defineCommand as defineCommand55 } from "citty";
|
|
6788
|
+
import consola45 from "consola";
|
|
6789
|
+
var sessionsRemoveCommand = defineCommand55({
|
|
7415
6790
|
meta: {
|
|
7416
6791
|
name: "remove",
|
|
7417
6792
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -7427,12 +6802,12 @@ var sessionsRemoveCommand = defineCommand56({
|
|
|
7427
6802
|
const id = String(args.familyId).trim();
|
|
7428
6803
|
if (!id) throw new CliError("familyId required");
|
|
7429
6804
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
7430
|
-
|
|
6805
|
+
consola45.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
7431
6806
|
}
|
|
7432
6807
|
});
|
|
7433
6808
|
|
|
7434
6809
|
// src/commands/sessions/index.ts
|
|
7435
|
-
var sessionsCommand =
|
|
6810
|
+
var sessionsCommand = defineCommand56({
|
|
7436
6811
|
meta: {
|
|
7437
6812
|
name: "sessions",
|
|
7438
6813
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -7444,10 +6819,10 @@ var sessionsCommand = defineCommand57({
|
|
|
7444
6819
|
});
|
|
7445
6820
|
|
|
7446
6821
|
// src/commands/dns-check.ts
|
|
7447
|
-
import { defineCommand as
|
|
7448
|
-
import
|
|
6822
|
+
import { defineCommand as defineCommand57 } from "citty";
|
|
6823
|
+
import consola46 from "consola";
|
|
7449
6824
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
7450
|
-
var dnsCheckCommand =
|
|
6825
|
+
var dnsCheckCommand = defineCommand57({
|
|
7451
6826
|
meta: {
|
|
7452
6827
|
name: "dns-check",
|
|
7453
6828
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -7461,7 +6836,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7461
6836
|
},
|
|
7462
6837
|
async run({ args }) {
|
|
7463
6838
|
const domain = args.domain;
|
|
7464
|
-
|
|
6839
|
+
consola46.start(`Checking _ddisa.${domain}...`);
|
|
7465
6840
|
try {
|
|
7466
6841
|
const result = await resolveDDISA3(domain);
|
|
7467
6842
|
if (!result) {
|
|
@@ -7470,7 +6845,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7470
6845
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
7471
6846
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
7472
6847
|
}
|
|
7473
|
-
|
|
6848
|
+
consola46.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
7474
6849
|
console.log("");
|
|
7475
6850
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
7476
6851
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -7479,14 +6854,14 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7479
6854
|
if (result.priority !== void 0)
|
|
7480
6855
|
console.log(` Priority: ${result.priority}`);
|
|
7481
6856
|
console.log("");
|
|
7482
|
-
|
|
6857
|
+
consola46.start(`Verifying IdP at ${result.idp}...`);
|
|
7483
6858
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
7484
6859
|
if (!discoResp.ok) {
|
|
7485
|
-
|
|
6860
|
+
consola46.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
7486
6861
|
return;
|
|
7487
6862
|
}
|
|
7488
6863
|
const disco = await discoResp.json();
|
|
7489
|
-
|
|
6864
|
+
consola46.success(`IdP is reachable`);
|
|
7490
6865
|
console.log(` Issuer: ${disco.issuer}`);
|
|
7491
6866
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
7492
6867
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -7504,7 +6879,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7504
6879
|
// src/commands/health.ts
|
|
7505
6880
|
import { exec } from "child_process";
|
|
7506
6881
|
import { promisify } from "util";
|
|
7507
|
-
import { defineCommand as
|
|
6882
|
+
import { defineCommand as defineCommand58 } from "citty";
|
|
7508
6883
|
var execAsync = promisify(exec);
|
|
7509
6884
|
async function resolveApeShellPath() {
|
|
7510
6885
|
try {
|
|
@@ -7540,7 +6915,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
7540
6915
|
}
|
|
7541
6916
|
}
|
|
7542
6917
|
async function runHealth(args) {
|
|
7543
|
-
const version = true ? "1.
|
|
6918
|
+
const version = true ? "1.12.0" : "0.0.0";
|
|
7544
6919
|
const auth = loadAuth();
|
|
7545
6920
|
if (!auth) {
|
|
7546
6921
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -7603,7 +6978,7 @@ async function runHealth(args) {
|
|
|
7603
6978
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
7604
6979
|
}
|
|
7605
6980
|
}
|
|
7606
|
-
var healthCommand =
|
|
6981
|
+
var healthCommand = defineCommand58({
|
|
7607
6982
|
meta: {
|
|
7608
6983
|
name: "health",
|
|
7609
6984
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -7621,8 +6996,8 @@ var healthCommand = defineCommand59({
|
|
|
7621
6996
|
});
|
|
7622
6997
|
|
|
7623
6998
|
// src/commands/workflows.ts
|
|
7624
|
-
import { defineCommand as
|
|
7625
|
-
import
|
|
6999
|
+
import { defineCommand as defineCommand59 } from "citty";
|
|
7000
|
+
import consola47 from "consola";
|
|
7626
7001
|
|
|
7627
7002
|
// src/guides/index.ts
|
|
7628
7003
|
var guides = [
|
|
@@ -7672,7 +7047,7 @@ var guides = [
|
|
|
7672
7047
|
];
|
|
7673
7048
|
|
|
7674
7049
|
// src/commands/workflows.ts
|
|
7675
|
-
var workflowsCommand =
|
|
7050
|
+
var workflowsCommand = defineCommand59({
|
|
7676
7051
|
meta: {
|
|
7677
7052
|
name: "workflows",
|
|
7678
7053
|
description: "Discover workflow guides"
|
|
@@ -7693,7 +7068,7 @@ var workflowsCommand = defineCommand60({
|
|
|
7693
7068
|
if (args.id) {
|
|
7694
7069
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
7695
7070
|
if (!guide) {
|
|
7696
|
-
|
|
7071
|
+
consola47.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
7697
7072
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
7698
7073
|
}
|
|
7699
7074
|
if (args.json) {
|
|
@@ -7733,15 +7108,15 @@ var workflowsCommand = defineCommand60({
|
|
|
7733
7108
|
});
|
|
7734
7109
|
|
|
7735
7110
|
// src/version-check.ts
|
|
7736
|
-
import { existsSync as
|
|
7111
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
7737
7112
|
import { homedir as homedir13 } from "os";
|
|
7738
|
-
import { join as
|
|
7739
|
-
import
|
|
7113
|
+
import { join as join15 } from "path";
|
|
7114
|
+
import consola48 from "consola";
|
|
7740
7115
|
var PACKAGE_NAME = "@openape/apes";
|
|
7741
7116
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
7742
|
-
var CACHE_FILE =
|
|
7117
|
+
var CACHE_FILE = join15(homedir13(), ".config", "apes", ".version-check.json");
|
|
7743
7118
|
function readCache() {
|
|
7744
|
-
if (!
|
|
7119
|
+
if (!existsSync18(CACHE_FILE)) return null;
|
|
7745
7120
|
try {
|
|
7746
7121
|
return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
|
|
7747
7122
|
} catch {
|
|
@@ -7750,8 +7125,8 @@ function readCache() {
|
|
|
7750
7125
|
}
|
|
7751
7126
|
function writeCache(entry) {
|
|
7752
7127
|
try {
|
|
7753
|
-
const dir =
|
|
7754
|
-
if (!
|
|
7128
|
+
const dir = join15(homedir13(), ".config", "apes");
|
|
7129
|
+
if (!existsSync18(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
7755
7130
|
writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
7756
7131
|
} catch {
|
|
7757
7132
|
}
|
|
@@ -7781,7 +7156,7 @@ async function fetchLatestVersion() {
|
|
|
7781
7156
|
}
|
|
7782
7157
|
function warnIfBehind(currentVersion, latest) {
|
|
7783
7158
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
7784
|
-
|
|
7159
|
+
consola48.warn(
|
|
7785
7160
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
7786
7161
|
);
|
|
7787
7162
|
}
|
|
@@ -7813,10 +7188,10 @@ if (shellRewrite) {
|
|
|
7813
7188
|
if (shellRewrite.action === "rewrite") {
|
|
7814
7189
|
process.argv = shellRewrite.argv;
|
|
7815
7190
|
} else if (shellRewrite.action === "version") {
|
|
7816
|
-
console.log(`ape-shell ${"1.
|
|
7191
|
+
console.log(`ape-shell ${"1.12.0"} (OpenApe DDISA shell wrapper)`);
|
|
7817
7192
|
process.exit(0);
|
|
7818
7193
|
} else if (shellRewrite.action === "help") {
|
|
7819
|
-
console.log(`ape-shell ${"1.
|
|
7194
|
+
console.log(`ape-shell ${"1.12.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
7820
7195
|
console.log("");
|
|
7821
7196
|
console.log("Usage:");
|
|
7822
7197
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -7840,7 +7215,7 @@ if (shellRewrite) {
|
|
|
7840
7215
|
}
|
|
7841
7216
|
}
|
|
7842
7217
|
var debug = process.argv.includes("--debug");
|
|
7843
|
-
var grantsCommand =
|
|
7218
|
+
var grantsCommand = defineCommand60({
|
|
7844
7219
|
meta: {
|
|
7845
7220
|
name: "grants",
|
|
7846
7221
|
description: "Grant management"
|
|
@@ -7861,7 +7236,7 @@ var grantsCommand = defineCommand61({
|
|
|
7861
7236
|
"delegation-revoke": delegationRevokeCommand
|
|
7862
7237
|
}
|
|
7863
7238
|
});
|
|
7864
|
-
var configCommand =
|
|
7239
|
+
var configCommand = defineCommand60({
|
|
7865
7240
|
meta: {
|
|
7866
7241
|
name: "config",
|
|
7867
7242
|
description: "Configuration management"
|
|
@@ -7871,10 +7246,10 @@ var configCommand = defineCommand61({
|
|
|
7871
7246
|
set: configSetCommand
|
|
7872
7247
|
}
|
|
7873
7248
|
});
|
|
7874
|
-
var main =
|
|
7249
|
+
var main = defineCommand60({
|
|
7875
7250
|
meta: {
|
|
7876
7251
|
name: "apes",
|
|
7877
|
-
version: "1.
|
|
7252
|
+
version: "1.12.0",
|
|
7878
7253
|
description: "Unified CLI for OpenApe"
|
|
7879
7254
|
},
|
|
7880
7255
|
subCommands: {
|
|
@@ -7931,20 +7306,20 @@ async function maybeRefreshAuth() {
|
|
|
7931
7306
|
}
|
|
7932
7307
|
}
|
|
7933
7308
|
await maybeRefreshAuth();
|
|
7934
|
-
await maybeWarnStaleVersion("1.
|
|
7309
|
+
await maybeWarnStaleVersion("1.12.0").catch(() => {
|
|
7935
7310
|
});
|
|
7936
7311
|
runMain(main).catch((err) => {
|
|
7937
7312
|
if (err instanceof CliExit) {
|
|
7938
7313
|
process.exit(err.exitCode);
|
|
7939
7314
|
}
|
|
7940
7315
|
if (err instanceof CliError) {
|
|
7941
|
-
|
|
7316
|
+
consola49.error(err.message);
|
|
7942
7317
|
process.exit(err.exitCode);
|
|
7943
7318
|
}
|
|
7944
7319
|
if (debug) {
|
|
7945
|
-
|
|
7320
|
+
consola49.error(err);
|
|
7946
7321
|
} else {
|
|
7947
|
-
|
|
7322
|
+
consola49.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
7948
7323
|
}
|
|
7949
7324
|
process.exit(1);
|
|
7950
7325
|
});
|