@openape/apes 1.11.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/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";
|
|
@@ -4434,147 +4407,107 @@ var authorizeNestCommand = defineCommand30({
|
|
|
4434
4407
|
});
|
|
4435
4408
|
|
|
4436
4409
|
// src/commands/nest/destroy.ts
|
|
4437
|
-
import process2 from "process";
|
|
4438
4410
|
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
4411
|
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));
|
|
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 {
|
|
4497
4444
|
}
|
|
4445
|
+
throw err;
|
|
4498
4446
|
}
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
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);
|
|
4503
4477
|
}
|
|
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
4478
|
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;
|
|
4479
|
+
unlinkSync(intentPath);
|
|
4530
4480
|
} catch {
|
|
4531
|
-
return null;
|
|
4532
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));
|
|
4533
4486
|
}
|
|
4534
4487
|
|
|
4535
4488
|
// src/commands/nest/destroy.ts
|
|
4536
4489
|
var destroyNestCommand = defineCommand31({
|
|
4537
4490
|
meta: {
|
|
4538
4491
|
name: "destroy",
|
|
4539
|
-
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."
|
|
4540
4493
|
},
|
|
4541
4494
|
args: {
|
|
4542
|
-
name: { type: "positional", required: true, description: "Agent name to destroy" }
|
|
4543
|
-
port: { type: "string", description: "Override nest port (default: 9091)" }
|
|
4495
|
+
name: { type: "positional", required: true, description: "Agent name to destroy" }
|
|
4544
4496
|
},
|
|
4545
4497
|
async run({ args }) {
|
|
4546
4498
|
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}`);
|
|
4499
|
+
await dispatchIntent({ action: "destroy", name });
|
|
4500
|
+
consola27.success(`Destroyed ${name}`);
|
|
4568
4501
|
}
|
|
4569
4502
|
});
|
|
4570
4503
|
|
|
4571
4504
|
// src/commands/nest/install.ts
|
|
4572
4505
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
4573
|
-
import { existsSync as
|
|
4574
|
-
import { homedir as
|
|
4575
|
-
import { dirname as dirname3, join as
|
|
4506
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
4507
|
+
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
4508
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
4576
4509
|
import { defineCommand as defineCommand32 } from "citty";
|
|
4577
|
-
import
|
|
4510
|
+
import consola28 from "consola";
|
|
4578
4511
|
|
|
4579
4512
|
// src/commands/nest/apes-agents-adapter.ts
|
|
4580
4513
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4641,13 +4574,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
4641
4574
|
// src/commands/nest/install.ts
|
|
4642
4575
|
var PLIST_LABEL = "ai.openape.nest";
|
|
4643
4576
|
function plistPath() {
|
|
4644
|
-
return
|
|
4577
|
+
return join11(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
4645
4578
|
}
|
|
4646
4579
|
function escape2(s) {
|
|
4647
4580
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4648
4581
|
}
|
|
4649
4582
|
function buildPlist(args) {
|
|
4650
|
-
const logsDir =
|
|
4583
|
+
const logsDir = join11(args.userHome, "Library", "Logs");
|
|
4651
4584
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4652
4585
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4653
4586
|
<plist version="1.0">
|
|
@@ -4682,40 +4615,40 @@ function buildPlist(args) {
|
|
|
4682
4615
|
`;
|
|
4683
4616
|
}
|
|
4684
4617
|
function installAdapter2() {
|
|
4685
|
-
const target =
|
|
4686
|
-
|
|
4618
|
+
const target = join11(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4619
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
4687
4620
|
let existing = "";
|
|
4688
4621
|
try {
|
|
4689
|
-
existing =
|
|
4622
|
+
existing = readFileSync11(target, "utf8");
|
|
4690
4623
|
} catch {
|
|
4691
4624
|
}
|
|
4692
4625
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4693
|
-
|
|
4694
|
-
|
|
4626
|
+
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4627
|
+
consola28.success(`Wrote shapes adapter ${target}`);
|
|
4695
4628
|
return true;
|
|
4696
4629
|
}
|
|
4697
4630
|
function writeBridgeModelDefault(model) {
|
|
4698
|
-
const envDir =
|
|
4699
|
-
const envFile =
|
|
4700
|
-
|
|
4631
|
+
const envDir = join11(homedir11(), "litellm");
|
|
4632
|
+
const envFile = join11(envDir, ".env");
|
|
4633
|
+
mkdirSync5(envDir, { recursive: true });
|
|
4701
4634
|
let lines = [];
|
|
4702
|
-
if (
|
|
4703
|
-
lines =
|
|
4635
|
+
if (existsSync13(envFile)) {
|
|
4636
|
+
lines = readFileSync11(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
|
|
4704
4637
|
}
|
|
4705
4638
|
lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
|
|
4706
4639
|
while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
|
|
4707
|
-
|
|
4640
|
+
writeFileSync7(envFile, `${lines.join("\n")}
|
|
4708
4641
|
`, { mode: 384 });
|
|
4709
4642
|
}
|
|
4710
4643
|
function findBinary(name) {
|
|
4711
4644
|
for (const dir of [
|
|
4712
|
-
|
|
4645
|
+
join11(homedir11(), ".bun", "bin"),
|
|
4713
4646
|
"/opt/homebrew/bin",
|
|
4714
4647
|
"/usr/local/bin",
|
|
4715
4648
|
"/usr/bin"
|
|
4716
4649
|
]) {
|
|
4717
|
-
const p2 =
|
|
4718
|
-
if (
|
|
4650
|
+
const p2 = join11(dir, name);
|
|
4651
|
+
if (existsSync13(p2)) return p2;
|
|
4719
4652
|
}
|
|
4720
4653
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4721
4654
|
}
|
|
@@ -4735,35 +4668,35 @@ var installNestCommand = defineCommand32({
|
|
|
4735
4668
|
}
|
|
4736
4669
|
},
|
|
4737
4670
|
async run({ args }) {
|
|
4738
|
-
const homeDir =
|
|
4671
|
+
const homeDir = homedir11();
|
|
4739
4672
|
const port = Number(args.port ?? 9091);
|
|
4740
4673
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
4741
4674
|
throw new Error(`invalid port ${port}`);
|
|
4742
4675
|
}
|
|
4743
4676
|
const nestBin = findBinary("openape-nest");
|
|
4744
4677
|
const apesBin = findBinary("apes");
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
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}`);
|
|
4749
4682
|
if (typeof args["bridge-model"] === "string" && args["bridge-model"]) {
|
|
4750
4683
|
writeBridgeModelDefault(args["bridge-model"]);
|
|
4751
|
-
|
|
4684
|
+
consola28.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
|
|
4752
4685
|
}
|
|
4753
4686
|
installAdapter2();
|
|
4754
|
-
|
|
4755
|
-
|
|
4687
|
+
mkdirSync5(join11(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4688
|
+
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
4756
4689
|
const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
|
|
4757
4690
|
let existing = "";
|
|
4758
4691
|
try {
|
|
4759
|
-
existing =
|
|
4692
|
+
existing = readFileSync11(plistPath(), "utf8");
|
|
4760
4693
|
} catch {
|
|
4761
4694
|
}
|
|
4762
4695
|
if (existing !== desired) {
|
|
4763
|
-
|
|
4764
|
-
|
|
4696
|
+
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
4697
|
+
consola28.success("Wrote launchd plist");
|
|
4765
4698
|
} else {
|
|
4766
|
-
|
|
4699
|
+
consola28.info("plist already up to date");
|
|
4767
4700
|
}
|
|
4768
4701
|
const uid = userInfo2().uid;
|
|
4769
4702
|
try {
|
|
@@ -4771,208 +4704,116 @@ var installNestCommand = defineCommand32({
|
|
|
4771
4704
|
} catch {
|
|
4772
4705
|
}
|
|
4773
4706
|
execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
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.");
|
|
4782
4715
|
}
|
|
4783
4716
|
});
|
|
4784
4717
|
|
|
4785
4718
|
// src/commands/nest/list.ts
|
|
4786
|
-
import process3 from "process";
|
|
4787
4719
|
import { defineCommand as defineCommand33 } from "citty";
|
|
4788
|
-
import
|
|
4720
|
+
import consola29 from "consola";
|
|
4789
4721
|
var listNestCommand = defineCommand33({
|
|
4790
4722
|
meta: {
|
|
4791
4723
|
name: "list",
|
|
4792
|
-
description: "List agents registered with the local nest.
|
|
4724
|
+
description: "List agents registered with the local nest. File-based intent."
|
|
4793
4725
|
},
|
|
4794
4726
|
args: {
|
|
4795
|
-
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
4796
4727
|
json: { type: "boolean", description: "JSON output for scripts" }
|
|
4797
4728
|
},
|
|
4798
4729
|
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
|
-
}
|
|
4730
|
+
const result = await dispatchIntent({ action: "list" });
|
|
4820
4731
|
if (args.json) {
|
|
4821
|
-
console.log(JSON.stringify(
|
|
4732
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4822
4733
|
return;
|
|
4823
4734
|
}
|
|
4824
|
-
if (
|
|
4825
|
-
|
|
4735
|
+
if (result.agents.length === 0) {
|
|
4736
|
+
consola29.info("(no agents registered with this nest)");
|
|
4826
4737
|
return;
|
|
4827
4738
|
}
|
|
4828
|
-
|
|
4829
|
-
for (const a of
|
|
4739
|
+
consola29.info(`${result.agents.length} agent(s) registered with this nest:`);
|
|
4740
|
+
for (const a of result.agents) {
|
|
4830
4741
|
const bridge = a.bridge ? " bridge=on" : "";
|
|
4831
|
-
|
|
4742
|
+
consola29.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
4832
4743
|
}
|
|
4833
4744
|
}
|
|
4834
4745
|
});
|
|
4835
4746
|
|
|
4836
4747
|
// src/commands/nest/spawn.ts
|
|
4837
|
-
import process4 from "process";
|
|
4838
4748
|
import { defineCommand as defineCommand34 } from "citty";
|
|
4839
|
-
import
|
|
4749
|
+
import consola30 from "consola";
|
|
4840
4750
|
var spawnNestCommand = defineCommand34({
|
|
4841
4751
|
meta: {
|
|
4842
4752
|
name: "spawn",
|
|
4843
|
-
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."
|
|
4844
4754
|
},
|
|
4845
4755
|
args: {
|
|
4846
4756
|
name: { type: "positional", required: true, description: "Agent name (lowercase, [a-z0-9-], max 24 chars)" },
|
|
4847
4757
|
"no-bridge": { type: "boolean", description: "Skip installing the chat-bridge daemon (default: install it)" },
|
|
4848
4758
|
"bridge-key": { type: "string", description: "Override LITELLM_API_KEY (default: read from ~/litellm/.env)" },
|
|
4849
4759
|
"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)" }
|
|
4760
|
+
"bridge-model": { type: "string", description: "Override APE_CHAT_BRIDGE_MODEL" }
|
|
4852
4761
|
},
|
|
4853
4762
|
async run({ args }) {
|
|
4854
4763
|
const name = String(args.name);
|
|
4855
|
-
const
|
|
4856
|
-
|
|
4857
|
-
const reqBody = {
|
|
4764
|
+
const intent = {
|
|
4765
|
+
action: "spawn",
|
|
4858
4766
|
name,
|
|
4859
4767
|
bridge: !args["no-bridge"]
|
|
4860
4768
|
};
|
|
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`);
|
|
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})`);
|
|
4932
4774
|
}
|
|
4933
4775
|
});
|
|
4934
4776
|
|
|
4935
4777
|
// src/commands/nest/uninstall.ts
|
|
4936
4778
|
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
|
|
4779
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync2 } from "fs";
|
|
4780
|
+
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
4781
|
+
import { join as join12 } from "path";
|
|
4782
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
4783
|
+
import consola31 from "consola";
|
|
4942
4784
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4943
|
-
var uninstallNestCommand =
|
|
4785
|
+
var uninstallNestCommand = defineCommand35({
|
|
4944
4786
|
meta: {
|
|
4945
4787
|
name: "uninstall",
|
|
4946
4788
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4947
4789
|
},
|
|
4948
4790
|
async run() {
|
|
4949
4791
|
const uid = userInfo3().uid;
|
|
4950
|
-
const path2 =
|
|
4792
|
+
const path2 = join12(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4951
4793
|
try {
|
|
4952
4794
|
execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4953
|
-
|
|
4795
|
+
consola31.success("Nest daemon stopped");
|
|
4954
4796
|
} catch {
|
|
4955
|
-
|
|
4797
|
+
consola31.info("Nest daemon was not loaded");
|
|
4956
4798
|
}
|
|
4957
|
-
if (
|
|
4958
|
-
|
|
4959
|
-
|
|
4799
|
+
if (existsSync14(path2)) {
|
|
4800
|
+
unlinkSync2(path2);
|
|
4801
|
+
consola31.success(`Removed ${path2}`);
|
|
4960
4802
|
}
|
|
4961
|
-
|
|
4803
|
+
consola31.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4962
4804
|
}
|
|
4963
4805
|
});
|
|
4964
4806
|
|
|
4965
4807
|
// src/commands/nest/index.ts
|
|
4966
|
-
var nestCommand =
|
|
4808
|
+
var nestCommand = defineCommand36({
|
|
4967
4809
|
meta: {
|
|
4968
4810
|
name: "nest",
|
|
4969
|
-
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."
|
|
4970
4812
|
},
|
|
4971
4813
|
subCommands: {
|
|
4972
4814
|
install: installNestCommand,
|
|
4973
4815
|
enroll: enrollNestCommand,
|
|
4974
4816
|
authorize: authorizeNestCommand,
|
|
4975
|
-
status: statusNestCommand,
|
|
4976
4817
|
list: listNestCommand,
|
|
4977
4818
|
spawn: spawnNestCommand,
|
|
4978
4819
|
destroy: destroyNestCommand,
|
|
@@ -4981,12 +4822,12 @@ var nestCommand = defineCommand37({
|
|
|
4981
4822
|
});
|
|
4982
4823
|
|
|
4983
4824
|
// src/commands/yolo/index.ts
|
|
4984
|
-
import { defineCommand as
|
|
4825
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
4985
4826
|
|
|
4986
4827
|
// src/commands/yolo/clear.ts
|
|
4987
|
-
import { defineCommand as
|
|
4988
|
-
import
|
|
4989
|
-
var yoloClearCommand =
|
|
4828
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
4829
|
+
import consola32 from "consola";
|
|
4830
|
+
var yoloClearCommand = defineCommand37({
|
|
4990
4831
|
meta: {
|
|
4991
4832
|
name: "clear",
|
|
4992
4833
|
description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
|
|
@@ -5015,15 +4856,15 @@ var yoloClearCommand = defineCommand38({
|
|
|
5015
4856
|
const text = await res.text().catch(() => "");
|
|
5016
4857
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
5017
4858
|
}
|
|
5018
|
-
|
|
4859
|
+
consola32.success(`YOLO-policy cleared on ${email}`);
|
|
5019
4860
|
}
|
|
5020
4861
|
});
|
|
5021
4862
|
|
|
5022
4863
|
// src/commands/yolo/set.ts
|
|
5023
|
-
import { defineCommand as
|
|
5024
|
-
import
|
|
4864
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
4865
|
+
import consola33 from "consola";
|
|
5025
4866
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
5026
|
-
var yoloSetCommand =
|
|
4867
|
+
var yoloSetCommand = defineCommand38({
|
|
5027
4868
|
meta: {
|
|
5028
4869
|
name: "set",
|
|
5029
4870
|
description: "Write a YOLO-policy on a DDISA agent you own"
|
|
@@ -5071,12 +4912,12 @@ var yoloSetCommand = defineCommand39({
|
|
|
5071
4912
|
const denyPatterns = parseList(args.deny);
|
|
5072
4913
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
5073
4914
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
if (allowPatterns.length)
|
|
5077
|
-
if (denyPatterns.length)
|
|
5078
|
-
if (denyRiskThreshold)
|
|
5079
|
-
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()}`);
|
|
5080
4921
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
5081
4922
|
const res = await fetch(url, {
|
|
5082
4923
|
method: "PUT",
|
|
@@ -5096,7 +4937,7 @@ var yoloSetCommand = defineCommand39({
|
|
|
5096
4937
|
const text = await res.text().catch(() => "");
|
|
5097
4938
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
5098
4939
|
}
|
|
5099
|
-
|
|
4940
|
+
consola33.success(`YOLO-policy applied to ${email}`);
|
|
5100
4941
|
}
|
|
5101
4942
|
});
|
|
5102
4943
|
function parseList(s) {
|
|
@@ -5114,9 +4955,9 @@ function parseExpiresIn(s) {
|
|
|
5114
4955
|
}
|
|
5115
4956
|
|
|
5116
4957
|
// src/commands/yolo/show.ts
|
|
5117
|
-
import { defineCommand as
|
|
5118
|
-
import
|
|
5119
|
-
var yoloShowCommand =
|
|
4958
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
4959
|
+
import consola34 from "consola";
|
|
4960
|
+
var yoloShowCommand = defineCommand39({
|
|
5120
4961
|
meta: {
|
|
5121
4962
|
name: "show",
|
|
5122
4963
|
description: "Print the YOLO-policy currently set on a DDISA agent"
|
|
@@ -5153,17 +4994,17 @@ var yoloShowCommand = defineCommand40({
|
|
|
5153
4994
|
console.log(JSON.stringify(policy, null, 2));
|
|
5154
4995
|
return;
|
|
5155
4996
|
}
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
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)"}`);
|
|
5162
5003
|
}
|
|
5163
5004
|
});
|
|
5164
5005
|
|
|
5165
5006
|
// src/commands/yolo/index.ts
|
|
5166
|
-
var yoloCommand =
|
|
5007
|
+
var yoloCommand = defineCommand40({
|
|
5167
5008
|
meta: {
|
|
5168
5009
|
name: "yolo",
|
|
5169
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)."
|
|
@@ -5176,15 +5017,15 @@ var yoloCommand = defineCommand41({
|
|
|
5176
5017
|
});
|
|
5177
5018
|
|
|
5178
5019
|
// src/commands/adapter/index.ts
|
|
5179
|
-
import { defineCommand as
|
|
5180
|
-
import
|
|
5181
|
-
var adapterCommand =
|
|
5020
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
5021
|
+
import consola35 from "consola";
|
|
5022
|
+
var adapterCommand = defineCommand41({
|
|
5182
5023
|
meta: {
|
|
5183
5024
|
name: "adapter",
|
|
5184
5025
|
description: "Manage CLI adapters"
|
|
5185
5026
|
},
|
|
5186
5027
|
subCommands: {
|
|
5187
|
-
list:
|
|
5028
|
+
list: defineCommand41({
|
|
5188
5029
|
meta: {
|
|
5189
5030
|
name: "list",
|
|
5190
5031
|
description: "List available adapters"
|
|
@@ -5215,7 +5056,7 @@ var adapterCommand = defineCommand42({
|
|
|
5215
5056
|
`);
|
|
5216
5057
|
return;
|
|
5217
5058
|
}
|
|
5218
|
-
|
|
5059
|
+
consola35.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
5219
5060
|
for (const a of index2.adapters) {
|
|
5220
5061
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
5221
5062
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -5237,7 +5078,7 @@ var adapterCommand = defineCommand42({
|
|
|
5237
5078
|
return;
|
|
5238
5079
|
}
|
|
5239
5080
|
if (local.length === 0) {
|
|
5240
|
-
|
|
5081
|
+
consola35.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
5241
5082
|
return;
|
|
5242
5083
|
}
|
|
5243
5084
|
for (const a of local) {
|
|
@@ -5245,7 +5086,7 @@ var adapterCommand = defineCommand42({
|
|
|
5245
5086
|
}
|
|
5246
5087
|
}
|
|
5247
5088
|
}),
|
|
5248
|
-
install:
|
|
5089
|
+
install: defineCommand41({
|
|
5249
5090
|
meta: {
|
|
5250
5091
|
name: "install",
|
|
5251
5092
|
description: "Install an adapter from the registry"
|
|
@@ -5274,24 +5115,24 @@ var adapterCommand = defineCommand42({
|
|
|
5274
5115
|
for (const id of ids) {
|
|
5275
5116
|
const entry = findAdapter(index, id);
|
|
5276
5117
|
if (!entry) {
|
|
5277
|
-
|
|
5118
|
+
consola35.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
5278
5119
|
continue;
|
|
5279
5120
|
}
|
|
5280
5121
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
5281
5122
|
if (conflicts.length > 0) {
|
|
5282
5123
|
for (const c2 of conflicts) {
|
|
5283
|
-
|
|
5284
|
-
|
|
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}`);
|
|
5285
5126
|
}
|
|
5286
5127
|
}
|
|
5287
5128
|
const result = await installAdapter(entry, { local });
|
|
5288
5129
|
const verb = result.updated ? "Updated" : "Installed";
|
|
5289
|
-
|
|
5290
|
-
|
|
5130
|
+
consola35.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
5131
|
+
consola35.info(`Digest: ${result.digest}`);
|
|
5291
5132
|
}
|
|
5292
5133
|
}
|
|
5293
5134
|
}),
|
|
5294
|
-
remove:
|
|
5135
|
+
remove: defineCommand41({
|
|
5295
5136
|
meta: {
|
|
5296
5137
|
name: "remove",
|
|
5297
5138
|
description: "Remove an installed adapter"
|
|
@@ -5314,9 +5155,9 @@ var adapterCommand = defineCommand42({
|
|
|
5314
5155
|
let failed = false;
|
|
5315
5156
|
for (const id of ids) {
|
|
5316
5157
|
if (removeAdapter(id, local)) {
|
|
5317
|
-
|
|
5158
|
+
consola35.success(`Removed adapter: ${id}`);
|
|
5318
5159
|
} else {
|
|
5319
|
-
|
|
5160
|
+
consola35.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
5320
5161
|
failed = true;
|
|
5321
5162
|
}
|
|
5322
5163
|
}
|
|
@@ -5324,7 +5165,7 @@ var adapterCommand = defineCommand42({
|
|
|
5324
5165
|
throw new CliError("Some adapters could not be removed");
|
|
5325
5166
|
}
|
|
5326
5167
|
}),
|
|
5327
|
-
info:
|
|
5168
|
+
info: defineCommand41({
|
|
5328
5169
|
meta: {
|
|
5329
5170
|
name: "info",
|
|
5330
5171
|
description: "Show detailed adapter information"
|
|
@@ -5366,7 +5207,7 @@ var adapterCommand = defineCommand42({
|
|
|
5366
5207
|
}
|
|
5367
5208
|
}
|
|
5368
5209
|
}),
|
|
5369
|
-
search:
|
|
5210
|
+
search: defineCommand41({
|
|
5370
5211
|
meta: {
|
|
5371
5212
|
name: "search",
|
|
5372
5213
|
description: "Search adapters in the registry"
|
|
@@ -5398,7 +5239,7 @@ var adapterCommand = defineCommand42({
|
|
|
5398
5239
|
return;
|
|
5399
5240
|
}
|
|
5400
5241
|
if (results.length === 0) {
|
|
5401
|
-
|
|
5242
|
+
consola35.info(`No adapters matching "${query}"`);
|
|
5402
5243
|
return;
|
|
5403
5244
|
}
|
|
5404
5245
|
for (const a of results) {
|
|
@@ -5407,7 +5248,7 @@ var adapterCommand = defineCommand42({
|
|
|
5407
5248
|
}
|
|
5408
5249
|
}
|
|
5409
5250
|
}),
|
|
5410
|
-
update:
|
|
5251
|
+
update: defineCommand41({
|
|
5411
5252
|
meta: {
|
|
5412
5253
|
name: "update",
|
|
5413
5254
|
description: "Update installed adapters"
|
|
@@ -5433,33 +5274,33 @@ var adapterCommand = defineCommand42({
|
|
|
5433
5274
|
const targetId = args.id ? String(args.id) : void 0;
|
|
5434
5275
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
5435
5276
|
if (targets.length === 0) {
|
|
5436
|
-
|
|
5277
|
+
consola35.info("No adapters installed to update.");
|
|
5437
5278
|
return;
|
|
5438
5279
|
}
|
|
5439
5280
|
for (const id of targets) {
|
|
5440
5281
|
const entry = findAdapter(index, id);
|
|
5441
5282
|
if (!entry) {
|
|
5442
|
-
|
|
5283
|
+
consola35.warn(`${id}: not found in registry, skipping`);
|
|
5443
5284
|
continue;
|
|
5444
5285
|
}
|
|
5445
5286
|
const localDigest = getInstalledDigest(id, false);
|
|
5446
5287
|
if (localDigest === entry.digest) {
|
|
5447
|
-
|
|
5288
|
+
consola35.info(`${id}: already up to date`);
|
|
5448
5289
|
continue;
|
|
5449
5290
|
}
|
|
5450
5291
|
if (localDigest && !args.yes) {
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
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");
|
|
5455
5296
|
continue;
|
|
5456
5297
|
}
|
|
5457
5298
|
const result = await installAdapter(entry);
|
|
5458
|
-
|
|
5299
|
+
consola35.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
5459
5300
|
}
|
|
5460
5301
|
}
|
|
5461
5302
|
}),
|
|
5462
|
-
verify:
|
|
5303
|
+
verify: defineCommand41({
|
|
5463
5304
|
meta: {
|
|
5464
5305
|
name: "verify",
|
|
5465
5306
|
description: "Verify installed adapter against registry digest"
|
|
@@ -5492,7 +5333,7 @@ var adapterCommand = defineCommand42({
|
|
|
5492
5333
|
if (!localDigest)
|
|
5493
5334
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
5494
5335
|
if (localDigest === entry.digest) {
|
|
5495
|
-
|
|
5336
|
+
consola35.success(`${id}: digest matches registry`);
|
|
5496
5337
|
} else {
|
|
5497
5338
|
console.log(` Local: ${localDigest}`);
|
|
5498
5339
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -5505,10 +5346,10 @@ var adapterCommand = defineCommand42({
|
|
|
5505
5346
|
|
|
5506
5347
|
// src/commands/run.ts
|
|
5507
5348
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
5508
|
-
import { hostname as
|
|
5349
|
+
import { hostname as hostname5 } from "os";
|
|
5509
5350
|
import { basename } from "path";
|
|
5510
|
-
import { defineCommand as
|
|
5511
|
-
import
|
|
5351
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5352
|
+
import consola36 from "consola";
|
|
5512
5353
|
function shouldWaitForGrant(args) {
|
|
5513
5354
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
5514
5355
|
}
|
|
@@ -5545,7 +5386,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
5545
5386
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
5546
5387
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
5547
5388
|
if (mode === "human") {
|
|
5548
|
-
|
|
5389
|
+
consola36.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
5549
5390
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
5550
5391
|
console.log(` Check status: ${statusCmd}`);
|
|
5551
5392
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -5555,7 +5396,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
5555
5396
|
return;
|
|
5556
5397
|
}
|
|
5557
5398
|
const maxMin = getPollMaxMinutes();
|
|
5558
|
-
|
|
5399
|
+
consola36.success(`Grant ${grant.id} created (pending approval)`);
|
|
5559
5400
|
console.log(` Approve: ${approveUrl}`);
|
|
5560
5401
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
5561
5402
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -5577,7 +5418,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
5577
5418
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
5578
5419
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
5579
5420
|
}
|
|
5580
|
-
var runCommand =
|
|
5421
|
+
var runCommand = defineCommand42({
|
|
5581
5422
|
meta: {
|
|
5582
5423
|
name: "run",
|
|
5583
5424
|
description: "Execute a grant-secured command"
|
|
@@ -5666,7 +5507,7 @@ async function runShellMode(command, args) {
|
|
|
5666
5507
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
5667
5508
|
if (adapterHandled) return;
|
|
5668
5509
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5669
|
-
const targetHost = args.host ||
|
|
5510
|
+
const targetHost = args.host || hostname5();
|
|
5670
5511
|
try {
|
|
5671
5512
|
const grants = await apiFetch(
|
|
5672
5513
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -5680,7 +5521,7 @@ async function runShellMode(command, args) {
|
|
|
5680
5521
|
}
|
|
5681
5522
|
} catch {
|
|
5682
5523
|
}
|
|
5683
|
-
|
|
5524
|
+
consola36.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
5684
5525
|
const grant = await apiFetch(grantsUrl, {
|
|
5685
5526
|
method: "POST",
|
|
5686
5527
|
body: {
|
|
@@ -5700,8 +5541,8 @@ async function runShellMode(command, args) {
|
|
|
5700
5541
|
host: targetHost
|
|
5701
5542
|
});
|
|
5702
5543
|
if (shouldWaitForGrant(args)) {
|
|
5703
|
-
|
|
5704
|
-
|
|
5544
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5545
|
+
consola36.info("Waiting for approval...");
|
|
5705
5546
|
const maxWait = 3e5;
|
|
5706
5547
|
const interval = 3e3;
|
|
5707
5548
|
const start = Date.now();
|
|
@@ -5732,13 +5573,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5732
5573
|
try {
|
|
5733
5574
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
5734
5575
|
} catch (err) {
|
|
5735
|
-
|
|
5576
|
+
consola36.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
5736
5577
|
return false;
|
|
5737
5578
|
}
|
|
5738
5579
|
try {
|
|
5739
5580
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5740
5581
|
if (existingGrantId) {
|
|
5741
|
-
|
|
5582
|
+
consola36.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
5742
5583
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5743
5584
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5744
5585
|
return true;
|
|
@@ -5746,7 +5587,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5746
5587
|
} catch {
|
|
5747
5588
|
}
|
|
5748
5589
|
const approval = args.approval ?? "once";
|
|
5749
|
-
|
|
5590
|
+
consola36.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
5750
5591
|
const grant = await createShapesGrant(resolved, {
|
|
5751
5592
|
idp,
|
|
5752
5593
|
approval,
|
|
@@ -5754,19 +5595,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5754
5595
|
});
|
|
5755
5596
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5756
5597
|
const n2 = grant.similar_grants.similar_grants.length;
|
|
5757
|
-
|
|
5758
|
-
|
|
5598
|
+
consola36.info("");
|
|
5599
|
+
consola36.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5759
5600
|
}
|
|
5760
5601
|
notifyGrantPending({
|
|
5761
5602
|
grantId: grant.id,
|
|
5762
5603
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
5763
5604
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
5764
5605
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
5765
|
-
host: args.host ||
|
|
5606
|
+
host: args.host || hostname5()
|
|
5766
5607
|
});
|
|
5767
5608
|
if (shouldWaitForGrant(args)) {
|
|
5768
|
-
|
|
5769
|
-
|
|
5609
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5610
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5770
5611
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5771
5612
|
if (status !== "approved")
|
|
5772
5613
|
throw new CliError(`Grant ${status}`);
|
|
@@ -5840,7 +5681,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5840
5681
|
try {
|
|
5841
5682
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5842
5683
|
if (existingGrantId) {
|
|
5843
|
-
|
|
5684
|
+
consola36.info(`Reusing existing grant: ${existingGrantId}`);
|
|
5844
5685
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5845
5686
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5846
5687
|
return;
|
|
@@ -5854,17 +5695,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5854
5695
|
});
|
|
5855
5696
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5856
5697
|
const n2 = grant.similar_grants.similar_grants.length;
|
|
5857
|
-
|
|
5858
|
-
|
|
5698
|
+
consola36.info("");
|
|
5699
|
+
consola36.info(` Similar grant(s) found (${n2}). Your approver can extend an existing grant to cover this request.`);
|
|
5859
5700
|
if (grant.similar_grants.widened_details?.length) {
|
|
5860
5701
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
5861
|
-
|
|
5702
|
+
consola36.info(` Broader scope: ${wider}`);
|
|
5862
5703
|
}
|
|
5863
|
-
|
|
5704
|
+
consola36.info("");
|
|
5864
5705
|
}
|
|
5865
5706
|
if (shouldWaitForGrant(args)) {
|
|
5866
|
-
|
|
5867
|
-
|
|
5707
|
+
consola36.info(`Grant requested: ${grant.id}`);
|
|
5708
|
+
consola36.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5868
5709
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5869
5710
|
if (status !== "approved")
|
|
5870
5711
|
throw new Error(`Grant ${status}`);
|
|
@@ -5883,8 +5724,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5883
5724
|
const idp = getIdpUrl(args.idp);
|
|
5884
5725
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
5885
5726
|
const command = action.split(" ");
|
|
5886
|
-
const targetHost = args.host ||
|
|
5887
|
-
|
|
5727
|
+
const targetHost = args.host || hostname5();
|
|
5728
|
+
consola36.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5888
5729
|
const grant = await apiFetch(grantsUrl, {
|
|
5889
5730
|
method: "POST",
|
|
5890
5731
|
body: {
|
|
@@ -5901,9 +5742,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5901
5742
|
printPendingGrantInfo(grant, idp);
|
|
5902
5743
|
throw new CliExit(getAsyncExitCode());
|
|
5903
5744
|
}
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
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...");
|
|
5907
5748
|
const maxWait = 15 * 60 * 1e3;
|
|
5908
5749
|
const interval = 3e3;
|
|
5909
5750
|
const start = Date.now();
|
|
@@ -5911,7 +5752,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5911
5752
|
while (Date.now() - start < maxWait) {
|
|
5912
5753
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5913
5754
|
if (status.status === "approved") {
|
|
5914
|
-
|
|
5755
|
+
consola36.success("Grant approved!");
|
|
5915
5756
|
approved = true;
|
|
5916
5757
|
break;
|
|
5917
5758
|
}
|
|
@@ -5926,12 +5767,12 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5926
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.`
|
|
5927
5768
|
);
|
|
5928
5769
|
}
|
|
5929
|
-
|
|
5770
|
+
consola36.info("Fetching grant token...");
|
|
5930
5771
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5931
5772
|
method: "POST"
|
|
5932
5773
|
});
|
|
5933
5774
|
if (audience === "escapes") {
|
|
5934
|
-
|
|
5775
|
+
consola36.info(`Executing: ${command.join(" ")}`);
|
|
5935
5776
|
try {
|
|
5936
5777
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5937
5778
|
execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
@@ -5949,8 +5790,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5949
5790
|
|
|
5950
5791
|
// src/commands/proxy.ts
|
|
5951
5792
|
import { spawn as spawn2 } from "child_process";
|
|
5952
|
-
import { defineCommand as
|
|
5953
|
-
import
|
|
5793
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5794
|
+
import consola37 from "consola";
|
|
5954
5795
|
|
|
5955
5796
|
// src/proxy/config.ts
|
|
5956
5797
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5984,10 +5825,10 @@ note = "VPC-internal hostname suffix"
|
|
|
5984
5825
|
|
|
5985
5826
|
// src/proxy/local-proxy.ts
|
|
5986
5827
|
import { spawn } from "child_process";
|
|
5987
|
-
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as
|
|
5828
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
5988
5829
|
import { createRequire } from "module";
|
|
5989
5830
|
import { tmpdir as tmpdir3 } from "os";
|
|
5990
|
-
import { dirname as dirname4, join as
|
|
5831
|
+
import { dirname as dirname4, join as join13, resolve as resolve3 } from "path";
|
|
5991
5832
|
var require2 = createRequire(import.meta.url);
|
|
5992
5833
|
function findProxyBin() {
|
|
5993
5834
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -5999,9 +5840,9 @@ function findProxyBin() {
|
|
|
5999
5840
|
return resolve3(dirname4(pkgPath), binRel);
|
|
6000
5841
|
}
|
|
6001
5842
|
async function startEphemeralProxy(configToml) {
|
|
6002
|
-
const tmpDir = mkdtempSync3(
|
|
6003
|
-
const configPath =
|
|
6004
|
-
|
|
5843
|
+
const tmpDir = mkdtempSync3(join13(tmpdir3(), "openape-proxy-"));
|
|
5844
|
+
const configPath = join13(tmpDir, "config.toml");
|
|
5845
|
+
writeFileSync8(configPath, configToml, { mode: 384 });
|
|
6005
5846
|
const binPath = findProxyBin();
|
|
6006
5847
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
6007
5848
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6093,10 +5934,10 @@ function resolveProxyConfigOptions() {
|
|
|
6093
5934
|
77
|
|
6094
5935
|
);
|
|
6095
5936
|
}
|
|
6096
|
-
|
|
5937
|
+
consola37.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
6097
5938
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
6098
5939
|
}
|
|
6099
|
-
var proxyCommand =
|
|
5940
|
+
var proxyCommand = defineCommand43({
|
|
6100
5941
|
meta: {
|
|
6101
5942
|
name: "proxy",
|
|
6102
5943
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -6118,12 +5959,12 @@ var proxyCommand = defineCommand44({
|
|
|
6118
5959
|
let close = null;
|
|
6119
5960
|
if (reuseUrl) {
|
|
6120
5961
|
proxyUrl = reuseUrl;
|
|
6121
|
-
|
|
5962
|
+
consola37.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
6122
5963
|
} else {
|
|
6123
5964
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
6124
5965
|
proxyUrl = ephemeral.url;
|
|
6125
5966
|
close = ephemeral.close;
|
|
6126
|
-
|
|
5967
|
+
consola37.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
6127
5968
|
}
|
|
6128
5969
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
6129
5970
|
const childEnv = {
|
|
@@ -6155,7 +5996,7 @@ var proxyCommand = defineCommand44({
|
|
|
6155
5996
|
else resolveExit(code ?? 0);
|
|
6156
5997
|
});
|
|
6157
5998
|
child.once("error", (err) => {
|
|
6158
|
-
|
|
5999
|
+
consola37.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
6159
6000
|
resolveExit(127);
|
|
6160
6001
|
});
|
|
6161
6002
|
});
|
|
@@ -6169,8 +6010,8 @@ function signalNumber(signal) {
|
|
|
6169
6010
|
}
|
|
6170
6011
|
|
|
6171
6012
|
// src/commands/explain.ts
|
|
6172
|
-
import { defineCommand as
|
|
6173
|
-
var explainCommand =
|
|
6013
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
6014
|
+
var explainCommand = defineCommand44({
|
|
6174
6015
|
meta: {
|
|
6175
6016
|
name: "explain",
|
|
6176
6017
|
description: "Show what permission a command would need"
|
|
@@ -6208,9 +6049,9 @@ var explainCommand = defineCommand45({
|
|
|
6208
6049
|
});
|
|
6209
6050
|
|
|
6210
6051
|
// src/commands/config/get.ts
|
|
6211
|
-
import { defineCommand as
|
|
6212
|
-
import
|
|
6213
|
-
var configGetCommand =
|
|
6052
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
6053
|
+
import consola38 from "consola";
|
|
6054
|
+
var configGetCommand = defineCommand45({
|
|
6214
6055
|
meta: {
|
|
6215
6056
|
name: "get",
|
|
6216
6057
|
description: "Get a configuration value"
|
|
@@ -6230,7 +6071,7 @@ var configGetCommand = defineCommand46({
|
|
|
6230
6071
|
if (idp)
|
|
6231
6072
|
console.log(idp);
|
|
6232
6073
|
else
|
|
6233
|
-
|
|
6074
|
+
consola38.info("No IdP configured.");
|
|
6234
6075
|
break;
|
|
6235
6076
|
}
|
|
6236
6077
|
case "email": {
|
|
@@ -6238,7 +6079,7 @@ var configGetCommand = defineCommand46({
|
|
|
6238
6079
|
if (auth?.email)
|
|
6239
6080
|
console.log(auth.email);
|
|
6240
6081
|
else
|
|
6241
|
-
|
|
6082
|
+
consola38.info("Not logged in.");
|
|
6242
6083
|
break;
|
|
6243
6084
|
}
|
|
6244
6085
|
default: {
|
|
@@ -6251,7 +6092,7 @@ var configGetCommand = defineCommand46({
|
|
|
6251
6092
|
if (sectionObj && field in sectionObj) {
|
|
6252
6093
|
console.log(sectionObj[field]);
|
|
6253
6094
|
} else {
|
|
6254
|
-
|
|
6095
|
+
consola38.info(`Key "${key}" not set.`);
|
|
6255
6096
|
}
|
|
6256
6097
|
} else {
|
|
6257
6098
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -6262,9 +6103,9 @@ var configGetCommand = defineCommand46({
|
|
|
6262
6103
|
});
|
|
6263
6104
|
|
|
6264
6105
|
// src/commands/config/set.ts
|
|
6265
|
-
import { defineCommand as
|
|
6266
|
-
import
|
|
6267
|
-
var configSetCommand =
|
|
6106
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6107
|
+
import consola39 from "consola";
|
|
6108
|
+
var configSetCommand = defineCommand46({
|
|
6268
6109
|
meta: {
|
|
6269
6110
|
name: "set",
|
|
6270
6111
|
description: "Set a configuration value"
|
|
@@ -6300,12 +6141,12 @@ var configSetCommand = defineCommand47({
|
|
|
6300
6141
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
6301
6142
|
}
|
|
6302
6143
|
saveConfig(config);
|
|
6303
|
-
|
|
6144
|
+
consola39.success(`Set ${key} = ${value}`);
|
|
6304
6145
|
}
|
|
6305
6146
|
});
|
|
6306
6147
|
|
|
6307
6148
|
// src/commands/fetch/index.ts
|
|
6308
|
-
import { defineCommand as
|
|
6149
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
6309
6150
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
6310
6151
|
const token = getAuthToken();
|
|
6311
6152
|
if (!token) {
|
|
@@ -6341,13 +6182,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
6341
6182
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
6342
6183
|
}
|
|
6343
6184
|
}
|
|
6344
|
-
var fetchCommand =
|
|
6185
|
+
var fetchCommand = defineCommand47({
|
|
6345
6186
|
meta: {
|
|
6346
6187
|
name: "fetch",
|
|
6347
6188
|
description: "Make authenticated HTTP requests"
|
|
6348
6189
|
},
|
|
6349
6190
|
subCommands: {
|
|
6350
|
-
get:
|
|
6191
|
+
get: defineCommand47({
|
|
6351
6192
|
meta: {
|
|
6352
6193
|
name: "get",
|
|
6353
6194
|
description: "GET request with auth token"
|
|
@@ -6373,7 +6214,7 @@ var fetchCommand = defineCommand48({
|
|
|
6373
6214
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
6374
6215
|
}
|
|
6375
6216
|
}),
|
|
6376
|
-
post:
|
|
6217
|
+
post: defineCommand47({
|
|
6377
6218
|
meta: {
|
|
6378
6219
|
name: "post",
|
|
6379
6220
|
description: "POST request with auth token"
|
|
@@ -6412,8 +6253,8 @@ var fetchCommand = defineCommand48({
|
|
|
6412
6253
|
});
|
|
6413
6254
|
|
|
6414
6255
|
// src/commands/mcp/index.ts
|
|
6415
|
-
import { defineCommand as
|
|
6416
|
-
var mcpCommand =
|
|
6256
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
6257
|
+
var mcpCommand = defineCommand48({
|
|
6417
6258
|
meta: {
|
|
6418
6259
|
name: "mcp",
|
|
6419
6260
|
description: "Start MCP server for AI agents"
|
|
@@ -6436,25 +6277,25 @@ var mcpCommand = defineCommand49({
|
|
|
6436
6277
|
if (transport !== "stdio" && transport !== "sse") {
|
|
6437
6278
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
6438
6279
|
}
|
|
6439
|
-
const { startMcpServer } = await import("./server-
|
|
6280
|
+
const { startMcpServer } = await import("./server-TGGXHP4H.js");
|
|
6440
6281
|
await startMcpServer(transport, port);
|
|
6441
6282
|
}
|
|
6442
6283
|
});
|
|
6443
6284
|
|
|
6444
6285
|
// src/commands/init/index.ts
|
|
6445
|
-
import { existsSync as
|
|
6286
|
+
import { existsSync as existsSync15, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
|
|
6446
6287
|
import { randomBytes } from "crypto";
|
|
6447
6288
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
6448
|
-
import { join as
|
|
6449
|
-
import { defineCommand as
|
|
6450
|
-
import
|
|
6289
|
+
import { join as join14 } from "path";
|
|
6290
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6291
|
+
import consola40 from "consola";
|
|
6451
6292
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
6452
6293
|
async function downloadTemplate(repo, targetDir) {
|
|
6453
6294
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
6454
6295
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
6455
6296
|
}
|
|
6456
6297
|
function installDeps(dir) {
|
|
6457
|
-
const hasLockFile = (name) =>
|
|
6298
|
+
const hasLockFile = (name) => existsSync15(join14(dir, name));
|
|
6458
6299
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
6459
6300
|
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
6460
6301
|
} else if (hasLockFile("bun.lockb")) {
|
|
@@ -6464,20 +6305,20 @@ function installDeps(dir) {
|
|
|
6464
6305
|
}
|
|
6465
6306
|
}
|
|
6466
6307
|
async function promptChoice(message, choices) {
|
|
6467
|
-
const result = await
|
|
6308
|
+
const result = await consola40.prompt(message, { type: "select", options: choices });
|
|
6468
6309
|
if (typeof result === "symbol") {
|
|
6469
6310
|
throw new CliExit(0);
|
|
6470
6311
|
}
|
|
6471
6312
|
return result;
|
|
6472
6313
|
}
|
|
6473
6314
|
async function promptText(message, defaultValue) {
|
|
6474
|
-
const result = await
|
|
6315
|
+
const result = await consola40.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
6475
6316
|
if (typeof result === "symbol") {
|
|
6476
6317
|
throw new CliExit(0);
|
|
6477
6318
|
}
|
|
6478
6319
|
return result || defaultValue || "";
|
|
6479
6320
|
}
|
|
6480
|
-
var initCommand =
|
|
6321
|
+
var initCommand = defineCommand49({
|
|
6481
6322
|
meta: {
|
|
6482
6323
|
name: "init",
|
|
6483
6324
|
description: "Scaffold a new OpenApe project"
|
|
@@ -6519,23 +6360,23 @@ var initCommand = defineCommand50({
|
|
|
6519
6360
|
});
|
|
6520
6361
|
async function initSP(targetDir) {
|
|
6521
6362
|
const dir = targetDir || "my-app";
|
|
6522
|
-
if (
|
|
6363
|
+
if (existsSync15(join14(dir, "package.json"))) {
|
|
6523
6364
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
6524
6365
|
}
|
|
6525
|
-
|
|
6366
|
+
consola40.start("Scaffolding SP starter...");
|
|
6526
6367
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
6527
|
-
|
|
6528
|
-
|
|
6368
|
+
consola40.success("Scaffolded from openape-sp-starter");
|
|
6369
|
+
consola40.start("Installing dependencies...");
|
|
6529
6370
|
installDeps(dir);
|
|
6530
|
-
|
|
6531
|
-
const envExample =
|
|
6532
|
-
const envFile =
|
|
6533
|
-
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)) {
|
|
6534
6375
|
copyFileSync(envExample, envFile);
|
|
6535
|
-
|
|
6376
|
+
consola40.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
6536
6377
|
}
|
|
6537
6378
|
console.log("");
|
|
6538
|
-
|
|
6379
|
+
consola40.box([
|
|
6539
6380
|
`cd ${dir}`,
|
|
6540
6381
|
"npm run dev",
|
|
6541
6382
|
"",
|
|
@@ -6544,7 +6385,7 @@ async function initSP(targetDir) {
|
|
|
6544
6385
|
}
|
|
6545
6386
|
async function initIdP(targetDir) {
|
|
6546
6387
|
const dir = targetDir || "my-idp";
|
|
6547
|
-
if (
|
|
6388
|
+
if (existsSync15(join14(dir, "package.json"))) {
|
|
6548
6389
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
6549
6390
|
}
|
|
6550
6391
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -6554,15 +6395,15 @@ async function initIdP(targetDir) {
|
|
|
6554
6395
|
"s3 (S3-compatible)"
|
|
6555
6396
|
]);
|
|
6556
6397
|
const adminEmail = await promptText("Admin email");
|
|
6557
|
-
|
|
6398
|
+
consola40.start("Scaffolding IdP starter...");
|
|
6558
6399
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
6559
|
-
|
|
6560
|
-
|
|
6400
|
+
consola40.success("Scaffolded from openape-idp-starter");
|
|
6401
|
+
consola40.start("Installing dependencies...");
|
|
6561
6402
|
installDeps(dir);
|
|
6562
|
-
|
|
6403
|
+
consola40.success("Dependencies installed");
|
|
6563
6404
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
6564
6405
|
const managementToken = randomBytes(32).toString("hex");
|
|
6565
|
-
|
|
6406
|
+
consola40.success("Secrets generated");
|
|
6566
6407
|
const isLocalhost = domain === "localhost";
|
|
6567
6408
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
6568
6409
|
const envContent = [
|
|
@@ -6576,11 +6417,11 @@ async function initIdP(targetDir) {
|
|
|
6576
6417
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
6577
6418
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
6578
6419
|
].join("\n");
|
|
6579
|
-
|
|
6420
|
+
writeFileSync9(join14(dir, ".env"), `${envContent}
|
|
6580
6421
|
`, { mode: 384 });
|
|
6581
|
-
|
|
6422
|
+
consola40.success(".env created");
|
|
6582
6423
|
console.log("");
|
|
6583
|
-
|
|
6424
|
+
consola40.box([
|
|
6584
6425
|
`cd ${dir}`,
|
|
6585
6426
|
"npm run dev",
|
|
6586
6427
|
"",
|
|
@@ -6597,11 +6438,11 @@ async function initIdP(targetDir) {
|
|
|
6597
6438
|
|
|
6598
6439
|
// src/commands/enroll.ts
|
|
6599
6440
|
import { Buffer as Buffer5 } from "buffer";
|
|
6600
|
-
import { existsSync as
|
|
6441
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
|
|
6601
6442
|
import { execFile as execFile2 } from "child_process";
|
|
6602
6443
|
import { sign as sign2 } from "crypto";
|
|
6603
|
-
import { defineCommand as
|
|
6604
|
-
import
|
|
6444
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6445
|
+
import consola41 from "consola";
|
|
6605
6446
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
6606
6447
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
6607
6448
|
var POLL_INTERVAL = 3e3;
|
|
@@ -6613,7 +6454,7 @@ function openBrowser2(url) {
|
|
|
6613
6454
|
}
|
|
6614
6455
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
6615
6456
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6616
|
-
const keyContent =
|
|
6457
|
+
const keyContent = readFileSync12(resolvedKey, "utf-8");
|
|
6617
6458
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
6618
6459
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
6619
6460
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -6644,7 +6485,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
6644
6485
|
}
|
|
6645
6486
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
6646
6487
|
}
|
|
6647
|
-
var enrollCommand =
|
|
6488
|
+
var enrollCommand = defineCommand50({
|
|
6648
6489
|
meta: {
|
|
6649
6490
|
name: "enroll",
|
|
6650
6491
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -6664,38 +6505,38 @@ var enrollCommand = defineCommand51({
|
|
|
6664
6505
|
}
|
|
6665
6506
|
},
|
|
6666
6507
|
async run({ args }) {
|
|
6667
|
-
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) => {
|
|
6668
6509
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
6669
6510
|
return r3;
|
|
6670
6511
|
}) || DEFAULT_IDP_URL2;
|
|
6671
|
-
const agentName = args.name || await
|
|
6512
|
+
const agentName = args.name || await consola41.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r3) => {
|
|
6672
6513
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
6673
6514
|
return r3;
|
|
6674
6515
|
});
|
|
6675
6516
|
if (!agentName) {
|
|
6676
6517
|
throw new CliError("Agent name is required.");
|
|
6677
6518
|
}
|
|
6678
|
-
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) => {
|
|
6679
6520
|
if (typeof r3 === "symbol") throw new CliExit(0);
|
|
6680
6521
|
return r3;
|
|
6681
6522
|
}) || DEFAULT_KEY_PATH;
|
|
6682
6523
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6683
6524
|
let publicKey;
|
|
6684
|
-
if (
|
|
6525
|
+
if (existsSync16(resolvedKey)) {
|
|
6685
6526
|
publicKey = readPublicKey(resolvedKey);
|
|
6686
|
-
|
|
6527
|
+
consola41.success(`Using existing key ${keyPath}`);
|
|
6687
6528
|
} else {
|
|
6688
|
-
|
|
6529
|
+
consola41.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
6689
6530
|
publicKey = generateAndSaveKey(keyPath);
|
|
6690
|
-
|
|
6531
|
+
consola41.success(`Key pair generated at ${keyPath}`);
|
|
6691
6532
|
}
|
|
6692
6533
|
const encodedKey = encodeURIComponent(publicKey);
|
|
6693
6534
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
6694
|
-
|
|
6695
|
-
|
|
6535
|
+
consola41.info("Opening browser for enrollment...");
|
|
6536
|
+
consola41.info(`\u2192 ${idp}/enroll`);
|
|
6696
6537
|
openBrowser2(enrollUrl);
|
|
6697
6538
|
console.log("");
|
|
6698
|
-
const agentEmail = await
|
|
6539
|
+
const agentEmail = await consola41.prompt(
|
|
6699
6540
|
"Agent email (shown in browser after enrollment)",
|
|
6700
6541
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
6701
6542
|
).then((r3) => {
|
|
@@ -6705,7 +6546,7 @@ var enrollCommand = defineCommand51({
|
|
|
6705
6546
|
if (!agentEmail) {
|
|
6706
6547
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
6707
6548
|
}
|
|
6708
|
-
|
|
6549
|
+
consola41.start("Verifying enrollment...");
|
|
6709
6550
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
6710
6551
|
saveAuth({
|
|
6711
6552
|
idp,
|
|
@@ -6717,18 +6558,18 @@ var enrollCommand = defineCommand51({
|
|
|
6717
6558
|
config.defaults = { ...config.defaults, idp };
|
|
6718
6559
|
config.agent = { key: keyPath, email: agentEmail };
|
|
6719
6560
|
saveConfig(config);
|
|
6720
|
-
|
|
6721
|
-
|
|
6561
|
+
consola41.success(`Agent enrolled as ${agentEmail}`);
|
|
6562
|
+
consola41.success("Config saved to ~/.config/apes/");
|
|
6722
6563
|
console.log("");
|
|
6723
|
-
|
|
6564
|
+
consola41.info("Verify with: apes whoami");
|
|
6724
6565
|
}
|
|
6725
6566
|
});
|
|
6726
6567
|
|
|
6727
6568
|
// src/commands/register-user.ts
|
|
6728
|
-
import { existsSync as
|
|
6729
|
-
import { defineCommand as
|
|
6730
|
-
import
|
|
6731
|
-
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({
|
|
6732
6573
|
meta: {
|
|
6733
6574
|
name: "register-user",
|
|
6734
6575
|
description: "Register a sub-user with SSH key"
|
|
@@ -6764,8 +6605,8 @@ var registerUserCommand = defineCommand52({
|
|
|
6764
6605
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
6765
6606
|
}
|
|
6766
6607
|
let publicKey = args.key;
|
|
6767
|
-
if (
|
|
6768
|
-
publicKey =
|
|
6608
|
+
if (existsSync17(args.key)) {
|
|
6609
|
+
publicKey = readFileSync13(args.key, "utf-8").trim();
|
|
6769
6610
|
}
|
|
6770
6611
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
6771
6612
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -6783,18 +6624,18 @@ var registerUserCommand = defineCommand52({
|
|
|
6783
6624
|
...userType ? { type: userType } : {}
|
|
6784
6625
|
}
|
|
6785
6626
|
});
|
|
6786
|
-
|
|
6627
|
+
consola42.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
6787
6628
|
}
|
|
6788
6629
|
});
|
|
6789
6630
|
|
|
6790
6631
|
// src/commands/utils/index.ts
|
|
6791
|
-
import { defineCommand as
|
|
6632
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
6792
6633
|
|
|
6793
6634
|
// src/commands/utils/dig.ts
|
|
6794
|
-
import { defineCommand as
|
|
6795
|
-
import
|
|
6635
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
6636
|
+
import consola43 from "consola";
|
|
6796
6637
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
6797
|
-
var digCommand =
|
|
6638
|
+
var digCommand = defineCommand52({
|
|
6798
6639
|
meta: {
|
|
6799
6640
|
name: "dig",
|
|
6800
6641
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -6867,12 +6708,12 @@ var digCommand = defineCommand53({
|
|
|
6867
6708
|
console.log(` domain: ${domain}`);
|
|
6868
6709
|
console.log("");
|
|
6869
6710
|
if (!result.ddisa.found) {
|
|
6870
|
-
|
|
6711
|
+
consola43.warn(`No DDISA record at _ddisa.${domain}`);
|
|
6871
6712
|
if (result.hint) console.log(`
|
|
6872
6713
|
${result.hint}`);
|
|
6873
6714
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6874
6715
|
}
|
|
6875
|
-
|
|
6716
|
+
consola43.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
6876
6717
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
6877
6718
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
6878
6719
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -6882,13 +6723,13 @@ ${result.hint}`);
|
|
|
6882
6723
|
return;
|
|
6883
6724
|
}
|
|
6884
6725
|
if (result.idpDiscovery.ok) {
|
|
6885
|
-
|
|
6726
|
+
consola43.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
6886
6727
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
6887
6728
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
6888
6729
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
6889
6730
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
6890
6731
|
} else {
|
|
6891
|
-
|
|
6732
|
+
consola43.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
6892
6733
|
if (result.hint) console.log(`
|
|
6893
6734
|
${result.hint}`);
|
|
6894
6735
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -6897,7 +6738,7 @@ ${result.hint}`);
|
|
|
6897
6738
|
});
|
|
6898
6739
|
|
|
6899
6740
|
// src/commands/utils/index.ts
|
|
6900
|
-
var utilsCommand =
|
|
6741
|
+
var utilsCommand = defineCommand53({
|
|
6901
6742
|
meta: {
|
|
6902
6743
|
name: "utils",
|
|
6903
6744
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -6908,12 +6749,12 @@ var utilsCommand = defineCommand54({
|
|
|
6908
6749
|
});
|
|
6909
6750
|
|
|
6910
6751
|
// src/commands/sessions/index.ts
|
|
6911
|
-
import { defineCommand as
|
|
6752
|
+
import { defineCommand as defineCommand56 } from "citty";
|
|
6912
6753
|
|
|
6913
6754
|
// src/commands/sessions/list.ts
|
|
6914
|
-
import { defineCommand as
|
|
6915
|
-
import
|
|
6916
|
-
var sessionsListCommand =
|
|
6755
|
+
import { defineCommand as defineCommand54 } from "citty";
|
|
6756
|
+
import consola44 from "consola";
|
|
6757
|
+
var sessionsListCommand = defineCommand54({
|
|
6917
6758
|
meta: {
|
|
6918
6759
|
name: "list",
|
|
6919
6760
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6931,7 +6772,7 @@ var sessionsListCommand = defineCommand55({
|
|
|
6931
6772
|
return;
|
|
6932
6773
|
}
|
|
6933
6774
|
if (result.data.length === 0) {
|
|
6934
|
-
|
|
6775
|
+
consola44.info("No active sessions.");
|
|
6935
6776
|
return;
|
|
6936
6777
|
}
|
|
6937
6778
|
for (const f of result.data) {
|
|
@@ -6943,9 +6784,9 @@ var sessionsListCommand = defineCommand55({
|
|
|
6943
6784
|
});
|
|
6944
6785
|
|
|
6945
6786
|
// src/commands/sessions/remove.ts
|
|
6946
|
-
import { defineCommand as
|
|
6947
|
-
import
|
|
6948
|
-
var sessionsRemoveCommand =
|
|
6787
|
+
import { defineCommand as defineCommand55 } from "citty";
|
|
6788
|
+
import consola45 from "consola";
|
|
6789
|
+
var sessionsRemoveCommand = defineCommand55({
|
|
6949
6790
|
meta: {
|
|
6950
6791
|
name: "remove",
|
|
6951
6792
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6961,12 +6802,12 @@ var sessionsRemoveCommand = defineCommand56({
|
|
|
6961
6802
|
const id = String(args.familyId).trim();
|
|
6962
6803
|
if (!id) throw new CliError("familyId required");
|
|
6963
6804
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6964
|
-
|
|
6805
|
+
consola45.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6965
6806
|
}
|
|
6966
6807
|
});
|
|
6967
6808
|
|
|
6968
6809
|
// src/commands/sessions/index.ts
|
|
6969
|
-
var sessionsCommand =
|
|
6810
|
+
var sessionsCommand = defineCommand56({
|
|
6970
6811
|
meta: {
|
|
6971
6812
|
name: "sessions",
|
|
6972
6813
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6978,10 +6819,10 @@ var sessionsCommand = defineCommand57({
|
|
|
6978
6819
|
});
|
|
6979
6820
|
|
|
6980
6821
|
// src/commands/dns-check.ts
|
|
6981
|
-
import { defineCommand as
|
|
6982
|
-
import
|
|
6822
|
+
import { defineCommand as defineCommand57 } from "citty";
|
|
6823
|
+
import consola46 from "consola";
|
|
6983
6824
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6984
|
-
var dnsCheckCommand =
|
|
6825
|
+
var dnsCheckCommand = defineCommand57({
|
|
6985
6826
|
meta: {
|
|
6986
6827
|
name: "dns-check",
|
|
6987
6828
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6995,7 +6836,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
6995
6836
|
},
|
|
6996
6837
|
async run({ args }) {
|
|
6997
6838
|
const domain = args.domain;
|
|
6998
|
-
|
|
6839
|
+
consola46.start(`Checking _ddisa.${domain}...`);
|
|
6999
6840
|
try {
|
|
7000
6841
|
const result = await resolveDDISA3(domain);
|
|
7001
6842
|
if (!result) {
|
|
@@ -7004,7 +6845,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7004
6845
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
7005
6846
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
7006
6847
|
}
|
|
7007
|
-
|
|
6848
|
+
consola46.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
7008
6849
|
console.log("");
|
|
7009
6850
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
7010
6851
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -7013,14 +6854,14 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7013
6854
|
if (result.priority !== void 0)
|
|
7014
6855
|
console.log(` Priority: ${result.priority}`);
|
|
7015
6856
|
console.log("");
|
|
7016
|
-
|
|
6857
|
+
consola46.start(`Verifying IdP at ${result.idp}...`);
|
|
7017
6858
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
7018
6859
|
if (!discoResp.ok) {
|
|
7019
|
-
|
|
6860
|
+
consola46.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
7020
6861
|
return;
|
|
7021
6862
|
}
|
|
7022
6863
|
const disco = await discoResp.json();
|
|
7023
|
-
|
|
6864
|
+
consola46.success(`IdP is reachable`);
|
|
7024
6865
|
console.log(` Issuer: ${disco.issuer}`);
|
|
7025
6866
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
7026
6867
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -7038,7 +6879,7 @@ var dnsCheckCommand = defineCommand58({
|
|
|
7038
6879
|
// src/commands/health.ts
|
|
7039
6880
|
import { exec } from "child_process";
|
|
7040
6881
|
import { promisify } from "util";
|
|
7041
|
-
import { defineCommand as
|
|
6882
|
+
import { defineCommand as defineCommand58 } from "citty";
|
|
7042
6883
|
var execAsync = promisify(exec);
|
|
7043
6884
|
async function resolveApeShellPath() {
|
|
7044
6885
|
try {
|
|
@@ -7074,7 +6915,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
7074
6915
|
}
|
|
7075
6916
|
}
|
|
7076
6917
|
async function runHealth(args) {
|
|
7077
|
-
const version = true ? "1.
|
|
6918
|
+
const version = true ? "1.12.0" : "0.0.0";
|
|
7078
6919
|
const auth = loadAuth();
|
|
7079
6920
|
if (!auth) {
|
|
7080
6921
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -7137,7 +6978,7 @@ async function runHealth(args) {
|
|
|
7137
6978
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
7138
6979
|
}
|
|
7139
6980
|
}
|
|
7140
|
-
var healthCommand =
|
|
6981
|
+
var healthCommand = defineCommand58({
|
|
7141
6982
|
meta: {
|
|
7142
6983
|
name: "health",
|
|
7143
6984
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -7155,8 +6996,8 @@ var healthCommand = defineCommand59({
|
|
|
7155
6996
|
});
|
|
7156
6997
|
|
|
7157
6998
|
// src/commands/workflows.ts
|
|
7158
|
-
import { defineCommand as
|
|
7159
|
-
import
|
|
6999
|
+
import { defineCommand as defineCommand59 } from "citty";
|
|
7000
|
+
import consola47 from "consola";
|
|
7160
7001
|
|
|
7161
7002
|
// src/guides/index.ts
|
|
7162
7003
|
var guides = [
|
|
@@ -7206,7 +7047,7 @@ var guides = [
|
|
|
7206
7047
|
];
|
|
7207
7048
|
|
|
7208
7049
|
// src/commands/workflows.ts
|
|
7209
|
-
var workflowsCommand =
|
|
7050
|
+
var workflowsCommand = defineCommand59({
|
|
7210
7051
|
meta: {
|
|
7211
7052
|
name: "workflows",
|
|
7212
7053
|
description: "Discover workflow guides"
|
|
@@ -7227,7 +7068,7 @@ var workflowsCommand = defineCommand60({
|
|
|
7227
7068
|
if (args.id) {
|
|
7228
7069
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
7229
7070
|
if (!guide) {
|
|
7230
|
-
|
|
7071
|
+
consola47.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
7231
7072
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
7232
7073
|
}
|
|
7233
7074
|
if (args.json) {
|
|
@@ -7267,26 +7108,26 @@ var workflowsCommand = defineCommand60({
|
|
|
7267
7108
|
});
|
|
7268
7109
|
|
|
7269
7110
|
// src/version-check.ts
|
|
7270
|
-
import { existsSync as
|
|
7271
|
-
import { homedir as
|
|
7272
|
-
import { join as
|
|
7273
|
-
import
|
|
7111
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
7112
|
+
import { homedir as homedir13 } from "os";
|
|
7113
|
+
import { join as join15 } from "path";
|
|
7114
|
+
import consola48 from "consola";
|
|
7274
7115
|
var PACKAGE_NAME = "@openape/apes";
|
|
7275
7116
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
7276
|
-
var CACHE_FILE =
|
|
7117
|
+
var CACHE_FILE = join15(homedir13(), ".config", "apes", ".version-check.json");
|
|
7277
7118
|
function readCache() {
|
|
7278
|
-
if (!
|
|
7119
|
+
if (!existsSync18(CACHE_FILE)) return null;
|
|
7279
7120
|
try {
|
|
7280
|
-
return JSON.parse(
|
|
7121
|
+
return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
|
|
7281
7122
|
} catch {
|
|
7282
7123
|
return null;
|
|
7283
7124
|
}
|
|
7284
7125
|
}
|
|
7285
7126
|
function writeCache(entry) {
|
|
7286
7127
|
try {
|
|
7287
|
-
const dir =
|
|
7288
|
-
if (!
|
|
7289
|
-
|
|
7128
|
+
const dir = join15(homedir13(), ".config", "apes");
|
|
7129
|
+
if (!existsSync18(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
7130
|
+
writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
7290
7131
|
} catch {
|
|
7291
7132
|
}
|
|
7292
7133
|
}
|
|
@@ -7315,7 +7156,7 @@ async function fetchLatestVersion() {
|
|
|
7315
7156
|
}
|
|
7316
7157
|
function warnIfBehind(currentVersion, latest) {
|
|
7317
7158
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
7318
|
-
|
|
7159
|
+
consola48.warn(
|
|
7319
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.)`
|
|
7320
7161
|
);
|
|
7321
7162
|
}
|
|
@@ -7347,10 +7188,10 @@ if (shellRewrite) {
|
|
|
7347
7188
|
if (shellRewrite.action === "rewrite") {
|
|
7348
7189
|
process.argv = shellRewrite.argv;
|
|
7349
7190
|
} else if (shellRewrite.action === "version") {
|
|
7350
|
-
console.log(`ape-shell ${"1.
|
|
7191
|
+
console.log(`ape-shell ${"1.12.0"} (OpenApe DDISA shell wrapper)`);
|
|
7351
7192
|
process.exit(0);
|
|
7352
7193
|
} else if (shellRewrite.action === "help") {
|
|
7353
|
-
console.log(`ape-shell ${"1.
|
|
7194
|
+
console.log(`ape-shell ${"1.12.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
7354
7195
|
console.log("");
|
|
7355
7196
|
console.log("Usage:");
|
|
7356
7197
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -7374,7 +7215,7 @@ if (shellRewrite) {
|
|
|
7374
7215
|
}
|
|
7375
7216
|
}
|
|
7376
7217
|
var debug = process.argv.includes("--debug");
|
|
7377
|
-
var grantsCommand =
|
|
7218
|
+
var grantsCommand = defineCommand60({
|
|
7378
7219
|
meta: {
|
|
7379
7220
|
name: "grants",
|
|
7380
7221
|
description: "Grant management"
|
|
@@ -7395,7 +7236,7 @@ var grantsCommand = defineCommand61({
|
|
|
7395
7236
|
"delegation-revoke": delegationRevokeCommand
|
|
7396
7237
|
}
|
|
7397
7238
|
});
|
|
7398
|
-
var configCommand =
|
|
7239
|
+
var configCommand = defineCommand60({
|
|
7399
7240
|
meta: {
|
|
7400
7241
|
name: "config",
|
|
7401
7242
|
description: "Configuration management"
|
|
@@ -7405,10 +7246,10 @@ var configCommand = defineCommand61({
|
|
|
7405
7246
|
set: configSetCommand
|
|
7406
7247
|
}
|
|
7407
7248
|
});
|
|
7408
|
-
var main =
|
|
7249
|
+
var main = defineCommand60({
|
|
7409
7250
|
meta: {
|
|
7410
7251
|
name: "apes",
|
|
7411
|
-
version: "1.
|
|
7252
|
+
version: "1.12.0",
|
|
7412
7253
|
description: "Unified CLI for OpenApe"
|
|
7413
7254
|
},
|
|
7414
7255
|
subCommands: {
|
|
@@ -7465,20 +7306,20 @@ async function maybeRefreshAuth() {
|
|
|
7465
7306
|
}
|
|
7466
7307
|
}
|
|
7467
7308
|
await maybeRefreshAuth();
|
|
7468
|
-
await maybeWarnStaleVersion("1.
|
|
7309
|
+
await maybeWarnStaleVersion("1.12.0").catch(() => {
|
|
7469
7310
|
});
|
|
7470
7311
|
runMain(main).catch((err) => {
|
|
7471
7312
|
if (err instanceof CliExit) {
|
|
7472
7313
|
process.exit(err.exitCode);
|
|
7473
7314
|
}
|
|
7474
7315
|
if (err instanceof CliError) {
|
|
7475
|
-
|
|
7316
|
+
consola49.error(err.message);
|
|
7476
7317
|
process.exit(err.exitCode);
|
|
7477
7318
|
}
|
|
7478
7319
|
if (debug) {
|
|
7479
|
-
|
|
7320
|
+
consola49.error(err);
|
|
7480
7321
|
} else {
|
|
7481
|
-
|
|
7322
|
+
consola49.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
7482
7323
|
}
|
|
7483
7324
|
process.exit(1);
|
|
7484
7325
|
});
|