@openape/apes 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
import "./chunk-7OCVIDC7.js";
|
|
78
78
|
|
|
79
79
|
// src/cli.ts
|
|
80
|
-
import
|
|
80
|
+
import consola49 from "consola";
|
|
81
81
|
|
|
82
82
|
// src/ape-shell.ts
|
|
83
83
|
import path from "path";
|
|
@@ -107,7 +107,7 @@ function rewriteApeShellArgs(argv, argv0) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/cli.ts
|
|
110
|
-
import { defineCommand as
|
|
110
|
+
import { defineCommand as defineCommand60, runMain } from "citty";
|
|
111
111
|
|
|
112
112
|
// src/commands/auth/login.ts
|
|
113
113
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -393,7 +393,7 @@ async function loginWithPKCE(idp) {
|
|
|
393
393
|
consola2.success(`Logged in as ${payload.email || payload.sub}`);
|
|
394
394
|
}
|
|
395
395
|
async function loginWithKey(idp, keyPath, agentEmail) {
|
|
396
|
-
const { readFileSync:
|
|
396
|
+
const { readFileSync: readFileSync15 } = await import("fs");
|
|
397
397
|
const { sign: sign3 } = await import("crypto");
|
|
398
398
|
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-6X3YZXSD.js");
|
|
399
399
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
@@ -406,7 +406,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
|
|
|
406
406
|
throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
|
|
407
407
|
}
|
|
408
408
|
const { challenge } = await challengeResp.json();
|
|
409
|
-
const keyContent =
|
|
409
|
+
const keyContent = readFileSync15(keyPath, "utf-8");
|
|
410
410
|
const privateKey = loadEd25519PrivateKey2(keyContent);
|
|
411
411
|
const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
|
|
412
412
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -2693,46 +2693,19 @@ function buildBridgeBlock(bridge) {
|
|
|
2693
2693
|
return `
|
|
2694
2694
|
mkdir -p "$HOME_DIR/Library/Application Support/openape/bridge" "$HOME_DIR/Library/Logs"
|
|
2695
2695
|
cat > "$HOME_DIR/Library/Application Support/openape/bridge/.env" ${shHeredoc(bridge.envFile)}
|
|
2696
|
-
cat > "$HOME_DIR/Library/Application Support/openape/bridge/start.sh" ${shHeredoc(bridge.startScript)}
|
|
2697
|
-
chmod 755 "$HOME_DIR/Library/Application Support/openape/bridge/start.sh"
|
|
2698
2696
|
chmod 600 "$HOME_DIR/Library/Application Support/openape/bridge/.env"
|
|
2699
|
-
|
|
2700
|
-
# System-wide LaunchDaemon \u2014 root-owned, mode 644 (launchd refuses
|
|
2701
|
-
# group/world-writable plists). UserName in the plist makes launchd run
|
|
2702
|
-
# the binary as the agent, not root.
|
|
2703
|
-
cat > ${shQuote(bridge.plistPath)} ${shHeredoc(bridge.plistContent)}
|
|
2704
|
-
chown root:wheel ${shQuote(bridge.plistPath)}
|
|
2705
|
-
chmod 644 ${shQuote(bridge.plistPath)}
|
|
2706
2697
|
`;
|
|
2707
2698
|
}
|
|
2708
|
-
function buildBridgeBootstrapBlock(
|
|
2709
|
-
|
|
2710
|
-
return `
|
|
2711
|
-
launchctl bootout "system/${bridge.plistLabel}" 2>/dev/null || true
|
|
2712
|
-
launchctl bootstrap system ${shQuote(bridge.plistPath)} || \\
|
|
2713
|
-
echo "warn: bridge bootstrap into system domain failed; check ${bridge.plistPath}"
|
|
2714
|
-
`;
|
|
2699
|
+
function buildBridgeBootstrapBlock(_bridge, _name) {
|
|
2700
|
+
return "";
|
|
2715
2701
|
}
|
|
2716
|
-
function buildTroopBlock(
|
|
2717
|
-
if (!troop) return "";
|
|
2702
|
+
function buildTroopBlock(_troop) {
|
|
2718
2703
|
return `
|
|
2719
|
-
mkdir -p "$HOME_DIR/Library/
|
|
2720
|
-
cat > ${shQuote(troop.plistPath)} ${shHeredoc(troop.plistContent)}
|
|
2721
|
-
chown root:wheel ${shQuote(troop.plistPath)}
|
|
2722
|
-
chmod 644 ${shQuote(troop.plistPath)}
|
|
2704
|
+
mkdir -p "$HOME_DIR/Library/Logs" "$HOME_DIR/.openape/agent/tasks"
|
|
2723
2705
|
`;
|
|
2724
2706
|
}
|
|
2725
|
-
function buildTroopBootstrapBlock(
|
|
2726
|
-
|
|
2727
|
-
return `
|
|
2728
|
-
# Bootstrap the troop sync launchd in the system domain. setup.sh runs
|
|
2729
|
-
# as root via \`apes run --as root\`, so we have permission. Stale label
|
|
2730
|
-
# is bootouted first to make re-spawn idempotent.
|
|
2731
|
-
echo "==> Installing troop sync launchd as ${name}\u2026"
|
|
2732
|
-
launchctl bootout "system/${troop.plistLabel}" 2>/dev/null || true
|
|
2733
|
-
launchctl bootstrap system ${shQuote(troop.plistPath)} || \\
|
|
2734
|
-
echo "warn: troop sync bootstrap failed; check ${troop.plistPath}"
|
|
2735
|
-
`;
|
|
2707
|
+
function buildTroopBootstrapBlock(_troop, _name) {
|
|
2708
|
+
return "";
|
|
2736
2709
|
}
|
|
2737
2710
|
function buildDestroyTeardownScript(input) {
|
|
2738
2711
|
const { name, homeDir, adminUser } = input;
|
|
@@ -4265,7 +4238,7 @@ var agentsCommand = defineCommand28({
|
|
|
4265
4238
|
});
|
|
4266
4239
|
|
|
4267
4240
|
// src/commands/nest/index.ts
|
|
4268
|
-
import { defineCommand as
|
|
4241
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
4269
4242
|
|
|
4270
4243
|
// src/commands/nest/authorize.ts
|
|
4271
4244
|
import { execFileSync as execFileSync8 } from "child_process";
|
|
@@ -4385,7 +4358,14 @@ var DEFAULT_ALLOW_PATTERNS = [
|
|
|
4385
4358
|
// is the wrapper that gets unwrapped before grant creation. So the
|
|
4386
4359
|
// YOLO target string is just `openape-chat-bridge`, not the full
|
|
4387
4360
|
// wrapped invocation.
|
|
4388
|
-
"openape-chat-bridge"
|
|
4361
|
+
"openape-chat-bridge",
|
|
4362
|
+
// Phase E: per-agent pm2 management. The Nest shells out
|
|
4363
|
+
// `apes run --as <agent> -- pm2 startOrReload /var/openape/nest/agents/<agent>/ecosystem.config.js`
|
|
4364
|
+
// and `apes run --as <agent> -- pm2 delete openape-bridge-<agent>`.
|
|
4365
|
+
// Inner-command (post-unwrap) targets:
|
|
4366
|
+
"pm2 startOrReload *",
|
|
4367
|
+
"pm2 delete openape-bridge-*",
|
|
4368
|
+
"pm2 jlist"
|
|
4389
4369
|
];
|
|
4390
4370
|
var authorizeNestCommand = defineCommand30({
|
|
4391
4371
|
meta: {
|
|
@@ -4434,147 +4414,107 @@ var authorizeNestCommand = defineCommand30({
|
|
|
4434
4414
|
});
|
|
4435
4415
|
|
|
4436
4416
|
// src/commands/nest/destroy.ts
|
|
4437
|
-
import process2 from "process";
|
|
4438
4417
|
import { defineCommand as defineCommand31 } from "citty";
|
|
4439
|
-
import consola28 from "consola";
|
|
4440
|
-
|
|
4441
|
-
// src/lib/nest-grant-flow.ts
|
|
4442
|
-
import { hostname as hostname5 } from "os";
|
|
4443
4418
|
import consola27 from "consola";
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
grant_type: approval,
|
|
4477
|
-
command: opts.command,
|
|
4478
|
-
reason: opts.reason ?? opts.command.join(" ")
|
|
4479
|
-
}
|
|
4480
|
-
});
|
|
4481
|
-
let approved = grant.status === "approved";
|
|
4482
|
-
const maxWait = 15 * 60 * 1e3;
|
|
4483
|
-
const interval = 3e3;
|
|
4484
|
-
const start = Date.now();
|
|
4485
|
-
while (!approved && Date.now() - start < maxWait) {
|
|
4486
|
-
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
4487
|
-
if (status.status === "approved") {
|
|
4488
|
-
approved = true;
|
|
4489
|
-
break;
|
|
4490
|
-
}
|
|
4491
|
-
if (status.status === "denied" || status.status === "revoked") {
|
|
4492
|
-
throw new CliError(`Grant ${status.status}.`);
|
|
4493
|
-
}
|
|
4494
|
-
if (!approved) {
|
|
4495
|
-
consola27.info(`Waiting for approval: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4496
|
-
await new Promise((r3) => setTimeout(r3, interval));
|
|
4419
|
+
|
|
4420
|
+
// src/lib/nest-intent.ts
|
|
4421
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync10, statSync as statSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
|
|
4422
|
+
import { homedir as homedir10 } from "os";
|
|
4423
|
+
import { join as join10 } from "path";
|
|
4424
|
+
import { randomUUID } from "crypto";
|
|
4425
|
+
var POLL_INTERVAL_MS = 200;
|
|
4426
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4427
|
+
function resolveIntentDir() {
|
|
4428
|
+
if (process.env.OPENAPE_NEST_INTENT_DIR) return process.env.OPENAPE_NEST_INTENT_DIR;
|
|
4429
|
+
if (existsSync12("/var/openape/nest/intents")) return "/var/openape/nest/intents";
|
|
4430
|
+
return join10(homedir10(), ".openape", "nest", "intents");
|
|
4431
|
+
}
|
|
4432
|
+
async function dispatchIntent(intent, opts = {}) {
|
|
4433
|
+
const id = randomUUID();
|
|
4434
|
+
const dir = resolveIntentDir();
|
|
4435
|
+
if (!existsSync12(dir)) {
|
|
4436
|
+
throw new CliError(`Nest intent dir does not exist: ${dir}
|
|
4437
|
+
Is the nest daemon running? Try \`ps aux | grep openape-nest\`.`);
|
|
4438
|
+
}
|
|
4439
|
+
const intentPath = join10(dir, `${id}.json`);
|
|
4440
|
+
const responsePath = join10(dir, `${id}.response`);
|
|
4441
|
+
const tmpPath = `${intentPath}.tmp`;
|
|
4442
|
+
writeFileSync6(tmpPath, `${JSON.stringify({ id, ...intent })}
|
|
4443
|
+
`, { mode: 432 });
|
|
4444
|
+
try {
|
|
4445
|
+
const fs = await import("fs");
|
|
4446
|
+
fs.renameSync(tmpPath, intentPath);
|
|
4447
|
+
} catch (err) {
|
|
4448
|
+
try {
|
|
4449
|
+
unlinkSync(tmpPath);
|
|
4450
|
+
} catch {
|
|
4497
4451
|
}
|
|
4452
|
+
throw err;
|
|
4498
4453
|
}
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4454
|
+
const deadline = Date.now() + (opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
4455
|
+
while (Date.now() < deadline) {
|
|
4456
|
+
if (existsSync12(responsePath)) {
|
|
4457
|
+
let raw;
|
|
4458
|
+
try {
|
|
4459
|
+
const st = statSync2(responsePath);
|
|
4460
|
+
if (Date.now() - st.mtimeMs < 50) {
|
|
4461
|
+
await sleep(50);
|
|
4462
|
+
}
|
|
4463
|
+
raw = readFileSync10(responsePath, "utf8");
|
|
4464
|
+
} catch {
|
|
4465
|
+
await sleep(POLL_INTERVAL_MS);
|
|
4466
|
+
continue;
|
|
4467
|
+
}
|
|
4468
|
+
try {
|
|
4469
|
+
unlinkSync(responsePath);
|
|
4470
|
+
} catch {
|
|
4471
|
+
}
|
|
4472
|
+
let parsed;
|
|
4473
|
+
try {
|
|
4474
|
+
parsed = JSON.parse(raw);
|
|
4475
|
+
} catch (err) {
|
|
4476
|
+
throw new CliError(`malformed nest response: ${err instanceof Error ? err.message : String(err)}`);
|
|
4477
|
+
}
|
|
4478
|
+
if (!parsed.ok) {
|
|
4479
|
+
throw new CliError(`nest: ${parsed.error}`);
|
|
4480
|
+
}
|
|
4481
|
+
return parsed.result;
|
|
4482
|
+
}
|
|
4483
|
+
await sleep(POLL_INTERVAL_MS);
|
|
4503
4484
|
}
|
|
4504
|
-
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
4505
|
-
method: "POST"
|
|
4506
|
-
});
|
|
4507
|
-
return authz_jwt;
|
|
4508
|
-
}
|
|
4509
|
-
function nestBaseUrl(port) {
|
|
4510
|
-
const p2 = port ?? Number(process.env.OPENAPE_NEST_PORT ?? 9091);
|
|
4511
|
-
return `http://127.0.0.1:${p2}`;
|
|
4512
|
-
}
|
|
4513
|
-
async function findReusableNestGrant(opts) {
|
|
4514
4485
|
try {
|
|
4515
|
-
|
|
4516
|
-
`${opts.grantsUrl}?requester=${encodeURIComponent(opts.requester)}&status=approved&limit=50`
|
|
4517
|
-
);
|
|
4518
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
4519
|
-
const match = grants.data.find((g) => {
|
|
4520
|
-
const r3 = g.request;
|
|
4521
|
-
if (r3.audience !== NEST_AUDIENCE) return false;
|
|
4522
|
-
if (r3.target_host !== opts.targetHost) return false;
|
|
4523
|
-
if (r3.grant_type === "once") return false;
|
|
4524
|
-
if (r3.grant_type === "timed" && g.expires_at && g.expires_at <= now) return false;
|
|
4525
|
-
const cmd = r3.command ?? [];
|
|
4526
|
-
if (cmd.length !== opts.command.length) return false;
|
|
4527
|
-
return cmd.every((c2, i) => c2 === opts.command[i]);
|
|
4528
|
-
});
|
|
4529
|
-
return match?.id ?? null;
|
|
4486
|
+
unlinkSync(intentPath);
|
|
4530
4487
|
} catch {
|
|
4531
|
-
return null;
|
|
4532
4488
|
}
|
|
4489
|
+
throw new CliError(`nest intent timeout (${(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS) / 1e3}s) \u2014 Nest daemon may not be running or is stuck.`);
|
|
4490
|
+
}
|
|
4491
|
+
function sleep(ms) {
|
|
4492
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
4533
4493
|
}
|
|
4534
4494
|
|
|
4535
4495
|
// src/commands/nest/destroy.ts
|
|
4536
4496
|
var destroyNestCommand = defineCommand31({
|
|
4537
4497
|
meta: {
|
|
4538
4498
|
name: "destroy",
|
|
4539
|
-
description: "Tear down an agent on the local nest
|
|
4499
|
+
description: "Tear down an agent on the local nest. Drops an intent file the nest daemon picks up."
|
|
4540
4500
|
},
|
|
4541
4501
|
args: {
|
|
4542
|
-
name: { type: "positional", required: true, description: "Agent name to destroy" }
|
|
4543
|
-
port: { type: "string", description: "Override nest port (default: 9091)" }
|
|
4502
|
+
name: { type: "positional", required: true, description: "Agent name to destroy" }
|
|
4544
4503
|
},
|
|
4545
4504
|
async run({ args }) {
|
|
4546
4505
|
const name = String(args.name);
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
try {
|
|
4550
|
-
const res = await fetch(`${base}/agents/${encodeURIComponent(name)}`, {
|
|
4551
|
-
method: "DELETE",
|
|
4552
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4553
|
-
});
|
|
4554
|
-
if (!res.ok) {
|
|
4555
|
-
const text = await res.text().catch(() => "");
|
|
4556
|
-
throw new CliError(`nest DELETE /agents/${name} failed: ${res.status} ${text}`);
|
|
4557
|
-
}
|
|
4558
|
-
} catch (err) {
|
|
4559
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
4560
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4561
|
-
consola28.error(`Nest daemon is not running at ${base}`);
|
|
4562
|
-
consola28.info(" Run: apes nest install");
|
|
4563
|
-
process2.exit(2);
|
|
4564
|
-
}
|
|
4565
|
-
throw err;
|
|
4566
|
-
}
|
|
4567
|
-
consola28.success(`Destroyed ${name}`);
|
|
4506
|
+
await dispatchIntent({ action: "destroy", name });
|
|
4507
|
+
consola27.success(`Destroyed ${name}`);
|
|
4568
4508
|
}
|
|
4569
4509
|
});
|
|
4570
4510
|
|
|
4571
4511
|
// src/commands/nest/install.ts
|
|
4572
4512
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
4573
|
-
import { existsSync as
|
|
4574
|
-
import { homedir as
|
|
4575
|
-
import { dirname as dirname3, join as
|
|
4513
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
4514
|
+
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
4515
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
4576
4516
|
import { defineCommand as defineCommand32 } from "citty";
|
|
4577
|
-
import
|
|
4517
|
+
import consola28 from "consola";
|
|
4578
4518
|
|
|
4579
4519
|
// src/commands/nest/apes-agents-adapter.ts
|
|
4580
4520
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4641,13 +4581,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
4641
4581
|
// src/commands/nest/install.ts
|
|
4642
4582
|
var PLIST_LABEL = "ai.openape.nest";
|
|
4643
4583
|
function plistPath() {
|
|
4644
|
-
return
|
|
4584
|
+
return join11(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
4645
4585
|
}
|
|
4646
4586
|
function escape2(s) {
|
|
4647
4587
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4648
4588
|
}
|
|
4649
4589
|
function buildPlist(args) {
|
|
4650
|
-
const logsDir =
|
|
4590
|
+
const logsDir = join11(args.userHome, "Library", "Logs");
|
|
4651
4591
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4652
4592
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4653
4593
|
<plist version="1.0">
|
|
@@ -4682,40 +4622,40 @@ function buildPlist(args) {
|
|
|
4682
4622
|
`;
|
|
4683
4623
|
}
|
|
4684
4624
|
function installAdapter2() {
|
|
4685
|
-
const target =
|
|
4686
|
-
|
|
4625
|
+
const target = join11(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4626
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
4687
4627
|
let existing = "";
|
|
4688
4628
|
try {
|
|
4689
|
-
existing =
|
|
4629
|
+
existing = readFileSync11(target, "utf8");
|
|
4690
4630
|
} catch {
|
|
4691
4631
|
}
|
|
4692
4632
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4693
|
-
|
|
4694
|
-
|
|
4633
|
+
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4634
|
+
consola28.success(`Wrote shapes adapter ${target}`);
|
|
4695
4635
|
return true;
|
|
4696
4636
|
}
|
|
4697
4637
|
function writeBridgeModelDefault(model) {
|
|
4698
|
-
const envDir =
|
|
4699
|
-
const envFile =
|
|
4700
|
-
|
|
4638
|
+
const envDir = join11(homedir11(), "litellm");
|
|
4639
|
+
const envFile = join11(envDir, ".env");
|
|
4640
|
+
mkdirSync5(envDir, { recursive: true });
|
|
4701
4641
|
let lines = [];
|
|
4702
|
-
if (
|
|
4703
|
-
lines =
|
|
4642
|
+
if (existsSync13(envFile)) {
|
|
4643
|
+
lines = readFileSync11(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
|
|
4704
4644
|
}
|
|
4705
4645
|
lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
|
|
4706
4646
|
while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
|
|
4707
|
-
|
|
4647
|
+
writeFileSync7(envFile, `${lines.join("\n")}
|
|
4708
4648
|
`, { mode: 384 });
|
|
4709
4649
|
}
|
|
4710
4650
|
function findBinary(name) {
|
|
4711
4651
|
for (const dir of [
|
|
4712
|
-
|
|
4652
|
+
join11(homedir11(), ".bun", "bin"),
|
|
4713
4653
|
"/opt/homebrew/bin",
|
|
4714
4654
|
"/usr/local/bin",
|
|
4715
4655
|
"/usr/bin"
|
|
4716
4656
|
]) {
|
|
4717
|
-
const p2 =
|
|
4718
|
-
if (
|
|
4657
|
+
const p2 = join11(dir, name);
|
|
4658
|
+
if (existsSync13(p2)) return p2;
|
|
4719
4659
|
}
|
|
4720
4660
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4721
4661
|
}
|
|
@@ -4735,35 +4675,35 @@ var installNestCommand = defineCommand32({
|
|
|
4735
4675
|
}
|
|
4736
4676
|
},
|
|
4737
4677
|
async run({ args }) {
|
|
4738
|
-
const homeDir =
|
|
4678
|
+
const homeDir = homedir11();
|
|
4739
4679
|
const port = Number(args.port ?? 9091);
|
|
4740
4680
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
4741
4681
|
throw new Error(`invalid port ${port}`);
|
|
4742
4682
|
}
|
|
4743
4683
|
const nestBin = findBinary("openape-nest");
|
|
4744
4684
|
const apesBin = findBinary("apes");
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4685
|
+
consola28.info(`Installing nest at ${plistPath()}`);
|
|
4686
|
+
consola28.info(` nest binary: ${nestBin}`);
|
|
4687
|
+
consola28.info(` apes binary: ${apesBin}`);
|
|
4688
|
+
consola28.info(` HTTP port: ${port}`);
|
|
4749
4689
|
if (typeof args["bridge-model"] === "string" && args["bridge-model"]) {
|
|
4750
4690
|
writeBridgeModelDefault(args["bridge-model"]);
|
|
4751
|
-
|
|
4691
|
+
consola28.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
|
|
4752
4692
|
}
|
|
4753
4693
|
installAdapter2();
|
|
4754
|
-
|
|
4755
|
-
|
|
4694
|
+
mkdirSync5(join11(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4695
|
+
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
4756
4696
|
const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
|
|
4757
4697
|
let existing = "";
|
|
4758
4698
|
try {
|
|
4759
|
-
existing =
|
|
4699
|
+
existing = readFileSync11(plistPath(), "utf8");
|
|
4760
4700
|
} catch {
|
|
4761
4701
|
}
|
|
4762
4702
|
if (existing !== desired) {
|
|
4763
|
-
|
|
4764
|
-
|
|
4703
|
+
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
4704
|
+
consola28.success("Wrote launchd plist");
|
|
4765
4705
|
} else {
|
|
4766
|
-
|
|
4706
|
+
consola28.info("plist already up to date");
|
|
4767
4707
|
}
|
|
4768
4708
|
const uid = userInfo2().uid;
|
|
4769
4709
|
try {
|
|
@@ -4771,208 +4711,116 @@ var installNestCommand = defineCommand32({
|
|
|
4771
4711
|
} catch {
|
|
4772
4712
|
}
|
|
4773
4713
|
execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4714
|
+
consola28.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4715
|
+
consola28.info("");
|
|
4716
|
+
consola28.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
4717
|
+
consola28.info("");
|
|
4718
|
+
consola28.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
4719
|
+
consola28.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
4720
|
+
consola28.info("");
|
|
4721
|
+
consola28.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
4782
4722
|
}
|
|
4783
4723
|
});
|
|
4784
4724
|
|
|
4785
4725
|
// src/commands/nest/list.ts
|
|
4786
|
-
import process3 from "process";
|
|
4787
4726
|
import { defineCommand as defineCommand33 } from "citty";
|
|
4788
|
-
import
|
|
4727
|
+
import consola29 from "consola";
|
|
4789
4728
|
var listNestCommand = defineCommand33({
|
|
4790
4729
|
meta: {
|
|
4791
4730
|
name: "list",
|
|
4792
|
-
description: "List agents registered with the local nest.
|
|
4731
|
+
description: "List agents registered with the local nest. File-based intent."
|
|
4793
4732
|
},
|
|
4794
4733
|
args: {
|
|
4795
|
-
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
4796
4734
|
json: { type: "boolean", description: "JSON output for scripts" }
|
|
4797
4735
|
},
|
|
4798
4736
|
async run({ args }) {
|
|
4799
|
-
const
|
|
4800
|
-
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
4801
|
-
let resp;
|
|
4802
|
-
try {
|
|
4803
|
-
const res = await fetch(`${base}/agents`, {
|
|
4804
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4805
|
-
});
|
|
4806
|
-
if (!res.ok) {
|
|
4807
|
-
const text = await res.text().catch(() => "");
|
|
4808
|
-
throw new CliError(`nest GET /agents failed: ${res.status} ${text}`);
|
|
4809
|
-
}
|
|
4810
|
-
resp = await res.json();
|
|
4811
|
-
} catch (err) {
|
|
4812
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
4813
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4814
|
-
consola30.error(`Nest daemon is not running at ${base}`);
|
|
4815
|
-
consola30.info(" Run: apes nest install");
|
|
4816
|
-
process3.exit(2);
|
|
4817
|
-
}
|
|
4818
|
-
throw err;
|
|
4819
|
-
}
|
|
4737
|
+
const result = await dispatchIntent({ action: "list" });
|
|
4820
4738
|
if (args.json) {
|
|
4821
|
-
console.log(JSON.stringify(
|
|
4739
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4822
4740
|
return;
|
|
4823
4741
|
}
|
|
4824
|
-
if (
|
|
4825
|
-
|
|
4742
|
+
if (result.agents.length === 0) {
|
|
4743
|
+
consola29.info("(no agents registered with this nest)");
|
|
4826
4744
|
return;
|
|
4827
4745
|
}
|
|
4828
|
-
|
|
4829
|
-
for (const a of
|
|
4746
|
+
consola29.info(`${result.agents.length} agent(s) registered with this nest:`);
|
|
4747
|
+
for (const a of result.agents) {
|
|
4830
4748
|
const bridge = a.bridge ? " bridge=on" : "";
|
|
4831
|
-
|
|
4749
|
+
consola29.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
4832
4750
|
}
|
|
4833
4751
|
}
|
|
4834
4752
|
});
|
|
4835
4753
|
|
|
4836
4754
|
// src/commands/nest/spawn.ts
|
|
4837
|
-
import process4 from "process";
|
|
4838
4755
|
import { defineCommand as defineCommand34 } from "citty";
|
|
4839
|
-
import
|
|
4756
|
+
import consola30 from "consola";
|
|
4840
4757
|
var spawnNestCommand = defineCommand34({
|
|
4841
4758
|
meta: {
|
|
4842
4759
|
name: "spawn",
|
|
4843
|
-
description: "Spawn a new agent on the local nest.
|
|
4760
|
+
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."
|
|
4844
4761
|
},
|
|
4845
4762
|
args: {
|
|
4846
4763
|
name: { type: "positional", required: true, description: "Agent name (lowercase, [a-z0-9-], max 24 chars)" },
|
|
4847
4764
|
"no-bridge": { type: "boolean", description: "Skip installing the chat-bridge daemon (default: install it)" },
|
|
4848
4765
|
"bridge-key": { type: "string", description: "Override LITELLM_API_KEY (default: read from ~/litellm/.env)" },
|
|
4849
4766
|
"bridge-base-url": { type: "string", description: "Override LITELLM_BASE_URL (default: read from ~/litellm/.env)" },
|
|
4850
|
-
"bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" }
|
|
4851
|
-
"port": { type: "string", description: "Override nest port (default: 9091)" }
|
|
4767
|
+
"bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" }
|
|
4852
4768
|
},
|
|
4853
4769
|
async run({ args }) {
|
|
4854
4770
|
const name = String(args.name);
|
|
4855
|
-
const
|
|
4856
|
-
|
|
4857
|
-
const reqBody = {
|
|
4771
|
+
const intent = {
|
|
4772
|
+
action: "spawn",
|
|
4858
4773
|
name,
|
|
4859
4774
|
bridge: !args["no-bridge"]
|
|
4860
4775
|
};
|
|
4861
|
-
if (typeof args["bridge-key"] === "string")
|
|
4862
|
-
if (typeof args["bridge-base-url"] === "string")
|
|
4863
|
-
if (typeof args["bridge-model"] === "string")
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
const res = await fetch(`${base}/agents`, {
|
|
4867
|
-
method: "POST",
|
|
4868
|
-
headers: {
|
|
4869
|
-
"Authorization": `Bearer ${token}`,
|
|
4870
|
-
"Content-Type": "application/json"
|
|
4871
|
-
},
|
|
4872
|
-
body: JSON.stringify(reqBody)
|
|
4873
|
-
});
|
|
4874
|
-
if (!res.ok) {
|
|
4875
|
-
const text = await res.text().catch(() => "");
|
|
4876
|
-
throw new CliError(`nest POST /agents failed: ${res.status} ${text}`);
|
|
4877
|
-
}
|
|
4878
|
-
resp = await res.json();
|
|
4879
|
-
} catch (err) {
|
|
4880
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
4881
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4882
|
-
consola31.error(`Nest daemon is not running at ${base}`);
|
|
4883
|
-
consola31.info(" Run: apes nest install");
|
|
4884
|
-
process4.exit(2);
|
|
4885
|
-
}
|
|
4886
|
-
throw err;
|
|
4887
|
-
}
|
|
4888
|
-
consola31.success(`Spawned ${resp.name} (uid=${resp.uid}, home=${resp.home})`);
|
|
4889
|
-
}
|
|
4890
|
-
});
|
|
4891
|
-
|
|
4892
|
-
// src/commands/nest/status.ts
|
|
4893
|
-
import process5 from "process";
|
|
4894
|
-
import { defineCommand as defineCommand35 } from "citty";
|
|
4895
|
-
import consola32 from "consola";
|
|
4896
|
-
var statusNestCommand = defineCommand35({
|
|
4897
|
-
meta: {
|
|
4898
|
-
name: "status",
|
|
4899
|
-
description: "Print health of the local nest-daemon (agents registered). Goes through DDISA grants."
|
|
4900
|
-
},
|
|
4901
|
-
args: {
|
|
4902
|
-
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
4903
|
-
json: { type: "boolean", description: "JSON output for scripts" }
|
|
4904
|
-
},
|
|
4905
|
-
async run({ args }) {
|
|
4906
|
-
const token = await requestNestGrant({ command: ["nest", "status"] });
|
|
4907
|
-
const base = nestBaseUrl(args.port ? Number(args.port) : void 0);
|
|
4908
|
-
let status;
|
|
4909
|
-
try {
|
|
4910
|
-
const res = await fetch(`${base}/status`, {
|
|
4911
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4912
|
-
});
|
|
4913
|
-
if (!res.ok) {
|
|
4914
|
-
const text = await res.text().catch(() => "");
|
|
4915
|
-
throw new CliError(`nest GET /status failed: ${res.status} ${text}`);
|
|
4916
|
-
}
|
|
4917
|
-
status = await res.json();
|
|
4918
|
-
} catch (err) {
|
|
4919
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
4920
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4921
|
-
consola32.error(`Nest daemon is not running at ${base}`);
|
|
4922
|
-
consola32.info(" Run: apes nest install");
|
|
4923
|
-
process5.exit(2);
|
|
4924
|
-
}
|
|
4925
|
-
throw err;
|
|
4926
|
-
}
|
|
4927
|
-
if (args.json) {
|
|
4928
|
-
console.log(JSON.stringify(status, null, 2));
|
|
4929
|
-
return;
|
|
4930
|
-
}
|
|
4931
|
-
consola32.info(`Nest at ${base} \u2014 ${status.agents} agent(s) registered`);
|
|
4776
|
+
if (typeof args["bridge-key"] === "string") intent.bridgeKey = args["bridge-key"];
|
|
4777
|
+
if (typeof args["bridge-base-url"] === "string") intent.bridgeBaseUrl = args["bridge-base-url"];
|
|
4778
|
+
if (typeof args["bridge-model"] === "string") intent.bridgeModel = args["bridge-model"];
|
|
4779
|
+
const result = await dispatchIntent(intent);
|
|
4780
|
+
consola30.success(`Spawned ${result.name} (uid=${result.uid}, home=${result.home})`);
|
|
4932
4781
|
}
|
|
4933
4782
|
});
|
|
4934
4783
|
|
|
4935
4784
|
// src/commands/nest/uninstall.ts
|
|
4936
4785
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4937
|
-
import { existsSync as
|
|
4938
|
-
import { homedir as
|
|
4939
|
-
import { join as
|
|
4940
|
-
import { defineCommand as
|
|
4941
|
-
import
|
|
4786
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync2 } from "fs";
|
|
4787
|
+
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
4788
|
+
import { join as join12 } from "path";
|
|
4789
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
4790
|
+
import consola31 from "consola";
|
|
4942
4791
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4943
|
-
var uninstallNestCommand =
|
|
4792
|
+
var uninstallNestCommand = defineCommand35({
|
|
4944
4793
|
meta: {
|
|
4945
4794
|
name: "uninstall",
|
|
4946
4795
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4947
4796
|
},
|
|
4948
4797
|
async run() {
|
|
4949
4798
|
const uid = userInfo3().uid;
|
|
4950
|
-
const path2 =
|
|
4799
|
+
const path2 = join12(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4951
4800
|
try {
|
|
4952
4801
|
execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4953
|
-
|
|
4802
|
+
consola31.success("Nest daemon stopped");
|
|
4954
4803
|
} catch {
|
|
4955
|
-
|
|
4804
|
+
consola31.info("Nest daemon was not loaded");
|
|
4956
4805
|
}
|
|
4957
|
-
if (
|
|
4958
|
-
|
|
4959
|
-
|
|
4806
|
+
if (existsSync14(path2)) {
|
|
4807
|
+
unlinkSync2(path2);
|
|
4808
|
+
consola31.success(`Removed ${path2}`);
|
|
4960
4809
|
}
|
|
4961
|
-
|
|
4810
|
+
consola31.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4962
4811
|
}
|
|
4963
4812
|
});
|
|
4964
4813
|
|
|
4965
4814
|
// src/commands/nest/index.ts
|
|
4966
|
-
var nestCommand =
|
|
4815
|
+
var nestCommand = defineCommand36({
|
|
4967
4816
|
meta: {
|
|
4968
4817
|
name: "nest",
|
|
4969
|
-
description: "Manage the local Nest control-plane daemon. One-time setup: `install`
|
|
4818
|
+
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."
|
|
4970
4819
|
},
|
|
4971
4820
|
subCommands: {
|
|
4972
4821
|
install: installNestCommand,
|
|
4973
4822
|
enroll: enrollNestCommand,
|
|
4974
4823
|
authorize: authorizeNestCommand,
|
|
4975
|
-
status: statusNestCommand,
|
|
4976
4824
|
list: listNestCommand,
|
|
4977
4825
|
spawn: spawnNestCommand,
|
|
4978
4826
|
destroy: destroyNestCommand,
|
|
@@ -4981,12 +4829,12 @@ var nestCommand = defineCommand37({
|
|
|
4981
4829
|
});
|
|
4982
4830
|
|
|
4983
4831
|
// src/commands/yolo/index.ts
|
|
4984
|
-
import { defineCommand as
|
|
4832
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
4985
4833
|
|
|
4986
4834
|
// src/commands/yolo/clear.ts
|
|
4987
|
-
import { defineCommand as
|
|
4988
|
-
import
|
|
4989
|
-
var yoloClearCommand =
|
|
4835
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
4836
|
+
import consola32 from "consola";
|
|
4837
|
+
var yoloClearCommand = defineCommand37({
|
|
4990
4838
|
meta: {
|
|
4991
4839
|
name: "clear",
|
|
4992
4840
|
description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
|
|
@@ -5015,15 +4863,15 @@ var yoloClearCommand = defineCommand38({
|
|
|
5015
4863
|
const text = await res.text().catch(() => "");
|
|
5016
4864
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
5017
4865
|
}
|
|
5018
|
-
|
|
4866
|
+
consola32.success(`YOLO-policy cleared on ${email}`);
|
|
5019
4867
|
}
|
|
5020
4868
|
});
|
|
5021
4869
|
|
|
5022
4870
|
// src/commands/yolo/set.ts
|
|
5023
|
-
import { defineCommand as
|
|
5024
|
-
import
|
|
4871
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
4872
|
+
import consola33 from "consola";
|
|
5025
4873
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
5026
|
-
var yoloSetCommand =
|
|
4874
|
+
var yoloSetCommand = defineCommand38({
|
|
5027
4875
|
meta: {
|
|
5028
4876
|
name: "set",
|
|
5029
4877
|
description: "Write a YOLO-policy on a DDISA agent you own"
|
|
@@ -5071,12 +4919,12 @@ var yoloSetCommand = defineCommand39({
|
|
|
5071
4919
|
const denyPatterns = parseList(args.deny);
|
|
5072
4920
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
5073
4921
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
if (allowPatterns.length)
|
|
5077
|
-
if (denyPatterns.length)
|
|
5078
|
-
if (denyRiskThreshold)
|
|
5079
|
-
if (expiresAt)
|
|
4922
|
+
consola33.info(`Setting YOLO-policy on ${email}`);
|
|
4923
|
+
consola33.info(` mode: ${mode}`);
|
|
4924
|
+
if (allowPatterns.length) consola33.info(` allow_patterns: ${allowPatterns.join(", ")}`);
|
|
4925
|
+
if (denyPatterns.length) consola33.info(` deny_patterns: ${denyPatterns.join(", ")}`);
|
|
4926
|
+
if (denyRiskThreshold) consola33.info(` deny_risk: ${denyRiskThreshold}`);
|
|
4927
|
+
if (expiresAt) consola33.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
5080
4928
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
5081
4929
|
const res = await fetch(url, {
|
|
5082
4930
|
method: "PUT",
|
|
@@ -5096,7 +4944,7 @@ var yoloSetCommand = defineCommand39({
|
|
|
5096
4944
|
const text = await res.text().catch(() => "");
|
|
5097
4945
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
5098
4946
|
}
|
|
5099
|
-
|
|
4947
|
+
consola33.success(`YOLO-policy applied to ${email}`);
|
|
5100
4948
|
}
|
|
5101
4949
|
});
|
|
5102
4950
|
function parseList(s) {
|
|
@@ -5114,9 +4962,9 @@ function parseExpiresIn(s) {
|
|
|
5114
4962
|
}
|
|
5115
4963
|
|
|
5116
4964
|
// src/commands/yolo/show.ts
|
|
5117
|
-
import { defineCommand as
|
|
5118
|
-
import
|
|
5119
|
-
var yoloShowCommand =
|
|
4965
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
4966
|
+
import consola34 from "consola";
|
|
4967
|
+
var yoloShowCommand = defineCommand39({
|
|
5120
4968
|
meta: {
|
|
5121
4969
|
name: "show",
|
|
5122
4970
|
description: "Print the YOLO-policy currently set on a DDISA agent"
|
|
@@ -5153,17 +5001,17 @@ var yoloShowCommand = defineCommand40({
|
|
|
5153
5001
|
console.log(JSON.stringify(policy, null, 2));
|
|
5154
5002
|
return;
|
|
5155
5003
|
}
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5004
|
+
consola34.info(`YOLO-policy for ${email}`);
|
|
5005
|
+
consola34.info(` mode: ${policy.mode}`);
|
|
5006
|
+
consola34.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
|
|
5007
|
+
consola34.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
|
|
5008
|
+
consola34.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
|
|
5009
|
+
consola34.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
|
|
5162
5010
|
}
|
|
5163
5011
|
});
|
|
5164
5012
|
|
|
5165
5013
|
// src/commands/yolo/index.ts
|
|
5166
|
-
var yoloCommand =
|
|
5014
|
+
var yoloCommand = defineCommand40({
|
|
5167
5015
|
meta: {
|
|
5168
5016
|
name: "yolo",
|
|
5169
5017
|
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)."
|
|
@@ -5176,15 +5024,15 @@ var yoloCommand = defineCommand41({
|
|
|
5176
5024
|
});
|
|
5177
5025
|
|
|
5178
5026
|
// src/commands/adapter/index.ts
|
|
5179
|
-
import { defineCommand as
|
|
5180
|
-
import
|
|
5181
|
-
var adapterCommand =
|
|
5027
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
5028
|
+
import consola35 from "consola";
|
|
5029
|
+
var adapterCommand = defineCommand41({
|
|
5182
5030
|
meta: {
|
|
5183
5031
|
name: "adapter",
|
|
5184
5032
|
description: "Manage CLI adapters"
|
|
5185
5033
|
},
|
|
5186
5034
|
subCommands: {
|
|
5187
|
-
list:
|
|
5035
|
+
list: defineCommand41({
|
|
5188
5036
|
meta: {
|
|
5189
5037
|
name: "list",
|
|
5190
5038
|
description: "List available adapters"
|
|
@@ -5215,7 +5063,7 @@ var adapterCommand = defineCommand42({
|
|
|
5215
5063
|
`);
|
|
5216
5064
|
return;
|
|
5217
5065
|
}
|
|
5218
|
-
|
|
5066
|
+
consola35.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
5219
5067
|
for (const a of index2.adapters) {
|
|
5220
5068
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
5221
5069
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -5237,7 +5085,7 @@ var adapterCommand = defineCommand42({
|
|
|
5237
5085
|
return;
|
|
5238
5086
|
}
|
|
5239
5087
|
if (local.length === 0) {
|
|
5240
|
-
|
|
5088
|
+
consola35.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
5241
5089
|
return;
|
|
5242
5090
|
}
|
|
5243
5091
|
for (const a of local) {
|
|
@@ -5245,7 +5093,7 @@ var adapterCommand = defineCommand42({
|
|
|
5245
5093
|
}
|
|
5246
5094
|
}
|
|
5247
5095
|
}),
|
|
5248
|
-
install:
|
|
5096
|
+
install: defineCommand41({
|
|
5249
5097
|
meta: {
|
|
5250
5098
|
name: "install",
|
|
5251
5099
|
description: "Install an adapter from the registry"
|
|
@@ -5274,24 +5122,24 @@ var adapterCommand = defineCommand42({
|
|
|
5274
5122
|
for (const id of ids) {
|
|
5275
5123
|
const entry = findAdapter(index, id);
|
|
5276
5124
|
if (!entry) {
|
|
5277
|
-
|
|
5125
|
+
consola35.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
5278
5126
|
continue;
|
|
5279
5127
|
}
|
|
5280
5128
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
5281
5129
|
if (conflicts.length > 0) {
|
|
5282
5130
|
for (const c2 of conflicts) {
|
|
5283
|
-
|
|
5284
|
-
|
|
5131
|
+
consola35.warn(`Conflicting adapter found: ${c2.path} (id: ${c2.adapterId}, executable: ${c2.executable})`);
|
|
5132
|
+
consola35.warn(` Remove it with: apes adapter remove ${c2.adapterId}`);
|
|
5285
5133
|
}
|
|
5286
5134
|
}
|
|
5287
5135
|
const result = await installAdapter(entry, { local });
|
|
5288
5136
|
const verb = result.updated ? "Updated" : "Installed";
|
|
5289
|
-
|
|
5290
|
-
|
|
5137
|
+
consola35.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
5138
|
+
consola35.info(`Digest: ${result.digest}`);
|
|
5291
5139
|
}
|
|
5292
5140
|
}
|
|
5293
5141
|
}),
|
|
5294
|
-
remove:
|
|
5142
|
+
remove: defineCommand41({
|
|
5295
5143
|
meta: {
|
|
5296
5144
|
name: "remove",
|
|
5297
5145
|
description: "Remove an installed adapter"
|
|
@@ -5314,9 +5162,9 @@ var adapterCommand = defineCommand42({
|
|
|
5314
5162
|
let failed = false;
|
|
5315
5163
|
for (const id of ids) {
|
|
5316
5164
|
if (removeAdapter(id, local)) {
|
|
5317
|
-
|
|
5165
|
+
consola35.success(`Removed adapter: ${id}`);
|
|
5318
5166
|
} else {
|
|
5319
|
-
|
|
5167
|
+
consola35.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
5320
5168
|
failed = true;
|
|
5321
5169
|
}
|
|
5322
5170
|
}
|
|
@@ -5324,7 +5172,7 @@ var adapterCommand = defineCommand42({
|
|
|
5324
5172
|
throw new CliError("Some adapters could not be removed");
|
|
5325
5173
|
}
|
|
5326
5174
|
}),
|
|
5327
|
-
info:
|
|
5175
|
+
info: defineCommand41({
|
|
5328
5176
|
meta: {
|
|
5329
5177
|
name: "info",
|
|
5330
5178
|
description: "Show detailed adapter information"
|
|
@@ -5366,7 +5214,7 @@ var adapterCommand = defineCommand42({
|
|
|
5366
5214
|
}
|
|
5367
5215
|
}
|
|
5368
5216
|
}),
|
|
5369
|
-
search:
|
|
5217
|
+
search: defineCommand41({
|
|
5370
5218
|
meta: {
|
|
5371
5219
|
name: "search",
|
|
5372
5220
|
description: "Search adapters in the registry"
|
|
@@ -5398,7 +5246,7 @@ var adapterCommand = defineCommand42({
|
|
|
5398
5246
|
return;
|
|
5399
5247
|
}
|
|
5400
5248
|
if (results.length === 0) {
|
|
5401
|
-
|
|
5249
|
+
consola35.info(`No adapters matching "${query}"`);
|
|
5402
5250
|
return;
|
|
5403
5251
|
}
|
|
5404
5252
|
for (const a of results) {
|
|
@@ -5407,7 +5255,7 @@ var adapterCommand = defineCommand42({
|
|
|
5407
5255
|
}
|
|
5408
5256
|
}
|
|
5409
5257
|
}),
|
|
5410
|
-
update:
|
|
5258
|
+
update: defineCommand41({
|
|
5411
5259
|
meta: {
|
|
5412
5260
|
name: "update",
|
|
5413
5261
|
description: "Update installed adapters"
|
|
@@ -5433,33 +5281,33 @@ var adapterCommand = defineCommand42({
|
|
|
5433
5281
|
const targetId = args.id ? String(args.id) : void 0;
|
|
5434
5282
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
5435
5283
|
if (targets.length === 0) {
|
|
5436
|
-
|
|
5284
|
+
consola35.info("No adapters installed to update.");
|
|
5437
5285
|
return;
|
|
5438
5286
|
}
|
|
5439
5287
|
for (const id of targets) {
|
|
5440
5288
|
const entry = findAdapter(index, id);
|
|
5441
5289
|
if (!entry) {
|
|
5442
|
-
|
|
5290
|
+
consola35.warn(`${id}: not found in registry, skipping`);
|
|
5443
5291
|
continue;
|
|
5444
5292
|
}
|
|
5445
5293
|
const localDigest = getInstalledDigest(id, false);
|
|
5446
5294
|
if (localDigest === entry.digest) {
|
|
5447
|
-
|
|
5295
|
+
consola35.info(`${id}: already up to date`);
|
|
5448
5296
|
continue;
|
|
5449
5297
|
}
|
|
5450
5298
|
if (localDigest && !args.yes) {
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5299
|
+
consola35.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
5300
|
+
consola35.info(` Old: ${localDigest}`);
|
|
5301
|
+
consola35.info(` New: ${entry.digest}`);
|
|
5302
|
+
consola35.info(" Use --yes to confirm");
|
|
5455
5303
|
continue;
|
|
5456
5304
|
}
|
|
5457
5305
|
const result = await installAdapter(entry);
|
|
5458
|
-
|
|
5306
|
+
consola35.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
5459
5307
|
}
|
|
5460
5308
|
}
|
|
5461
5309
|
}),
|
|
5462
|
-
verify:
|
|
5310
|
+
verify: defineCommand41({
|
|
5463
5311
|
meta: {
|
|
5464
5312
|
name: "verify",
|
|
5465
5313
|
description: "Verify installed adapter against registry digest"
|
|
@@ -5492,7 +5340,7 @@ var adapterCommand = defineCommand42({
|
|
|
5492
5340
|
if (!localDigest)
|
|
5493
5341
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
5494
5342
|
if (localDigest === entry.digest) {
|
|
5495
|
-
|
|
5343
|
+
consola35.success(`${id}: digest matches registry`);
|
|
5496
5344
|
} else {
|
|
5497
5345
|
console.log(` Local: ${localDigest}`);
|
|
5498
5346
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -5505,10 +5353,10 @@ var adapterCommand = defineCommand42({
|
|
|
5505
5353
|
|
|
5506
5354
|
// src/commands/run.ts
|
|
5507
5355
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
5508
|
-
import { hostname as
|
|
5356
|
+
import { hostname as hostname5 } from "os";
|
|
5509
5357
|
import { basename } from "path";
|
|
5510
|
-
import { defineCommand as
|
|
5511
|
-
import
|
|
5358
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5359
|
+
import consola36 from "consola";
|
|
5512
5360
|
function shouldWaitForGrant(args) {
|
|
5513
5361
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
5514
5362
|
}
|
|
@@ -5545,7 +5393,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
5545
5393
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
5546
5394
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
5547
5395
|
if (mode === "human") {
|
|
5548
|
-
|
|
5396
|
+
consola36.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
5549
5397
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
5550
5398
|
console.log(` Check status: ${statusCmd}`);
|
|
5551
5399
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -5555,7 +5403,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
5555
5403
|
return;
|
|
5556
5404
|
}
|
|
5557
5405
|
const maxMin = getPollMaxMinutes();
|
|
5558
|
-
|
|
5406
|
+
consola36.success(`Grant ${grant.id} created (pending approval)`);
|
|
5559
5407
|
console.log(` Approve: ${approveUrl}`);
|
|
5560
5408
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
5561
5409
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -5577,7 +5425,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
5577
5425
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
5578
5426
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
5579
5427
|
}
|
|
5580
|
-
var runCommand =
|
|
5428
|
+
var runCommand = defineCommand42({
|
|
5581
5429
|
meta: {
|
|
5582
5430
|
name: "run",
|
|
5583
5431
|
description: "Execute a grant-secured command"
|
|
@@ -5666,7 +5514,7 @@ async function runShellMode(command, args) {
|
|
|
5666
5514
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
5667
5515
|
if (adapterHandled) return;
|
|
5668
5516
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5669
|
-
const targetHost = args.host ||
|
|
5517
|
+
const targetHost = args.host || hostname5();
|
|
5670
5518
|
try {
|
|
5671
5519
|
const grants = await apiFetch(
|
|
5672
5520
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -5680,7 +5528,7 @@ async function runShellMode(command, args) {
|
|
|
5680
5528
|
}
|
|
5681
5529
|
} catch {
|
|
5682
5530
|
}
|
|
5683
|
-
|
|
5531
|
+
consola36.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
5684
5532
|
const grant = await apiFetch(grantsUrl, {
|
|
5685
5533
|
method: "POST",
|
|
5686
5534
|
body: {
|
|
@@ -5700,8 +5548,8 @@ async function runShellMode(command, args) {
|
|
|
5700
5548
|
host: targetHost
|
|
5701
5549
|
});
|
|
5702
5550
|
if (shouldWaitForGrant(args)) {
|
|
5703
|
-
|
|
5704
|
-
|
|
5551
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5552
|
+
consola36.info("Waiting for approval...");
|
|
5705
5553
|
const maxWait = 3e5;
|
|
5706
5554
|
const interval = 3e3;
|
|
5707
5555
|
const start = Date.now();
|
|
@@ -5732,13 +5580,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5732
5580
|
try {
|
|
5733
5581
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
5734
5582
|
} catch (err) {
|
|
5735
|
-
|
|
5583
|
+
consola36.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
5736
5584
|
return false;
|
|
5737
5585
|
}
|
|
5738
5586
|
try {
|
|
5739
5587
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5740
5588
|
if (existingGrantId) {
|
|
5741
|
-
|
|
5589
|
+
consola36.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
5742
5590
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5743
5591
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5744
5592
|
return true;
|
|
@@ -5746,7 +5594,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5746
5594
|
} catch {
|
|
5747
5595
|
}
|
|
5748
5596
|
const approval = args.approval ?? "once";
|
|
5749
|
-
|
|
5597
|
+
consola36.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
5750
5598
|
const grant = await createShapesGrant(resolved, {
|
|
5751
5599
|
idp,
|
|
5752
5600
|
approval,
|
|
@@ -5754,19 +5602,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5754
5602
|
});
|
|
5755
5603
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5756
5604
|
const n2 = grant.similar_grants.similar_grants.length;
|
|
5757
|
-
|
|
5758
|
-
|
|
5605
|
+
consola36.info("");
|
|
5606
|
+
consola36.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5759
5607
|
}
|
|
5760
5608
|
notifyGrantPending({
|
|
5761
5609
|
grantId: grant.id,
|
|
5762
5610
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
5763
5611
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
5764
5612
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
5765
|
-
host: args.host ||
|
|
5613
|
+
host: args.host || hostname5()
|
|
5766
5614
|
});
|
|
5767
5615
|
if (shouldWaitForGrant(args)) {
|
|
5768
|
-
|
|
5769
|
-
|
|
5616
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5617
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5770
5618
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5771
5619
|
if (status !== "approved")
|
|
5772
5620
|
throw new CliError(`Grant ${status}`);
|
|
@@ -5840,7 +5688,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5840
5688
|
try {
|
|
5841
5689
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5842
5690
|
if (existingGrantId) {
|
|
5843
|
-
|
|
5691
|
+
consola36.info(`Reusing existing grant: ${existingGrantId}`);
|
|
5844
5692
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5845
5693
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5846
5694
|
return;
|
|
@@ -5854,17 +5702,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5854
5702
|
});
|
|
5855
5703
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5856
5704
|
const n2 = grant.similar_grants.similar_grants.length;
|
|
5857
|
-
|
|
5858
|
-
|
|
5705
|
+
consola36.info("");
|
|
5706
|
+
consola36.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5859
5707
|
if (grant.similar_grants.widened_details?.length) {
|
|
5860
5708
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
5861
|
-
|
|
5709
|
+
consola36.info(` Broader scope: ${wider}`);
|
|
5862
5710
|
}
|
|
5863
|
-
|
|
5711
|
+
consola36.info("");
|
|
5864
5712
|
}
|
|
5865
5713
|
if (shouldWaitForGrant(args)) {
|
|
5866
|
-
|
|
5867
|
-
|
|
5714
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5715
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5868
5716
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5869
5717
|
if (status !== "approved")
|
|
5870
5718
|
throw new Error(`Grant ${status}`);
|
|
@@ -5883,8 +5731,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5883
5731
|
const idp = getIdpUrl(args.idp);
|
|
5884
5732
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5885
5733
|
const command = action.split(" ");
|
|
5886
|
-
const targetHost = args.host ||
|
|
5887
|
-
|
|
5734
|
+
const targetHost = args.host || hostname5();
|
|
5735
|
+
consola36.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5888
5736
|
const grant = await apiFetch(grantsUrl, {
|
|
5889
5737
|
method: "POST",
|
|
5890
5738
|
body: {
|
|
@@ -5901,9 +5749,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5901
5749
|
printPendingGrantInfo(grant, idp);
|
|
5902
5750
|
throw new CliExit(getAsyncExitCode());
|
|
5903
5751
|
}
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5752
|
+
consola36.success(`Grant requested: ${grant.id}`);
|
|
5753
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5754
|
+
consola36.info("Waiting for approval...");
|
|
5907
5755
|
const maxWait = 15 * 60 * 1e3;
|
|
5908
5756
|
const interval = 3e3;
|
|
5909
5757
|
const start = Date.now();
|
|
@@ -5911,7 +5759,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5911
5759
|
while (Date.now() - start < maxWait) {
|
|
5912
5760
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5913
5761
|
if (status.status === "approved") {
|
|
5914
|
-
|
|
5762
|
+
consola36.success("Grant approved!");
|
|
5915
5763
|
approved = true;
|
|
5916
5764
|
break;
|
|
5917
5765
|
}
|
|
@@ -5926,12 +5774,12 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5926
5774
|
`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.`
|
|
5927
5775
|
);
|
|
5928
5776
|
}
|
|
5929
|
-
|
|
5777
|
+
consola36.info("Fetching grant token...");
|
|
5930
5778
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5931
5779
|
method: "POST"
|
|
5932
5780
|
});
|
|
5933
5781
|
if (audience === "escapes") {
|
|
5934
|
-
|
|
5782
|
+
consola36.info(`Executing: ${command.join(" ")}`);
|
|
5935
5783
|
try {
|
|
5936
5784
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5937
5785
|
execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
@@ -5949,8 +5797,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5949
5797
|
|
|
5950
5798
|
// src/commands/proxy.ts
|
|
5951
5799
|
import { spawn as spawn2 } from "child_process";
|
|
5952
|
-
import { defineCommand as
|
|
5953
|
-
import
|
|
5800
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5801
|
+
import consola37 from "consola";
|
|
5954
5802
|
|
|
5955
5803
|
// src/proxy/config.ts
|
|
5956
5804
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5984,10 +5832,10 @@ note = "VPC-internal hostname suffix"
|
|
|
5984
5832
|
|
|
5985
5833
|
// src/proxy/local-proxy.ts
|
|
5986
5834
|
import { spawn } from "child_process";
|
|
5987
|
-
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as
|
|
5835
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
5988
5836
|
import { createRequire } from "module";
|
|
5989
5837
|
import { tmpdir as tmpdir3 } from "os";
|
|
5990
|
-
import { dirname as dirname4, join as
|
|
5838
|
+
import { dirname as dirname4, join as join13, resolve as resolve3 } from "path";
|
|
5991
5839
|
var require2 = createRequire(import.meta.url);
|
|
5992
5840
|
function findProxyBin() {
|
|
5993
5841
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -5999,9 +5847,9 @@ function findProxyBin() {
|
|
|
5999
5847
|
return resolve3(dirname4(pkgPath), binRel);
|
|
6000
5848
|
}
|
|
6001
5849
|
async function startEphemeralProxy(configToml) {
|
|
6002
|
-
const tmpDir = mkdtempSync3(
|
|
6003
|
-
const configPath =
|
|
6004
|
-
|
|
5850
|
+
const tmpDir = mkdtempSync3(join13(tmpdir3(), "openape-proxy-"));
|
|
5851
|
+
const configPath = join13(tmpDir, "config.toml");
|
|
5852
|
+
writeFileSync8(configPath, configToml, { mode: 384 });
|
|
6005
5853
|
const binPath = findProxyBin();
|
|
6006
5854
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
6007
5855
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6093,10 +5941,10 @@ function resolveProxyConfigOptions() {
|
|
|
6093
5941
|
77
|
|
6094
5942
|
);
|
|
6095
5943
|
}
|
|
6096
|
-
|
|
5944
|
+
consola37.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
6097
5945
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
6098
5946
|
}
|
|
6099
|
-
var proxyCommand =
|
|
5947
|
+
var proxyCommand = defineCommand43({
|
|
6100
5948
|
meta: {
|
|
6101
5949
|
name: "proxy",
|
|
6102
5950
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -6118,12 +5966,12 @@ var proxyCommand = defineCommand44({
|
|
|
6118
5966
|
let close = null;
|
|
6119
5967
|
if (reuseUrl) {
|
|
6120
5968
|
proxyUrl = reuseUrl;
|
|
6121
|
-
|
|
5969
|
+
consola37.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
6122
5970
|
} else {
|
|
6123
5971
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
6124
5972
|
proxyUrl = ephemeral.url;
|
|
6125
5973
|
close = ephemeral.close;
|
|
6126
|
-
|
|
5974
|
+
consola37.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
6127
5975
|
}
|
|
6128
5976
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
6129
5977
|
const childEnv = {
|
|
@@ -6155,7 +6003,7 @@ var proxyCommand = defineCommand44({
|
|
|
6155
6003
|
else resolveExit(code ?? 0);
|
|
6156
6004
|
});
|
|
6157
6005
|
child.once("error", (err) => {
|
|
6158
|
-
|
|
6006
|
+
consola37.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
6159
6007
|
resolveExit(127);
|
|
6160
6008
|
});
|
|
6161
6009
|
});
|
|
@@ -6169,8 +6017,8 @@ function signalNumber(signal) {
|
|
|
6169
6017
|
}
|
|
6170
6018
|
|
|
6171
6019
|
// src/commands/explain.ts
|
|
6172
|
-
import { defineCommand as
|
|
6173
|
-
var explainCommand =
|
|
6020
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
6021
|
+
var explainCommand = defineCommand44({
|
|
6174
6022
|
meta: {
|
|
6175
6023
|
name: "explain",
|
|
6176
6024
|
description: "Show what permission a command would need"
|
|
@@ -6208,9 +6056,9 @@ var explainCommand = defineCommand45({
|
|
|
6208
6056
|
});
|
|
6209
6057
|
|
|
6210
6058
|
// src/commands/config/get.ts
|
|
6211
|
-
import { defineCommand as
|
|
6212
|
-
import
|
|
6213
|
-
var configGetCommand =
|
|
6059
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
6060
|
+
import consola38 from "consola";
|
|
6061
|
+
var configGetCommand = defineCommand45({
|
|
6214
6062
|
meta: {
|
|
6215
6063
|
name: "get",
|
|
6216
6064
|
description: "Get a configuration value"
|
|
@@ -6230,7 +6078,7 @@ var configGetCommand = defineCommand46({
|
|
|
6230
6078
|
if (idp)
|
|
6231
6079
|
console.log(idp);
|
|
6232
6080
|
else
|
|
6233
|
-
|
|
6081
|
+
consola38.info("No IdP configured.");
|
|
6234
6082
|
break;
|
|
6235
6083
|
}
|
|
6236
6084
|
case "email": {
|
|
@@ -6238,7 +6086,7 @@ var configGetCommand = defineCommand46({
|
|
|
6238
6086
|
if (auth?.email)
|
|
6239
6087
|
console.log(auth.email);
|
|
6240
6088
|
else
|
|
6241
|
-
|
|
6089
|
+
consola38.info("Not logged in.");
|
|
6242
6090
|
break;
|
|
6243
6091
|
}
|
|
6244
6092
|
default: {
|
|
@@ -6251,7 +6099,7 @@ var configGetCommand = defineCommand46({
|
|
|
6251
6099
|
if (sectionObj && field in sectionObj) {
|
|
6252
6100
|
console.log(sectionObj[field]);
|
|
6253
6101
|
} else {
|
|
6254
|
-
|
|
6102
|
+
consola38.info(`Key "${key}" not set.`);
|
|
6255
6103
|
}
|
|
6256
6104
|
} else {
|
|
6257
6105
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -6262,9 +6110,9 @@ var configGetCommand = defineCommand46({
|
|
|
6262
6110
|
});
|
|
6263
6111
|
|
|
6264
6112
|
// src/commands/config/set.ts
|
|
6265
|
-
import { defineCommand as
|
|
6266
|
-
import
|
|
6267
|
-
var configSetCommand =
|
|
6113
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6114
|
+
import consola39 from "consola";
|
|
6115
|
+
var configSetCommand = defineCommand46({
|
|
6268
6116
|
meta: {
|
|
6269
6117
|
name: "set",
|
|
6270
6118
|
description: "Set a configuration value"
|
|
@@ -6300,12 +6148,12 @@ var configSetCommand = defineCommand47({
|
|
|
6300
6148
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
6301
6149
|
}
|
|
6302
6150
|
saveConfig(config);
|
|
6303
|
-
|
|
6151
|
+
consola39.success(`Set ${key} = ${value}`);
|
|
6304
6152
|
}
|
|
6305
6153
|
});
|
|
6306
6154
|
|
|
6307
6155
|
// src/commands/fetch/index.ts
|
|
6308
|
-
import { defineCommand as
|
|
6156
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
6309
6157
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
6310
6158
|
const token = getAuthToken();
|
|
6311
6159
|
if (!token) {
|
|
@@ -6341,13 +6189,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
6341
6189
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
6342
6190
|
}
|
|
6343
6191
|
}
|
|
6344
|
-
var fetchCommand =
|
|
6192
|
+
var fetchCommand = defineCommand47({
|
|
6345
6193
|
meta: {
|
|
6346
6194
|
name: "fetch",
|
|
6347
6195
|
description: "Make authenticated HTTP requests"
|
|
6348
6196
|
},
|
|
6349
6197
|
subCommands: {
|
|
6350
|
-
get:
|
|
6198
|
+
get: defineCommand47({
|
|
6351
6199
|
meta: {
|
|
6352
6200
|
name: "get",
|
|
6353
6201
|
description: "GET request with auth token"
|
|
@@ -6373,7 +6221,7 @@ var fetchCommand = defineCommand48({
|
|
|
6373
6221
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
6374
6222
|
}
|
|
6375
6223
|
}),
|
|
6376
|
-
post:
|
|
6224
|
+
post: defineCommand47({
|
|
6377
6225
|
meta: {
|
|
6378
6226
|
name: "post",
|
|
6379
6227
|
description: "POST request with auth token"
|
|
@@ -6412,8 +6260,8 @@ var fetchCommand = defineCommand48({
|
|
|
6412
6260
|
});
|
|
6413
6261
|
|
|
6414
6262
|
// src/commands/mcp/index.ts
|
|
6415
|
-
import { defineCommand as
|
|
6416
|
-
var mcpCommand =
|
|
6263
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
6264
|
+
var mcpCommand = defineCommand48({
|
|
6417
6265
|
meta: {
|
|
6418
6266
|
name: "mcp",
|
|
6419
6267
|
description: "Start MCP server for AI agents"
|
|
@@ -6436,25 +6284,25 @@ var mcpCommand = defineCommand49({
|
|
|
6436
6284
|
if (transport !== "stdio" && transport !== "sse") {
|
|
6437
6285
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
6438
6286
|
}
|
|
6439
|
-
const { startMcpServer } = await import("./server-
|
|
6287
|
+
const { startMcpServer } = await import("./server-HYDAVEQI.js");
|
|
6440
6288
|
await startMcpServer(transport, port);
|
|
6441
6289
|
}
|
|
6442
6290
|
});
|
|
6443
6291
|
|
|
6444
6292
|
// src/commands/init/index.ts
|
|
6445
|
-
import { existsSync as
|
|
6293
|
+
import { existsSync as existsSync15, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
|
|
6446
6294
|
import { randomBytes } from "crypto";
|
|
6447
6295
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
6448
|
-
import { join as
|
|
6449
|
-
import { defineCommand as
|
|
6450
|
-
import
|
|
6296
|
+
import { join as join14 } from "path";
|
|
6297
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6298
|
+
import consola40 from "consola";
|
|
6451
6299
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
6452
6300
|
async function downloadTemplate(repo, targetDir) {
|
|
6453
6301
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
6454
6302
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
6455
6303
|
}
|
|
6456
6304
|
function installDeps(dir) {
|
|
6457
|
-
const hasLockFile = (name) =>
|
|
6305
|
+
const hasLockFile = (name) => existsSync15(join14(dir, name));
|
|
6458
6306
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
6459
6307
|
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
6460
6308
|
} else if (hasLockFile("bun.lockb")) {
|
|
@@ -6464,20 +6312,20 @@ function installDeps(dir) {
|
|
|
6464
6312
|
}
|
|
6465
6313
|
}
|
|
6466
6314
|
async function promptChoice(message, choices) {
|
|
6467
|
-
const result = await
|
|
6315
|
+
const result = await consola40.prompt(message, { type: "select", options: choices });
|
|
6468
6316
|
if (typeof result === "symbol") {
|
|
6469
6317
|
throw new CliExit(0);
|
|
6470
6318
|
}
|
|
6471
6319
|
return result;
|
|
6472
6320
|
}
|
|
6473
6321
|
async function promptText(message, defaultValue) {
|
|
6474
|
-
const result = await
|
|
6322
|
+
const result = await consola40.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
6475
6323
|
if (typeof result === "symbol") {
|
|
6476
6324
|
throw new CliExit(0);
|
|
6477
6325
|
}
|
|
6478
6326
|
return result || defaultValue || "";
|
|
6479
6327
|
}
|
|
6480
|
-
var initCommand =
|
|
6328
|
+
var initCommand = defineCommand49({
|
|
6481
6329
|
meta: {
|
|
6482
6330
|
name: "init",
|
|
6483
6331
|
description: "Scaffold a new OpenApe project"
|
|
@@ -6519,23 +6367,23 @@ var initCommand = defineCommand50({
|
|
|
6519
6367
|
});
|
|
6520
6368
|
async function initSP(targetDir) {
|
|
6521
6369
|
const dir = targetDir || "my-app";
|
|
6522
|
-
if (
|
|
6370
|
+
if (existsSync15(join14(dir, "package.json"))) {
|
|
6523
6371
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
6524
6372
|
}
|
|
6525
|
-
|
|
6373
|
+
consola40.start("Scaffolding SP starter...");
|
|
6526
6374
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
6527
|
-
|
|
6528
|
-
|
|
6375
|
+
consola40.success("Scaffolded from openape-sp-starter");
|
|
6376
|
+
consola40.start("Installing dependencies...");
|
|
6529
6377
|
installDeps(dir);
|
|
6530
|
-
|
|
6531
|
-
const envExample =
|
|
6532
|
-
const envFile =
|
|
6533
|
-
if (
|
|
6378
|
+
consola40.success("Dependencies installed");
|
|
6379
|
+
const envExample = join14(dir, ".env.example");
|
|
6380
|
+
const envFile = join14(dir, ".env");
|
|
6381
|
+
if (existsSync15(envExample) && !existsSync15(envFile)) {
|
|
6534
6382
|
copyFileSync(envExample, envFile);
|
|
6535
|
-
|
|
6383
|
+
consola40.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
6536
6384
|
}
|
|
6537
6385
|
console.log("");
|
|
6538
|
-
|
|
6386
|
+
consola40.box([
|
|
6539
6387
|
`cd ${dir}`,
|
|
6540
6388
|
"npm run dev",
|
|
6541
6389
|
"",
|
|
@@ -6544,7 +6392,7 @@ async function initSP(targetDir) {
|
|
|
6544
6392
|
}
|
|
6545
6393
|
async function initIdP(targetDir) {
|
|
6546
6394
|
const dir = targetDir || "my-idp";
|
|
6547
|
-
if (
|
|
6395
|
+
if (existsSync15(join14(dir, "package.json"))) {
|
|
6548
6396
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
6549
6397
|
}
|
|
6550
6398
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -6554,15 +6402,15 @@ async function initIdP(targetDir) {
|
|
|
6554
6402
|
"s3 (S3-compatible)"
|
|
6555
6403
|
]);
|
|
6556
6404
|
const adminEmail = await promptText("Admin email");
|
|
6557
|
-
|
|
6405
|
+
consola40.start("Scaffolding IdP starter...");
|
|
6558
6406
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
6559
|
-
|
|
6560
|
-
|
|
6407
|
+
consola40.success("Scaffolded from openape-idp-starter");
|
|
6408
|
+
consola40.start("Installing dependencies...");
|
|
6561
6409
|
installDeps(dir);
|
|
6562
|
-
|
|
6410
|
+
consola40.success("Dependencies installed");
|
|
6563
6411
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
6564
6412
|
const managementToken = randomBytes(32).toString("hex");
|
|
6565
|
-
|
|
6413
|
+
consola40.success("Secrets generated");
|
|
6566
6414
|
const isLocalhost = domain === "localhost";
|
|
6567
6415
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
6568
6416
|
const envContent = [
|
|
@@ -6576,11 +6424,11 @@ async function initIdP(targetDir) {
|
|
|
6576
6424
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
6577
6425
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
6578
6426
|
].join("\n");
|
|
6579
|
-
|
|
6427
|
+
writeFileSync9(join14(dir, ".env"), `${envContent}
|
|
6580
6428
|
`, { mode: 384 });
|
|
6581
|
-
|
|
6429
|
+
consola40.success(".env created");
|
|
6582
6430
|
console.log("");
|
|
6583
|
-
|
|
6431
|
+
consola40.box([
|
|
6584
6432
|
`cd ${dir}`,
|
|
6585
6433
|
"npm run dev",
|
|
6586
6434
|
"",
|
|
@@ -6597,11 +6445,11 @@ async function initIdP(targetDir) {
|
|
|
6597
6445
|
|
|
6598
6446
|
// src/commands/enroll.ts
|
|
6599
6447
|
import { Buffer as Buffer5 } from "buffer";
|
|
6600
|
-
import { existsSync as
|
|
6448
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
|
|
6601
6449
|
import { execFile as execFile2 } from "child_process";
|
|
6602
6450
|
import { sign as sign2 } from "crypto";
|
|
6603
|
-
import { defineCommand as
|
|
6604
|
-
import
|
|
6451
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6452
|
+
import consola41 from "consola";
|
|
6605
6453
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
6606
6454
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
6607
6455
|
var POLL_INTERVAL = 3e3;
|
|
@@ -6613,7 +6461,7 @@ function openBrowser2(url) {
|
|
|
6613
6461
|
}
|
|
6614
6462
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
6615
6463
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6616
|
-
const keyContent =
|
|
6464
|
+
const keyContent = readFileSync12(resolvedKey, "utf-8");
|
|
6617
6465
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
6618
6466
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
6619
6467
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -6644,7 +6492,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
6644
6492
|
}
|
|
6645
6493
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
6646
6494
|
}
|
|
6647
|
-
var enrollCommand =
|
|
6495
|
+
var enrollCommand = defineCommand50({
|
|
6648
6496
|
meta: {
|
|
6649
6497
|
name: "enroll",
|
|
6650
6498
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -6664,38 +6512,38 @@ var enrollCommand = defineCommand51({
|
|
|
6664
6512
|
}
|
|
6665
6513
|
},
|
|
6666
6514
|
async run({ args }) {
|
|
6667
|
-
const idp = args.idp || await
|
|
6515
|
+
const idp = args.idp || await consola41.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r3) => {
|
|
6668
6516
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
6669
6517
|
return r3;
|
|
6670
6518
|
}) || DEFAULT_IDP_URL2;
|
|
6671
|
-
const agentName = args.name || await
|
|
6519
|
+
const agentName = args.name || await consola41.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r3) => {
|
|
6672
6520
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
6673
6521
|
return r3;
|
|
6674
6522
|
});
|
|
6675
6523
|
if (!agentName) {
|
|
6676
6524
|
throw new CliError("Agent name is required.");
|
|
6677
6525
|
}
|
|
6678
|
-
const keyPath = args.key || await
|
|
6526
|
+
const keyPath = args.key || await consola41.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r3) => {
|
|
6679
6527
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
6680
6528
|
return r3;
|
|
6681
6529
|
}) || DEFAULT_KEY_PATH;
|
|
6682
6530
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6683
6531
|
let publicKey;
|
|
6684
|
-
if (
|
|
6532
|
+
if (existsSync16(resolvedKey)) {
|
|
6685
6533
|
publicKey = readPublicKey(resolvedKey);
|
|
6686
|
-
|
|
6534
|
+
consola41.success(`Using existing key ${keyPath}`);
|
|
6687
6535
|
} else {
|
|
6688
|
-
|
|
6536
|
+
consola41.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
6689
6537
|
publicKey = generateAndSaveKey(keyPath);
|
|
6690
|
-
|
|
6538
|
+
consola41.success(`Key pair generated at ${keyPath}`);
|
|
6691
6539
|
}
|
|
6692
6540
|
const encodedKey = encodeURIComponent(publicKey);
|
|
6693
6541
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
6694
|
-
|
|
6695
|
-
|
|
6542
|
+
consola41.info("Opening browser for enrollment...");
|
|
6543
|
+
consola41.info(`\u2192 ${idp}/enroll`);
|
|
6696
6544
|
openBrowser2(enrollUrl);
|
|
6697
6545
|
console.log("");
|
|
6698
|
-
const agentEmail = await
|
|
6546
|
+
const agentEmail = await consola41.prompt(
|
|
6699
6547
|
"Agent email (shown in browser after enrollment)",
|
|
6700
6548
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
6701
6549
|
).then((r3) => {
|
|
@@ -6705,7 +6553,7 @@ var enrollCommand = defineCommand51({
|
|
|
6705
6553
|
if (!agentEmail) {
|
|
6706
6554
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
6707
6555
|
}
|
|
6708
|
-
|
|
6556
|
+
consola41.start("Verifying enrollment...");
|
|
6709
6557
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
6710
6558
|
saveAuth({
|
|
6711
6559
|
idp,
|
|
@@ -6717,18 +6565,18 @@ var enrollCommand = defineCommand51({
|
|
|
6717
6565
|
config.defaults = { ...config.defaults, idp };
|
|
6718
6566
|
config.agent = { key: keyPath, email: agentEmail };
|
|
6719
6567
|
saveConfig(config);
|
|
6720
|
-
|
|
6721
|
-
|
|
6568
|
+
consola41.success(`Agent enrolled as ${agentEmail}`);
|
|
6569
|
+
consola41.success("Config saved to ~/.config/apes/");
|
|
6722
6570
|
console.log("");
|
|
6723
|
-
|
|
6571
|
+
consola41.info("Verify with: apes whoami");
|
|
6724
6572
|
}
|
|
6725
6573
|
});
|
|
6726
6574
|
|
|
6727
6575
|
// src/commands/register-user.ts
|
|
6728
|
-
import { existsSync as
|
|
6729
|
-
import { defineCommand as
|
|
6730
|
-
import
|
|
6731
|
-
var registerUserCommand =
|
|
6576
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
6577
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
6578
|
+
import consola42 from "consola";
|
|
6579
|
+
var registerUserCommand = defineCommand51({
|
|
6732
6580
|
meta: {
|
|
6733
6581
|
name: "register-user",
|
|
6734
6582
|
description: "Register a sub-user with SSH key"
|
|
@@ -6764,8 +6612,8 @@ var registerUserCommand = defineCommand52({
|
|
|
6764
6612
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
6765
6613
|
}
|
|
6766
6614
|
let publicKey = args.key;
|
|
6767
|
-
if (
|
|
6768
|
-
publicKey =
|
|
6615
|
+
if (existsSync17(args.key)) {
|
|
6616
|
+
publicKey = readFileSync13(args.key, "utf-8").trim();
|
|
6769
6617
|
}
|
|
6770
6618
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
6771
6619
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -6783,18 +6631,18 @@ var registerUserCommand = defineCommand52({
|
|
|
6783
6631
|
...userType ? { type: userType } : {}
|
|
6784
6632
|
}
|
|
6785
6633
|
});
|
|
6786
|
-
|
|
6634
|
+
consola42.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
6787
6635
|
}
|
|
6788
6636
|
});
|
|
6789
6637
|
|
|
6790
6638
|
// src/commands/utils/index.ts
|
|
6791
|
-
import { defineCommand as
|
|
6639
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
6792
6640
|
|
|
6793
6641
|
// src/commands/utils/dig.ts
|
|
6794
|
-
import { defineCommand as
|
|
6795
|
-
import
|
|
6642
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
6643
|
+
import consola43 from "consola";
|
|
6796
6644
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
6797
|
-
var digCommand =
|
|
6645
|
+
var digCommand = defineCommand52({
|
|
6798
6646
|
meta: {
|
|
6799
6647
|
name: "dig",
|
|
6800
6648
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -6867,12 +6715,12 @@ var digCommand = defineCommand53({
|
|
|
6867
6715
|
console.log(` domain: ${domain}`);
|
|
6868
6716
|
console.log("");
|
|
6869
6717
|
if (!result.ddisa.found) {
|
|
6870
|
-
|
|
6718
|
+
consola43.warn(`No DDISA record at _ddisa.${domain}`);
|
|
6871
6719
|
if (result.hint) console.log(`
|
|
6872
6720
|
${result.hint}`);
|
|
6873
6721
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6874
6722
|
}
|
|
6875
|
-
|
|
6723
|
+
consola43.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
6876
6724
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
6877
6725
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
6878
6726
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -6882,13 +6730,13 @@ ${result.hint}`);
|
|
|
6882
6730
|
return;
|
|
6883
6731
|
}
|
|
6884
6732
|
if (result.idpDiscovery.ok) {
|
|
6885
|
-
|
|
6733
|
+
consola43.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
6886
6734
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
6887
6735
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
6888
6736
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
6889
6737
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
6890
6738
|
} else {
|
|
6891
|
-
|
|
6739
|
+
consola43.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
6892
6740
|
if (result.hint) console.log(`
|
|
6893
6741
|
${result.hint}`);
|
|
6894
6742
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -6897,7 +6745,7 @@ ${result.hint}`);
|
|
|
6897
6745
|
});
|
|
6898
6746
|
|
|
6899
6747
|
// src/commands/utils/index.ts
|
|
6900
|
-
var utilsCommand =
|
|
6748
|
+
var utilsCommand = defineCommand53({
|
|
6901
6749
|
meta: {
|
|
6902
6750
|
name: "utils",
|
|
6903
6751
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -6908,12 +6756,12 @@ var utilsCommand = defineCommand54({
|
|
|
6908
6756
|
});
|
|
6909
6757
|
|
|
6910
6758
|
// src/commands/sessions/index.ts
|
|
6911
|
-
import { defineCommand as
|
|
6759
|
+
import { defineCommand as defineCommand56 } from "citty";
|
|
6912
6760
|
|
|
6913
6761
|
// src/commands/sessions/list.ts
|
|
6914
|
-
import { defineCommand as
|
|
6915
|
-
import
|
|
6916
|
-
var sessionsListCommand =
|
|
6762
|
+
import { defineCommand as defineCommand54 } from "citty";
|
|
6763
|
+
import consola44 from "consola";
|
|
6764
|
+
var sessionsListCommand = defineCommand54({
|
|
6917
6765
|
meta: {
|
|
6918
6766
|
name: "list",
|
|
6919
6767
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6931,7 +6779,7 @@ var sessionsListCommand = defineCommand55({
|
|
|
6931
6779
|
return;
|
|
6932
6780
|
}
|
|
6933
6781
|
if (result.data.length === 0) {
|
|
6934
|
-
|
|
6782
|
+
consola44.info("No active sessions.");
|
|
6935
6783
|
return;
|
|
6936
6784
|
}
|
|
6937
6785
|
for (const f of result.data) {
|
|
@@ -6943,9 +6791,9 @@ var sessionsListCommand = defineCommand55({
|
|
|
6943
6791
|
});
|
|
6944
6792
|
|
|
6945
6793
|
// src/commands/sessions/remove.ts
|
|
6946
|
-
import { defineCommand as
|
|
6947
|
-
import
|
|
6948
|
-
var sessionsRemoveCommand =
|
|
6794
|
+
import { defineCommand as defineCommand55 } from "citty";
|
|
6795
|
+
import consola45 from "consola";
|
|
6796
|
+
var sessionsRemoveCommand = defineCommand55({
|
|
6949
6797
|
meta: {
|
|
6950
6798
|
name: "remove",
|
|
6951
6799
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6961,12 +6809,12 @@ var sessionsRemoveCommand = defineCommand56({
|
|
|
6961
6809
|
const id = String(args.familyId).trim();
|
|
6962
6810
|
if (!id) throw new CliError("familyId required");
|
|
6963
6811
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6964
|
-
|
|
6812
|
+
consola45.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6965
6813
|
}
|
|
6966
6814
|
});
|
|
6967
6815
|
|
|
6968
6816
|
// src/commands/sessions/index.ts
|
|
6969
|
-
var sessionsCommand =
|
|
6817
|
+
var sessionsCommand = defineCommand56({
|
|
6970
6818
|
meta: {
|
|
6971
6819
|
name: "sessions",
|
|
6972
6820
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6978,10 +6826,10 @@ var sessionsCommand = defineCommand57({
|
|
|
6978
6826
|
});
|
|
6979
6827
|
|
|
6980
6828
|
// src/commands/dns-check.ts
|
|
6981
|
-
import { defineCommand as
|
|
6982
|
-
import
|
|
6829
|
+
import { defineCommand as defineCommand57 } from "citty";
|
|
6830
|
+
import consola46 from "consola";
|
|
6983
6831
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6984
|
-
var dnsCheckCommand =
|
|
6832
|
+
var dnsCheckCommand = defineCommand57({
|
|
6985
6833
|
meta: {
|
|
6986
6834
|
name: "dns-check",
|
|
6987
6835
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6995,7 +6843,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
6995
6843
|
},
|
|
6996
6844
|
async run({ args }) {
|
|
6997
6845
|
const domain = args.domain;
|
|
6998
|
-
|
|
6846
|
+
consola46.start(`Checking _ddisa.${domain}...`);
|
|
6999
6847
|
try {
|
|
7000
6848
|
const result = await resolveDDISA3(domain);
|
|
7001
6849
|
if (!result) {
|
|
@@ -7004,7 +6852,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7004
6852
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
7005
6853
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
7006
6854
|
}
|
|
7007
|
-
|
|
6855
|
+
consola46.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
7008
6856
|
console.log("");
|
|
7009
6857
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
7010
6858
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -7013,14 +6861,14 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7013
6861
|
if (result.priority !== void 0)
|
|
7014
6862
|
console.log(` Priority: ${result.priority}`);
|
|
7015
6863
|
console.log("");
|
|
7016
|
-
|
|
6864
|
+
consola46.start(`Verifying IdP at ${result.idp}...`);
|
|
7017
6865
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
7018
6866
|
if (!discoResp.ok) {
|
|
7019
|
-
|
|
6867
|
+
consola46.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
7020
6868
|
return;
|
|
7021
6869
|
}
|
|
7022
6870
|
const disco = await discoResp.json();
|
|
7023
|
-
|
|
6871
|
+
consola46.success(`IdP is reachable`);
|
|
7024
6872
|
console.log(` Issuer: ${disco.issuer}`);
|
|
7025
6873
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
7026
6874
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -7038,7 +6886,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7038
6886
|
// src/commands/health.ts
|
|
7039
6887
|
import { exec } from "child_process";
|
|
7040
6888
|
import { promisify } from "util";
|
|
7041
|
-
import { defineCommand as
|
|
6889
|
+
import { defineCommand as defineCommand58 } from "citty";
|
|
7042
6890
|
var execAsync = promisify(exec);
|
|
7043
6891
|
async function resolveApeShellPath() {
|
|
7044
6892
|
try {
|
|
@@ -7074,7 +6922,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
7074
6922
|
}
|
|
7075
6923
|
}
|
|
7076
6924
|
async function runHealth(args) {
|
|
7077
|
-
const version = true ? "1.
|
|
6925
|
+
const version = true ? "1.13.0" : "0.0.0";
|
|
7078
6926
|
const auth = loadAuth();
|
|
7079
6927
|
if (!auth) {
|
|
7080
6928
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -7137,7 +6985,7 @@ async function runHealth(args) {
|
|
|
7137
6985
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
7138
6986
|
}
|
|
7139
6987
|
}
|
|
7140
|
-
var healthCommand =
|
|
6988
|
+
var healthCommand = defineCommand58({
|
|
7141
6989
|
meta: {
|
|
7142
6990
|
name: "health",
|
|
7143
6991
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -7155,8 +7003,8 @@ var healthCommand = defineCommand59({
|
|
|
7155
7003
|
});
|
|
7156
7004
|
|
|
7157
7005
|
// src/commands/workflows.ts
|
|
7158
|
-
import { defineCommand as
|
|
7159
|
-
import
|
|
7006
|
+
import { defineCommand as defineCommand59 } from "citty";
|
|
7007
|
+
import consola47 from "consola";
|
|
7160
7008
|
|
|
7161
7009
|
// src/guides/index.ts
|
|
7162
7010
|
var guides = [
|
|
@@ -7206,7 +7054,7 @@ var guides = [
|
|
|
7206
7054
|
];
|
|
7207
7055
|
|
|
7208
7056
|
// src/commands/workflows.ts
|
|
7209
|
-
var workflowsCommand =
|
|
7057
|
+
var workflowsCommand = defineCommand59({
|
|
7210
7058
|
meta: {
|
|
7211
7059
|
name: "workflows",
|
|
7212
7060
|
description: "Discover workflow guides"
|
|
@@ -7227,7 +7075,7 @@ var workflowsCommand = defineCommand60({
|
|
|
7227
7075
|
if (args.id) {
|
|
7228
7076
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
7229
7077
|
if (!guide) {
|
|
7230
|
-
|
|
7078
|
+
consola47.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
7231
7079
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
7232
7080
|
}
|
|
7233
7081
|
if (args.json) {
|
|
@@ -7267,26 +7115,26 @@ var workflowsCommand = defineCommand60({
|
|
|
7267
7115
|
});
|
|
7268
7116
|
|
|
7269
7117
|
// src/version-check.ts
|
|
7270
|
-
import { existsSync as
|
|
7271
|
-
import { homedir as
|
|
7272
|
-
import { join as
|
|
7273
|
-
import
|
|
7118
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
7119
|
+
import { homedir as homedir13 } from "os";
|
|
7120
|
+
import { join as join15 } from "path";
|
|
7121
|
+
import consola48 from "consola";
|
|
7274
7122
|
var PACKAGE_NAME = "@openape/apes";
|
|
7275
7123
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
7276
|
-
var CACHE_FILE =
|
|
7124
|
+
var CACHE_FILE = join15(homedir13(), ".config", "apes", ".version-check.json");
|
|
7277
7125
|
function readCache() {
|
|
7278
|
-
if (!
|
|
7126
|
+
if (!existsSync18(CACHE_FILE)) return null;
|
|
7279
7127
|
try {
|
|
7280
|
-
return JSON.parse(
|
|
7128
|
+
return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
|
|
7281
7129
|
} catch {
|
|
7282
7130
|
return null;
|
|
7283
7131
|
}
|
|
7284
7132
|
}
|
|
7285
7133
|
function writeCache(entry) {
|
|
7286
7134
|
try {
|
|
7287
|
-
const dir =
|
|
7288
|
-
if (!
|
|
7289
|
-
|
|
7135
|
+
const dir = join15(homedir13(), ".config", "apes");
|
|
7136
|
+
if (!existsSync18(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
7137
|
+
writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
7290
7138
|
} catch {
|
|
7291
7139
|
}
|
|
7292
7140
|
}
|
|
@@ -7315,7 +7163,7 @@ async function fetchLatestVersion() {
|
|
|
7315
7163
|
}
|
|
7316
7164
|
function warnIfBehind(currentVersion, latest) {
|
|
7317
7165
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
7318
|
-
|
|
7166
|
+
consola48.warn(
|
|
7319
7167
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
7320
7168
|
);
|
|
7321
7169
|
}
|
|
@@ -7347,10 +7195,10 @@ if (shellRewrite) {
|
|
|
7347
7195
|
if (shellRewrite.action === "rewrite") {
|
|
7348
7196
|
process.argv = shellRewrite.argv;
|
|
7349
7197
|
} else if (shellRewrite.action === "version") {
|
|
7350
|
-
console.log(`ape-shell ${"1.
|
|
7198
|
+
console.log(`ape-shell ${"1.13.0"} (OpenApe DDISA shell wrapper)`);
|
|
7351
7199
|
process.exit(0);
|
|
7352
7200
|
} else if (shellRewrite.action === "help") {
|
|
7353
|
-
console.log(`ape-shell ${"1.
|
|
7201
|
+
console.log(`ape-shell ${"1.13.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
7354
7202
|
console.log("");
|
|
7355
7203
|
console.log("Usage:");
|
|
7356
7204
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -7374,7 +7222,7 @@ if (shellRewrite) {
|
|
|
7374
7222
|
}
|
|
7375
7223
|
}
|
|
7376
7224
|
var debug = process.argv.includes("--debug");
|
|
7377
|
-
var grantsCommand =
|
|
7225
|
+
var grantsCommand = defineCommand60({
|
|
7378
7226
|
meta: {
|
|
7379
7227
|
name: "grants",
|
|
7380
7228
|
description: "Grant management"
|
|
@@ -7395,7 +7243,7 @@ var grantsCommand = defineCommand61({
|
|
|
7395
7243
|
"delegation-revoke": delegationRevokeCommand
|
|
7396
7244
|
}
|
|
7397
7245
|
});
|
|
7398
|
-
var configCommand =
|
|
7246
|
+
var configCommand = defineCommand60({
|
|
7399
7247
|
meta: {
|
|
7400
7248
|
name: "config",
|
|
7401
7249
|
description: "Configuration management"
|
|
@@ -7405,10 +7253,10 @@ var configCommand = defineCommand61({
|
|
|
7405
7253
|
set: configSetCommand
|
|
7406
7254
|
}
|
|
7407
7255
|
});
|
|
7408
|
-
var main =
|
|
7256
|
+
var main = defineCommand60({
|
|
7409
7257
|
meta: {
|
|
7410
7258
|
name: "apes",
|
|
7411
|
-
version: "1.
|
|
7259
|
+
version: "1.13.0",
|
|
7412
7260
|
description: "Unified CLI for OpenApe"
|
|
7413
7261
|
},
|
|
7414
7262
|
subCommands: {
|
|
@@ -7465,20 +7313,20 @@ async function maybeRefreshAuth() {
|
|
|
7465
7313
|
}
|
|
7466
7314
|
}
|
|
7467
7315
|
await maybeRefreshAuth();
|
|
7468
|
-
await maybeWarnStaleVersion("1.
|
|
7316
|
+
await maybeWarnStaleVersion("1.13.0").catch(() => {
|
|
7469
7317
|
});
|
|
7470
7318
|
runMain(main).catch((err) => {
|
|
7471
7319
|
if (err instanceof CliExit) {
|
|
7472
7320
|
process.exit(err.exitCode);
|
|
7473
7321
|
}
|
|
7474
7322
|
if (err instanceof CliError) {
|
|
7475
|
-
|
|
7323
|
+
consola49.error(err.message);
|
|
7476
7324
|
process.exit(err.exitCode);
|
|
7477
7325
|
}
|
|
7478
7326
|
if (debug) {
|
|
7479
|
-
|
|
7327
|
+
consola49.error(err);
|
|
7480
7328
|
} else {
|
|
7481
|
-
|
|
7329
|
+
consola49.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
7482
7330
|
}
|
|
7483
7331
|
process.exit(1);
|
|
7484
7332
|
});
|