@openape/apes 1.30.0 → 1.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-3LH4FT4R.js +138 -0
- package/dist/chunk-3LH4FT4R.js.map +1 -0
- package/dist/{chunk-NYJSBFLG.js → chunk-MMBFV5WN.js} +14 -6
- package/dist/{chunk-NYJSBFLG.js.map → chunk-MMBFV5WN.js.map} +1 -1
- package/dist/cli.js +690 -644
- package/dist/cli.js.map +1 -1
- package/dist/{http-UPOTOYQV.js → http-SILH37L7.js} +2 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/{orchestrator-P7QFDBBK.js → orchestrator-6PZXCE54.js} +2 -2
- package/dist/{server-X4HHOCKV.js → server-32CG5WG2.js} +3 -3
- package/package.json +5 -5
- package/dist/chunk-QMMRZPD2.js +0 -47
- package/dist/chunk-QMMRZPD2.js.map +0 -1
- /package/dist/{http-UPOTOYQV.js.map → http-SILH37L7.js.map} +0 -0
- /package/dist/{orchestrator-P7QFDBBK.js.map → orchestrator-6PZXCE54.js.map} +0 -0
- /package/dist/{server-X4HHOCKV.js.map → server-32CG5WG2.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CliError,
|
|
4
4
|
CliExit,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
materializeSecrets,
|
|
6
|
+
parseDuration,
|
|
7
|
+
readAgentEncryptionPublicKey,
|
|
8
|
+
startSecretsWatcher
|
|
9
|
+
} from "./chunk-3LH4FT4R.js";
|
|
7
10
|
import {
|
|
8
11
|
loadEd25519PrivateKey,
|
|
9
12
|
readPublicKeyComment
|
|
@@ -50,7 +53,7 @@ import {
|
|
|
50
53
|
getAgentChallengeEndpoint,
|
|
51
54
|
getDelegationsEndpoint,
|
|
52
55
|
getGrantsEndpoint
|
|
53
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-MMBFV5WN.js";
|
|
54
57
|
import {
|
|
55
58
|
AUTH_FILE,
|
|
56
59
|
CONFIG_DIR,
|
|
@@ -65,7 +68,7 @@ import {
|
|
|
65
68
|
} from "./chunk-OBF7IMQ2.js";
|
|
66
69
|
|
|
67
70
|
// src/cli.ts
|
|
68
|
-
import
|
|
71
|
+
import consola54 from "consola";
|
|
69
72
|
|
|
70
73
|
// src/ape-shell.ts
|
|
71
74
|
import path from "path";
|
|
@@ -95,10 +98,10 @@ function rewriteApeShellArgs(argv, argv0) {
|
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
// src/cli.ts
|
|
98
|
-
import { defineCommand as
|
|
101
|
+
import { defineCommand as defineCommand65, runMain } from "citty";
|
|
99
102
|
|
|
100
103
|
// src/commands/auth/login.ts
|
|
101
|
-
import { Buffer
|
|
104
|
+
import { Buffer } from "buffer";
|
|
102
105
|
import { execFile } from "child_process";
|
|
103
106
|
import { createServer } from "http";
|
|
104
107
|
import { homedir as homedir2 } from "os";
|
|
@@ -308,7 +311,7 @@ async function loginWithPKCE(idp) {
|
|
|
308
311
|
authUrl.searchParams.set("state", state);
|
|
309
312
|
authUrl.searchParams.set("nonce", nonce);
|
|
310
313
|
authUrl.searchParams.set("scope", "openid email profile offline_access");
|
|
311
|
-
const code = await new Promise((
|
|
314
|
+
const code = await new Promise((resolve5, reject) => {
|
|
312
315
|
const server = createServer((req, res) => {
|
|
313
316
|
const url = new URL(req.url, `http://localhost:${CALLBACK_PORT}`);
|
|
314
317
|
if (url.pathname === "/callback") {
|
|
@@ -325,7 +328,7 @@ async function loginWithPKCE(idp) {
|
|
|
325
328
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
326
329
|
res.end("<h1>Login successful!</h1><p>You can close this window.</p>");
|
|
327
330
|
server.close();
|
|
328
|
-
|
|
331
|
+
resolve5(authCode);
|
|
329
332
|
return;
|
|
330
333
|
}
|
|
331
334
|
res.writeHead(400);
|
|
@@ -397,7 +400,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
|
|
|
397
400
|
const { challenge } = await challengeResp.json();
|
|
398
401
|
const keyContent = readFileSync18(keyPath, "utf-8");
|
|
399
402
|
const privateKey = loadEd25519PrivateKey2(keyContent);
|
|
400
|
-
const signature = sign3(null,
|
|
403
|
+
const signature = sign3(null, Buffer.from(challenge), privateKey).toString("base64");
|
|
401
404
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
402
405
|
const authResp = await fetch(authenticateUrl, {
|
|
403
406
|
method: "POST",
|
|
@@ -888,7 +891,7 @@ async function waitForApproval2(grantsUrl, grantId) {
|
|
|
888
891
|
if (grant.status === "revoked") {
|
|
889
892
|
throw new CliError("Grant revoked.");
|
|
890
893
|
}
|
|
891
|
-
await new Promise((
|
|
894
|
+
await new Promise((resolve5) => setTimeout(resolve5, interval));
|
|
892
895
|
}
|
|
893
896
|
throw new CliError("Timed out waiting for approval.");
|
|
894
897
|
}
|
|
@@ -1460,12 +1463,75 @@ var delegationRevokeCommand = defineCommand16({
|
|
|
1460
1463
|
}
|
|
1461
1464
|
});
|
|
1462
1465
|
|
|
1466
|
+
// src/commands/grants/llm.ts
|
|
1467
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
1468
|
+
import consola16 from "consola";
|
|
1469
|
+
var AUDIENCE = "llms.openape.ai";
|
|
1470
|
+
var RESOURCE = "llm-account";
|
|
1471
|
+
function templateFor(account) {
|
|
1472
|
+
return account === "*" ? [{ resource: RESOURCE }] : [{ resource: RESOURCE, selector: { account } }];
|
|
1473
|
+
}
|
|
1474
|
+
function accountOf(g) {
|
|
1475
|
+
return (g.request.resource_chain_template ?? []).filter((r) => r.resource === RESOURCE).map((r) => r.selector?.account ?? "*").join(",") || "?";
|
|
1476
|
+
}
|
|
1477
|
+
var allow = defineCommand17({
|
|
1478
|
+
meta: { name: "allow", description: "Allow an agent to use an LLM account on llms.openape.ai" },
|
|
1479
|
+
args: {
|
|
1480
|
+
agent: { type: "positional", required: true, description: "Agent DDISA email (the grant delegate)" },
|
|
1481
|
+
account: { type: "positional", required: true, description: "LLM account name, or * for all" }
|
|
1482
|
+
},
|
|
1483
|
+
async run({ args }) {
|
|
1484
|
+
const agent = String(args.agent);
|
|
1485
|
+
if (!agent.includes("@")) {
|
|
1486
|
+
throw new CliError("Pass the full agent DDISA email (e.g. from `ape-troop agents list`), not a short name.");
|
|
1487
|
+
}
|
|
1488
|
+
const account = String(args.account);
|
|
1489
|
+
const grant = await apiFetch("/api/standing-grants", {
|
|
1490
|
+
method: "POST",
|
|
1491
|
+
body: {
|
|
1492
|
+
delegate: agent,
|
|
1493
|
+
audience: AUDIENCE,
|
|
1494
|
+
resource_chain_template: templateFor(account),
|
|
1495
|
+
grant_type: "always",
|
|
1496
|
+
reason: `LLM gateway access: ${account}`
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
consola16.success(`Granted ${agent} \u2192 llm-account[${account}] (standing grant ${grant.id})`);
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
1502
|
+
var list = defineCommand17({
|
|
1503
|
+
meta: { name: "list", description: "List LLM-account standing grants (optionally for one agent)" },
|
|
1504
|
+
args: { agent: { type: "positional", required: false, description: "Filter by agent email" } },
|
|
1505
|
+
async run({ args }) {
|
|
1506
|
+
const all = await apiFetch("/api/standing-grants");
|
|
1507
|
+
const filter = args.agent ? String(args.agent) : void 0;
|
|
1508
|
+
const llm = all.filter((g) => g.request.audience === AUDIENCE && (!filter || g.request.delegate === filter));
|
|
1509
|
+
if (!llm.length) {
|
|
1510
|
+
consola16.info("No LLM-account standing grants.");
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
for (const g of llm) consola16.log(`${g.id} ${g.request.delegate} \u2192 ${accountOf(g)} [${g.status}]`);
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
var revoke = defineCommand17({
|
|
1517
|
+
meta: { name: "revoke", description: "Revoke an LLM-account standing grant by id" },
|
|
1518
|
+
args: { id: { type: "positional", required: true, description: "Standing grant id" } },
|
|
1519
|
+
async run({ args }) {
|
|
1520
|
+
await apiFetch(`/api/standing-grants/${String(args.id)}`, { method: "DELETE" });
|
|
1521
|
+
consola16.success(`Revoked standing grant ${args.id}`);
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1524
|
+
var llmCommand = defineCommand17({
|
|
1525
|
+
meta: { name: "llm", description: "Grant agents access to LLM accounts on llms.openape.ai" },
|
|
1526
|
+
subCommands: { allow, list, revoke }
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1463
1529
|
// src/commands/admin/index.ts
|
|
1464
|
-
import { defineCommand as
|
|
1530
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
1465
1531
|
|
|
1466
1532
|
// src/commands/admin/users.ts
|
|
1467
|
-
import { defineCommand as
|
|
1468
|
-
import
|
|
1533
|
+
import { defineCommand as defineCommand18 } from "citty";
|
|
1534
|
+
import consola17 from "consola";
|
|
1469
1535
|
function getManagementToken() {
|
|
1470
1536
|
const token = process.env.APES_MANAGEMENT_TOKEN;
|
|
1471
1537
|
if (!token) {
|
|
@@ -1473,7 +1539,7 @@ function getManagementToken() {
|
|
|
1473
1539
|
}
|
|
1474
1540
|
return token;
|
|
1475
1541
|
}
|
|
1476
|
-
var usersListCommand =
|
|
1542
|
+
var usersListCommand = defineCommand18({
|
|
1477
1543
|
meta: {
|
|
1478
1544
|
name: "list",
|
|
1479
1545
|
description: "List all users"
|
|
@@ -1515,7 +1581,7 @@ var usersListCommand = defineCommand17({
|
|
|
1515
1581
|
return;
|
|
1516
1582
|
}
|
|
1517
1583
|
if (result.data.length === 0) {
|
|
1518
|
-
|
|
1584
|
+
consola17.info("No users found.");
|
|
1519
1585
|
return;
|
|
1520
1586
|
}
|
|
1521
1587
|
for (const u of result.data) {
|
|
@@ -1524,11 +1590,11 @@ var usersListCommand = defineCommand17({
|
|
|
1524
1590
|
console.log(`${u.email} ${u.name}${owner}${active}`);
|
|
1525
1591
|
}
|
|
1526
1592
|
if (result.pagination.has_more) {
|
|
1527
|
-
|
|
1593
|
+
consola17.info(`More results available. Use --cursor="${result.pagination.cursor}" to see next page.`);
|
|
1528
1594
|
}
|
|
1529
1595
|
}
|
|
1530
1596
|
});
|
|
1531
|
-
var usersCreateCommand =
|
|
1597
|
+
var usersCreateCommand = defineCommand18({
|
|
1532
1598
|
meta: {
|
|
1533
1599
|
name: "create",
|
|
1534
1600
|
description: "Create a user"
|
|
@@ -1559,10 +1625,10 @@ var usersCreateCommand = defineCommand17({
|
|
|
1559
1625
|
token
|
|
1560
1626
|
}
|
|
1561
1627
|
);
|
|
1562
|
-
|
|
1628
|
+
consola17.success(`User created: ${result.email} (${result.name})`);
|
|
1563
1629
|
}
|
|
1564
1630
|
});
|
|
1565
|
-
var usersDeleteCommand =
|
|
1631
|
+
var usersDeleteCommand = defineCommand18({
|
|
1566
1632
|
meta: {
|
|
1567
1633
|
name: "delete",
|
|
1568
1634
|
description: "Delete a user"
|
|
@@ -1585,7 +1651,7 @@ var usersDeleteCommand = defineCommand17({
|
|
|
1585
1651
|
method: "DELETE",
|
|
1586
1652
|
token
|
|
1587
1653
|
});
|
|
1588
|
-
|
|
1654
|
+
consola17.success(`User deleted: ${email}`);
|
|
1589
1655
|
}
|
|
1590
1656
|
});
|
|
1591
1657
|
|
|
@@ -1593,8 +1659,8 @@ var usersDeleteCommand = defineCommand17({
|
|
|
1593
1659
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
1594
1660
|
import { resolve } from "path";
|
|
1595
1661
|
import { homedir as homedir3 } from "os";
|
|
1596
|
-
import { defineCommand as
|
|
1597
|
-
import
|
|
1662
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
1663
|
+
import consola18 from "consola";
|
|
1598
1664
|
function getManagementToken2() {
|
|
1599
1665
|
const token = process.env.APES_MANAGEMENT_TOKEN;
|
|
1600
1666
|
if (!token) {
|
|
@@ -1602,7 +1668,7 @@ function getManagementToken2() {
|
|
|
1602
1668
|
}
|
|
1603
1669
|
return token;
|
|
1604
1670
|
}
|
|
1605
|
-
var sshKeysListCommand =
|
|
1671
|
+
var sshKeysListCommand = defineCommand19({
|
|
1606
1672
|
meta: {
|
|
1607
1673
|
name: "list",
|
|
1608
1674
|
description: "List SSH keys for a user"
|
|
@@ -1635,7 +1701,7 @@ var sshKeysListCommand = defineCommand18({
|
|
|
1635
1701
|
return;
|
|
1636
1702
|
}
|
|
1637
1703
|
if (keys.length === 0) {
|
|
1638
|
-
|
|
1704
|
+
consola18.info(`No SSH keys found for ${email}.`);
|
|
1639
1705
|
return;
|
|
1640
1706
|
}
|
|
1641
1707
|
for (const k of keys) {
|
|
@@ -1643,7 +1709,7 @@ var sshKeysListCommand = defineCommand18({
|
|
|
1643
1709
|
}
|
|
1644
1710
|
}
|
|
1645
1711
|
});
|
|
1646
|
-
var sshKeysAddCommand =
|
|
1712
|
+
var sshKeysAddCommand = defineCommand19({
|
|
1647
1713
|
meta: {
|
|
1648
1714
|
name: "add",
|
|
1649
1715
|
description: "Add an SSH key for a user"
|
|
@@ -1687,10 +1753,10 @@ var sshKeysAddCommand = defineCommand18({
|
|
|
1687
1753
|
token
|
|
1688
1754
|
}
|
|
1689
1755
|
);
|
|
1690
|
-
|
|
1756
|
+
consola18.success(`SSH key added: ${result.keyId} (${result.name})`);
|
|
1691
1757
|
}
|
|
1692
1758
|
});
|
|
1693
|
-
var sshKeysDeleteCommand =
|
|
1759
|
+
var sshKeysDeleteCommand = defineCommand19({
|
|
1694
1760
|
meta: {
|
|
1695
1761
|
name: "delete",
|
|
1696
1762
|
description: "Delete an SSH key"
|
|
@@ -1721,12 +1787,12 @@ var sshKeysDeleteCommand = defineCommand18({
|
|
|
1721
1787
|
token
|
|
1722
1788
|
}
|
|
1723
1789
|
);
|
|
1724
|
-
|
|
1790
|
+
consola18.success(`SSH key deleted: ${keyId}`);
|
|
1725
1791
|
}
|
|
1726
1792
|
});
|
|
1727
1793
|
|
|
1728
1794
|
// src/commands/admin/index.ts
|
|
1729
|
-
var usersCommand =
|
|
1795
|
+
var usersCommand = defineCommand20({
|
|
1730
1796
|
meta: {
|
|
1731
1797
|
name: "users",
|
|
1732
1798
|
description: "Manage users"
|
|
@@ -1737,7 +1803,7 @@ var usersCommand = defineCommand19({
|
|
|
1737
1803
|
delete: usersDeleteCommand
|
|
1738
1804
|
}
|
|
1739
1805
|
});
|
|
1740
|
-
var sshKeysCommand =
|
|
1806
|
+
var sshKeysCommand = defineCommand20({
|
|
1741
1807
|
meta: {
|
|
1742
1808
|
name: "ssh-keys",
|
|
1743
1809
|
description: "Manage SSH keys"
|
|
@@ -1748,7 +1814,7 @@ var sshKeysCommand = defineCommand19({
|
|
|
1748
1814
|
delete: sshKeysDeleteCommand
|
|
1749
1815
|
}
|
|
1750
1816
|
});
|
|
1751
|
-
var adminCommand =
|
|
1817
|
+
var adminCommand = defineCommand20({
|
|
1752
1818
|
meta: {
|
|
1753
1819
|
name: "admin",
|
|
1754
1820
|
description: "Admin commands (requires APES_MANAGEMENT_TOKEN)"
|
|
@@ -1760,12 +1826,12 @@ var adminCommand = defineCommand19({
|
|
|
1760
1826
|
});
|
|
1761
1827
|
|
|
1762
1828
|
// src/commands/agent/index.ts
|
|
1763
|
-
import { defineCommand as
|
|
1829
|
+
import { defineCommand as defineCommand22 } from "citty";
|
|
1764
1830
|
|
|
1765
1831
|
// src/commands/agent/deploy.ts
|
|
1766
1832
|
import { ensureFreshIdpAuth } from "@openape/cli-auth";
|
|
1767
|
-
import { defineCommand as
|
|
1768
|
-
import
|
|
1833
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
1834
|
+
import consola19 from "consola";
|
|
1769
1835
|
|
|
1770
1836
|
// src/lib/troop-client.ts
|
|
1771
1837
|
var DEFAULT_TROOP_URL = "https://troop.openape.ai";
|
|
@@ -1873,7 +1939,7 @@ async function api(url, token, init) {
|
|
|
1873
1939
|
return res.json();
|
|
1874
1940
|
}
|
|
1875
1941
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1876
|
-
var deployAgentCommand =
|
|
1942
|
+
var deployAgentCommand = defineCommand21({
|
|
1877
1943
|
meta: { name: "deploy", description: "Deploy an Agent Recipe (<repo>@<ref>) in one step" },
|
|
1878
1944
|
args: {
|
|
1879
1945
|
repo: { type: "positional", description: "Recipe repo + pinned ref, e.g. github.com/owner/name@v1.0.0" },
|
|
@@ -1899,8 +1965,8 @@ var deployAgentCommand = defineCommand20({
|
|
|
1899
1965
|
})
|
|
1900
1966
|
});
|
|
1901
1967
|
if (!json) {
|
|
1902
|
-
|
|
1903
|
-
|
|
1968
|
+
consola19.success(`Deploying ${deploy.agent_name} from ${repoRef} (ref ${deploy.ref})`);
|
|
1969
|
+
consola19.info(`Schedules: ${deploy.schedules.map((s) => `${s.task_id}=${s.cron}`).join(", ") || "none"}`);
|
|
1904
1970
|
}
|
|
1905
1971
|
const missing = missingCapabilities(deploy.required_capabilities, secrets);
|
|
1906
1972
|
if (missing.length > 0) {
|
|
@@ -1908,13 +1974,13 @@ var deployAgentCommand = defineCommand20({
|
|
|
1908
1974
|
throw new CliError(`missing required capability secrets: ${missing.join(", ")} (pass via --secret in --json mode)`);
|
|
1909
1975
|
}
|
|
1910
1976
|
for (const env of missing) {
|
|
1911
|
-
const val = await
|
|
1977
|
+
const val = await consola19.prompt(`Secret value for ${env}:`, { type: "text" });
|
|
1912
1978
|
if (typeof val !== "string" || val.length === 0) throw new CliError(`no value provided for ${env} \u2014 aborting`);
|
|
1913
1979
|
secrets[env] = val;
|
|
1914
1980
|
}
|
|
1915
1981
|
}
|
|
1916
1982
|
if (Object.keys(secrets).length > 0) {
|
|
1917
|
-
if (!json)
|
|
1983
|
+
if (!json) consola19.start("Waiting for the agent to come online\u2026");
|
|
1918
1984
|
let online = false;
|
|
1919
1985
|
for (let i = 0; i < 90 && !online; i++) {
|
|
1920
1986
|
const st = await api(
|
|
@@ -1943,11 +2009,11 @@ var deployAgentCommand = defineCommand20({
|
|
|
1943
2009
|
await sleep(3e3);
|
|
1944
2010
|
}
|
|
1945
2011
|
}
|
|
1946
|
-
if (!json)
|
|
2012
|
+
if (!json) consola19.success(`Bound ${env}`);
|
|
1947
2013
|
}
|
|
1948
2014
|
}
|
|
1949
2015
|
if (json) {
|
|
1950
|
-
|
|
2016
|
+
consola19.log(JSON.stringify({
|
|
1951
2017
|
ok: true,
|
|
1952
2018
|
agent_name: deploy.agent_name,
|
|
1953
2019
|
ref: deploy.ref,
|
|
@@ -1956,13 +2022,13 @@ var deployAgentCommand = defineCommand20({
|
|
|
1956
2022
|
bound: Object.keys(secrets)
|
|
1957
2023
|
}));
|
|
1958
2024
|
} else {
|
|
1959
|
-
|
|
2025
|
+
consola19.success(`${deploy.agent_name} deployed. Schedules are live; secrets sealed to the agent.`);
|
|
1960
2026
|
}
|
|
1961
2027
|
}
|
|
1962
2028
|
});
|
|
1963
2029
|
|
|
1964
2030
|
// src/commands/agent/index.ts
|
|
1965
|
-
var agentCommand =
|
|
2031
|
+
var agentCommand = defineCommand22({
|
|
1966
2032
|
meta: {
|
|
1967
2033
|
name: "agent",
|
|
1968
2034
|
description: "Agent Recipe operations (deploy)"
|
|
@@ -1973,15 +2039,15 @@ var agentCommand = defineCommand21({
|
|
|
1973
2039
|
});
|
|
1974
2040
|
|
|
1975
2041
|
// src/commands/agents/index.ts
|
|
1976
|
-
import { defineCommand as
|
|
2042
|
+
import { defineCommand as defineCommand33 } from "citty";
|
|
1977
2043
|
|
|
1978
2044
|
// src/commands/agents/allow.ts
|
|
1979
2045
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
1980
|
-
import { defineCommand as
|
|
1981
|
-
import
|
|
2046
|
+
import { defineCommand as defineCommand23 } from "citty";
|
|
2047
|
+
import consola20 from "consola";
|
|
1982
2048
|
|
|
1983
2049
|
// src/lib/agent-bootstrap.ts
|
|
1984
|
-
import { Buffer as
|
|
2050
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
1985
2051
|
import { createPrivateKey, sign } from "crypto";
|
|
1986
2052
|
import { exchangeWithDelegation } from "@openape/cli-auth";
|
|
1987
2053
|
var AGENT_NAME_REGEX = /^[a-z][a-z0-9-]{0,23}$/;
|
|
@@ -2047,7 +2113,7 @@ function decodeJwtClaims(token) {
|
|
|
2047
2113
|
const part = token.split(".")[1];
|
|
2048
2114
|
if (!part) return null;
|
|
2049
2115
|
const padded = part + "=".repeat((4 - part.length % 4) % 4);
|
|
2050
|
-
const json =
|
|
2116
|
+
const json = Buffer2.from(padded, "base64").toString("utf8");
|
|
2051
2117
|
return JSON.parse(json);
|
|
2052
2118
|
} catch {
|
|
2053
2119
|
return null;
|
|
@@ -2069,7 +2135,7 @@ async function issueAgentToken(input) {
|
|
|
2069
2135
|
throw new Error(`Challenge failed (${challengeResp.status}): ${text}`);
|
|
2070
2136
|
}
|
|
2071
2137
|
const { challenge } = await challengeResp.json();
|
|
2072
|
-
const signature = sign(null,
|
|
2138
|
+
const signature = sign(null, Buffer2.from(challenge), privateKey).toString("base64");
|
|
2073
2139
|
const authenticateUrl = await getAgentAuthenticateEndpoint(input.idp);
|
|
2074
2140
|
const authResp = await fetch(authenticateUrl, {
|
|
2075
2141
|
method: "POST",
|
|
@@ -2102,25 +2168,6 @@ mkdir -p "$HOME_DIR/.claude/hooks"
|
|
|
2102
2168
|
cat > "$HOME_DIR/.claude/settings.json" ${shHeredoc(input.claudeSettingsJson)}
|
|
2103
2169
|
cat > "$HOME_DIR/.claude/hooks/bash-via-ape-shell.sh" ${shHeredoc(input.hookScriptSource)}
|
|
2104
2170
|
chmod 755 "$HOME_DIR/.claude/hooks/bash-via-ape-shell.sh"
|
|
2105
|
-
` : "";
|
|
2106
|
-
const claudeTokenBlock = input.claudeOauthToken ? `
|
|
2107
|
-
mkdir -p "$HOME_DIR/.config/openape"
|
|
2108
|
-
cat > "$HOME_DIR/.config/openape/claude-token.env" ${shHeredoc(`# Auto-generated by 'apes agents spawn'. chmod 600 \u2014 contains a long-lived
|
|
2109
|
-
# Claude Code OAuth token. Rotate by editing this file in place; the
|
|
2110
|
-
# .zshenv / .profile source-lines below will pick it up automatically.
|
|
2111
|
-
export CLAUDE_CODE_OAUTH_TOKEN=${shQuote(input.claudeOauthToken)}
|
|
2112
|
-
`)}
|
|
2113
|
-
SOURCE_LINE='[ -f "$HOME/.config/openape/claude-token.env" ] && . "$HOME/.config/openape/claude-token.env"'
|
|
2114
|
-
for f in "$HOME_DIR/.zshenv" "$HOME_DIR/.profile"; do
|
|
2115
|
-
touch "$f"
|
|
2116
|
-
if ! grep -qF 'config/openape/claude-token.env' "$f" 2>/dev/null; then
|
|
2117
|
-
{
|
|
2118
|
-
echo ''
|
|
2119
|
-
echo '# OpenApe: load Claude Code OAuth token (added by apes agents spawn)'
|
|
2120
|
-
echo "$SOURCE_LINE"
|
|
2121
|
-
} >> "$f"
|
|
2122
|
-
fi
|
|
2123
|
-
done
|
|
2124
2171
|
` : "";
|
|
2125
2172
|
return `#!/bin/bash
|
|
2126
2173
|
set -euo pipefail
|
|
@@ -2163,7 +2210,7 @@ cat > "$HOME_DIR/.ssh/id_ed25519.pub" ${shHeredoc(input.publicKeySshLine)}
|
|
|
2163
2210
|
cat > "$HOME_DIR/.config/apes/auth.json" ${shHeredoc(input.authJson)}
|
|
2164
2211
|
cat > "$HOME_DIR/.config/openape/agent-x25519.key" ${shHeredoc(input.x25519PrivateKey)}
|
|
2165
2212
|
cat > "$HOME_DIR/.config/openape/agent-x25519.key.pub" ${shHeredoc(input.x25519PublicKey)}
|
|
2166
|
-
${claudeBlock}
|
|
2213
|
+
${claudeBlock}
|
|
2167
2214
|
# Per-agent task dir that \`apes agents sync\` writes to (XDG-style on
|
|
2168
2215
|
# Linux; was ~/Library/... on macOS).
|
|
2169
2216
|
mkdir -p "$HOME_DIR/.openape/agent/tasks"
|
|
@@ -2177,9 +2224,6 @@ chmod 644 "$HOME_DIR/.ssh/id_ed25519.pub"
|
|
|
2177
2224
|
chmod 600 "$HOME_DIR/.config/apes/auth.json"
|
|
2178
2225
|
chmod 600 "$HOME_DIR/.config/openape/agent-x25519.key"
|
|
2179
2226
|
chmod 644 "$HOME_DIR/.config/openape/agent-x25519.key.pub"
|
|
2180
|
-
if [ -f "$HOME_DIR/.config/openape/claude-token.env" ]; then
|
|
2181
|
-
chmod 600 "$HOME_DIR/.config/openape/claude-token.env"
|
|
2182
|
-
fi
|
|
2183
2227
|
|
|
2184
2228
|
echo "OK $NAME (linux user) uid=$NEW_UID home=$HOME_DIR"
|
|
2185
2229
|
`;
|
|
@@ -2425,7 +2469,7 @@ function getHostPlatform() {
|
|
|
2425
2469
|
}
|
|
2426
2470
|
|
|
2427
2471
|
// src/commands/agents/allow.ts
|
|
2428
|
-
var allowAgentCommand =
|
|
2472
|
+
var allowAgentCommand = defineCommand23({
|
|
2429
2473
|
meta: {
|
|
2430
2474
|
name: "allow",
|
|
2431
2475
|
description: "Add a peer to the agent's contact-allowlist so the bridge auto-accepts that peer's contact request."
|
|
@@ -2482,9 +2526,9 @@ print("ok")
|
|
|
2482
2526
|
PY
|
|
2483
2527
|
chmod 600 "$F"
|
|
2484
2528
|
`;
|
|
2485
|
-
|
|
2529
|
+
consola20.start(`Adding ${email} to ${agent}'s allowlist\u2026`);
|
|
2486
2530
|
execFileSync6(apes, ["run", "--as", agent, "--wait", "--", "bash", "-c", script], { stdio: "inherit" });
|
|
2487
|
-
|
|
2531
|
+
consola20.success(`${agent} will auto-accept future contact requests from ${email} (within ~30s of next bridge connect).`);
|
|
2488
2532
|
}
|
|
2489
2533
|
});
|
|
2490
2534
|
function shQuote2(s) {
|
|
@@ -2492,9 +2536,9 @@ function shQuote2(s) {
|
|
|
2492
2536
|
}
|
|
2493
2537
|
|
|
2494
2538
|
// src/commands/agents/cleanup-orphans.ts
|
|
2495
|
-
import { defineCommand as
|
|
2496
|
-
import
|
|
2497
|
-
var cleanupOrphansCommand =
|
|
2539
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
2540
|
+
import consola21 from "consola";
|
|
2541
|
+
var cleanupOrphansCommand = defineCommand24({
|
|
2498
2542
|
meta: {
|
|
2499
2543
|
name: "cleanup-orphans",
|
|
2500
2544
|
description: "Report agent-user tombstones. Linux userdel is atomic, so there are normally none."
|
|
@@ -2512,105 +2556,25 @@ var cleanupOrphansCommand = defineCommand23({
|
|
|
2512
2556
|
async run() {
|
|
2513
2557
|
const orphans = getHostPlatform().listOrphanAgentUsers();
|
|
2514
2558
|
if (orphans.length === 0) {
|
|
2515
|
-
|
|
2559
|
+
consola21.success("No agent tombstones \u2014 userdel is clean on Linux.");
|
|
2516
2560
|
return;
|
|
2517
2561
|
}
|
|
2518
|
-
|
|
2562
|
+
consola21.warn(`Found ${orphans.length} unexpected agent tombstone${orphans.length === 1 ? "" : "s"}:`);
|
|
2519
2563
|
for (const o of orphans) {
|
|
2520
2564
|
console.log(` \u2022 ${o.name}${o.uid !== null ? ` (uid=${o.uid})` : ""} \u2014 was ${o.homeDir}`);
|
|
2521
2565
|
}
|
|
2522
|
-
|
|
2566
|
+
consola21.info("Remove each one manually with `userdel -r <name>`.");
|
|
2523
2567
|
}
|
|
2524
2568
|
});
|
|
2525
2569
|
|
|
2526
2570
|
// src/commands/agents/code.ts
|
|
2527
|
-
import { existsSync as
|
|
2528
|
-
import { homedir as homedir5 } from "os";
|
|
2529
|
-
import { join as join4 } from "path";
|
|
2530
|
-
import process3 from "process";
|
|
2531
|
-
import { defineCommand as defineCommand24 } from "citty";
|
|
2532
|
-
import { consola as consola21 } from "consola";
|
|
2533
|
-
import { taskTools, runApeShell, runCodingTask, buildIssueGet, detectForge, createLlmReviewer, createLlmRiskAssessor, resolveMergePolicy } from "@openape/agent-runtime";
|
|
2534
|
-
|
|
2535
|
-
// src/lib/agent-secrets-runtime.ts
|
|
2536
|
-
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync4, watch } from "fs";
|
|
2571
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
2537
2572
|
import { homedir as homedir4 } from "os";
|
|
2538
2573
|
import { join as join3 } from "path";
|
|
2539
|
-
import
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
var X25519_PUBKEY_PATH = `${X25519_KEY_PATH}.pub`;
|
|
2544
|
-
function envNameFromFile(file) {
|
|
2545
|
-
if (!file.endsWith(".blob")) return null;
|
|
2546
|
-
const env = file.slice(0, -".blob".length);
|
|
2547
|
-
return /^[A-Z][A-Z0-9_]*$/.test(env) ? env : null;
|
|
2548
|
-
}
|
|
2549
|
-
function readAgentEncryptionKey(keyPath = X25519_KEY_PATH) {
|
|
2550
|
-
if (!existsSync5(keyPath)) return null;
|
|
2551
|
-
const k = readFileSync4(keyPath, "utf8").trim();
|
|
2552
|
-
return k.length > 0 ? k : null;
|
|
2553
|
-
}
|
|
2554
|
-
function readAgentEncryptionPublicKey(pubPath = X25519_PUBKEY_PATH) {
|
|
2555
|
-
if (!existsSync5(pubPath)) return null;
|
|
2556
|
-
const k = readFileSync4(pubPath, "utf8").trim();
|
|
2557
|
-
return k.length > 0 ? k : null;
|
|
2558
|
-
}
|
|
2559
|
-
function materializeSecrets(opts = {}) {
|
|
2560
|
-
const dir = opts.dir ?? SECRETS_DIR;
|
|
2561
|
-
const env = opts.env ?? process.env;
|
|
2562
|
-
const log = opts.log ?? (() => {
|
|
2563
|
-
});
|
|
2564
|
-
const applied = [];
|
|
2565
|
-
const failed = [];
|
|
2566
|
-
const key = readAgentEncryptionKey(opts.keyPath);
|
|
2567
|
-
const files = key && existsSync5(dir) ? readdirSync(dir) : [];
|
|
2568
|
-
for (const file of files) {
|
|
2569
|
-
const name = envNameFromFile(file);
|
|
2570
|
-
if (!name) continue;
|
|
2571
|
-
try {
|
|
2572
|
-
const box = JSON.parse(readFileSync4(join3(dir, file), "utf8"));
|
|
2573
|
-
env[name] = openString(box, key);
|
|
2574
|
-
applied.push(name);
|
|
2575
|
-
} catch (e) {
|
|
2576
|
-
failed.push(file);
|
|
2577
|
-
log(`secrets: failed to open ${file}: ${e.message}`);
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
const live = new Set(applied);
|
|
2581
|
-
for (const prev of opts.previouslyApplied ?? []) {
|
|
2582
|
-
if (!live.has(prev)) {
|
|
2583
|
-
delete env[prev];
|
|
2584
|
-
log(`secrets: revoked ${prev}`);
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
return { applied, failed };
|
|
2588
|
-
}
|
|
2589
|
-
function startSecretsWatcher(opts = {}) {
|
|
2590
|
-
const dir = opts.dir ?? SECRETS_DIR;
|
|
2591
|
-
const log = opts.log ?? (() => {
|
|
2592
|
-
});
|
|
2593
|
-
let appliedNames = /* @__PURE__ */ new Set();
|
|
2594
|
-
const run = () => {
|
|
2595
|
-
const r = materializeSecrets({ ...opts, previouslyApplied: appliedNames });
|
|
2596
|
-
appliedNames = new Set(r.applied);
|
|
2597
|
-
};
|
|
2598
|
-
run();
|
|
2599
|
-
if (!existsSync5(dir)) return () => {
|
|
2600
|
-
};
|
|
2601
|
-
let timer = null;
|
|
2602
|
-
const watcher = watch(dir, () => {
|
|
2603
|
-
if (timer) clearTimeout(timer);
|
|
2604
|
-
timer = setTimeout(run, 150);
|
|
2605
|
-
});
|
|
2606
|
-
watcher.on("error", (err) => log(`secrets: watcher error: ${err.message}`));
|
|
2607
|
-
return () => {
|
|
2608
|
-
if (timer) clearTimeout(timer);
|
|
2609
|
-
watcher.close();
|
|
2610
|
-
};
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
// src/commands/agents/code.ts
|
|
2574
|
+
import process3 from "process";
|
|
2575
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
2576
|
+
import { consola as consola22 } from "consola";
|
|
2577
|
+
import { taskTools, runApeShell, runCodingTask, buildIssueGet, detectForge, createLlmReviewer, createLlmRiskAssessor, resolveMergePolicy } from "@openape/agent-runtime";
|
|
2614
2578
|
var CliError2 = class extends Error {
|
|
2615
2579
|
};
|
|
2616
2580
|
var CODING_TOOLS = ["file.read", "file.write", "file.edit", "bash", "git.worktree", "verify", "forge.issue.get", "forge.pr.status"];
|
|
@@ -2623,9 +2587,9 @@ var DEFAULT_PERSONA = [
|
|
|
2623
2587
|
].join(" ");
|
|
2624
2588
|
function readLitellmConfig(model) {
|
|
2625
2589
|
const env = {};
|
|
2626
|
-
const envPath =
|
|
2627
|
-
if (
|
|
2628
|
-
for (const raw of
|
|
2590
|
+
const envPath = join3(homedir4(), "litellm", ".env");
|
|
2591
|
+
if (existsSync5(envPath)) {
|
|
2592
|
+
for (const raw of readFileSync4(envPath, "utf8").split("\n")) {
|
|
2629
2593
|
const line = raw.trim();
|
|
2630
2594
|
const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);
|
|
2631
2595
|
if (m) env[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
|
|
@@ -2641,11 +2605,11 @@ function readLitellmConfig(model) {
|
|
|
2641
2605
|
return { apiBase, apiKey, model: model || process3.env.APE_CHAT_BRIDGE_MODEL || "claude-haiku-4-5" };
|
|
2642
2606
|
}
|
|
2643
2607
|
function readPersona(file) {
|
|
2644
|
-
if (file &&
|
|
2645
|
-
const agentJson =
|
|
2646
|
-
if (
|
|
2608
|
+
if (file && existsSync5(file)) return readFileSync4(file, "utf8");
|
|
2609
|
+
const agentJson = join3(homedir4(), ".openape", "agent", "agent.json");
|
|
2610
|
+
if (existsSync5(agentJson)) {
|
|
2647
2611
|
try {
|
|
2648
|
-
const p = JSON.parse(
|
|
2612
|
+
const p = JSON.parse(readFileSync4(agentJson, "utf8"));
|
|
2649
2613
|
if (p.systemPrompt?.trim()) return p.systemPrompt;
|
|
2650
2614
|
} catch {
|
|
2651
2615
|
}
|
|
@@ -2660,7 +2624,7 @@ async function fetchIssue(forge, ref, repo) {
|
|
|
2660
2624
|
const title = j.title ?? j.fields?.["System.Title"] ?? `issue ${ref}`;
|
|
2661
2625
|
return { number, title, body: j.body };
|
|
2662
2626
|
}
|
|
2663
|
-
var codeAgentCommand =
|
|
2627
|
+
var codeAgentCommand = defineCommand25({
|
|
2664
2628
|
meta: {
|
|
2665
2629
|
name: "code",
|
|
2666
2630
|
description: "Run a coding task: issue to worktree to edit to verify to PR (policy-gated merge). The agent never self-merges."
|
|
@@ -2679,7 +2643,7 @@ var codeAgentCommand = defineCommand24({
|
|
|
2679
2643
|
const forge = args.forge || detectForge(repo);
|
|
2680
2644
|
try {
|
|
2681
2645
|
const { applied } = materializeSecrets();
|
|
2682
|
-
if (applied.length > 0)
|
|
2646
|
+
if (applied.length > 0) consola22.info(`Capabilities available: ${applied.join(", ")}`);
|
|
2683
2647
|
} catch {
|
|
2684
2648
|
}
|
|
2685
2649
|
const config = readLitellmConfig(args.model);
|
|
@@ -2695,22 +2659,22 @@ var codeAgentCommand = defineCommand24({
|
|
|
2695
2659
|
resolvePolicy: (worktree) => resolveMergePolicy(worktree),
|
|
2696
2660
|
reviewer: createLlmReviewer(config),
|
|
2697
2661
|
riskAssessor: createLlmRiskAssessor(config),
|
|
2698
|
-
log: (l) =>
|
|
2662
|
+
log: (l) => consola22.info(l),
|
|
2699
2663
|
streamAggregate
|
|
2700
2664
|
};
|
|
2701
2665
|
const refs = [];
|
|
2702
2666
|
if (args["poll-label"]) {
|
|
2703
2667
|
const slug = repo.replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
2704
|
-
const
|
|
2705
|
-
if (
|
|
2706
|
-
throw new CliError2(`gh issue list failed (exit ${
|
|
2668
|
+
const list2 = await runApeShell(`gh issue list --repo ${slug} --label '${args["poll-label"]}' --state open --json number --jq '.[].number'`);
|
|
2669
|
+
if (list2.exit_code !== 0) {
|
|
2670
|
+
throw new CliError2(`gh issue list failed (exit ${list2.exit_code}): ${(list2.stderr || list2.stdout).slice(0, 300)}`);
|
|
2707
2671
|
}
|
|
2708
|
-
if (
|
|
2672
|
+
if (list2.stdout.trim() === "" && /gh auth login|GH_TOKEN/i.test(list2.stderr)) {
|
|
2709
2673
|
throw new CliError2("gh is not authenticated (no GH_TOKEN). The agent's GH_TOKEN capability is not materialized \u2014 bind it via the deploy/secrets flow.");
|
|
2710
2674
|
}
|
|
2711
|
-
refs.push(...
|
|
2675
|
+
refs.push(...list2.stdout.split("\n").map((s) => s.trim()).filter((s) => /^\d+$/.test(s)));
|
|
2712
2676
|
if (refs.length === 0) {
|
|
2713
|
-
|
|
2677
|
+
consola22.info("no open issues with that label");
|
|
2714
2678
|
return;
|
|
2715
2679
|
}
|
|
2716
2680
|
} else if (args.issue) {
|
|
@@ -2720,9 +2684,9 @@ var codeAgentCommand = defineCommand24({
|
|
|
2720
2684
|
}
|
|
2721
2685
|
for (const ref of refs) {
|
|
2722
2686
|
const issue = await fetchIssue(forge, ref, repo);
|
|
2723
|
-
|
|
2687
|
+
consola22.start(`coding issue #${issue.number}: ${issue.title}`);
|
|
2724
2688
|
const result = await runCodingTask({ issue, repo, forge }, deps);
|
|
2725
|
-
|
|
2689
|
+
consola22.box(`#${issue.number} -> ${result.outcome}
|
|
2726
2690
|
PR: ${result.prRef ?? "(none)"}
|
|
2727
2691
|
${result.reason}`);
|
|
2728
2692
|
}
|
|
@@ -2730,27 +2694,27 @@ ${result.reason}`);
|
|
|
2730
2694
|
});
|
|
2731
2695
|
|
|
2732
2696
|
// src/commands/agents/destroy.ts
|
|
2733
|
-
import { defineCommand as
|
|
2734
|
-
import
|
|
2697
|
+
import { defineCommand as defineCommand26 } from "citty";
|
|
2698
|
+
import consola23 from "consola";
|
|
2735
2699
|
|
|
2736
2700
|
// src/lib/nest-registry.ts
|
|
2737
|
-
import { existsSync as
|
|
2738
|
-
import { homedir as
|
|
2739
|
-
import { join as
|
|
2701
|
+
import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2702
|
+
import { homedir as homedir5 } from "os";
|
|
2703
|
+
import { join as join4 } from "path";
|
|
2740
2704
|
function resolveRegistryPath() {
|
|
2741
2705
|
if (process.env.OPENAPE_NEST_REGISTRY_PATH) return process.env.OPENAPE_NEST_REGISTRY_PATH;
|
|
2742
|
-
if (
|
|
2743
|
-
if (
|
|
2744
|
-
return
|
|
2706
|
+
if (existsSync6("/var/openape/nest/agents.json")) return "/var/openape/nest/agents.json";
|
|
2707
|
+
if (existsSync6("/var/openape/nest")) return "/var/openape/nest/agents.json";
|
|
2708
|
+
return join4(homedir5(), ".openape", "nest", "agents.json");
|
|
2745
2709
|
}
|
|
2746
2710
|
function emptyRegistry() {
|
|
2747
2711
|
return { version: 1, agents: [] };
|
|
2748
2712
|
}
|
|
2749
2713
|
function readNestRegistry() {
|
|
2750
2714
|
const path2 = resolveRegistryPath();
|
|
2751
|
-
if (!
|
|
2715
|
+
if (!existsSync6(path2)) return emptyRegistry();
|
|
2752
2716
|
try {
|
|
2753
|
-
const parsed = JSON.parse(
|
|
2717
|
+
const parsed = JSON.parse(readFileSync5(path2, "utf8"));
|
|
2754
2718
|
if (parsed?.version !== 1 || !Array.isArray(parsed.agents)) return emptyRegistry();
|
|
2755
2719
|
return parsed;
|
|
2756
2720
|
} catch {
|
|
@@ -2784,7 +2748,7 @@ function removeNestAgent(name) {
|
|
|
2784
2748
|
}
|
|
2785
2749
|
|
|
2786
2750
|
// src/commands/agents/destroy.ts
|
|
2787
|
-
var destroyAgentCommand =
|
|
2751
|
+
var destroyAgentCommand = defineCommand26({
|
|
2788
2752
|
meta: {
|
|
2789
2753
|
name: "destroy",
|
|
2790
2754
|
description: "Tear down an agent: remove the OS user, hard-delete IdP agent, drop all SSH keys"
|
|
@@ -2829,7 +2793,7 @@ var destroyAgentCommand = defineCommand25({
|
|
|
2829
2793
|
const osUser = getHostPlatform().lookupAgentUser(name);
|
|
2830
2794
|
const osUserExists = !args["keep-os-user"] && osUser !== null;
|
|
2831
2795
|
if (!idpExists && !osUserExists) {
|
|
2832
|
-
|
|
2796
|
+
consola23.info(`Nothing to destroy: no IdP agent and no OS user for "${name}".`);
|
|
2833
2797
|
return;
|
|
2834
2798
|
}
|
|
2835
2799
|
if (!args.force) {
|
|
@@ -2841,14 +2805,14 @@ var destroyAgentCommand = defineCommand25({
|
|
|
2841
2805
|
if (idpExists) {
|
|
2842
2806
|
consequences.push(args.soft ? `\u2022 Deactivate IdP agent ${idpAgent.email} (PATCH isActive=false)` : `\u2022 Hard-delete IdP agent ${idpAgent.email} and all its SSH keys`);
|
|
2843
2807
|
}
|
|
2844
|
-
|
|
2808
|
+
consola23.warn(`About to destroy "${name}":
|
|
2845
2809
|
${consequences.join("\n")}`);
|
|
2846
2810
|
if (!process.stdin.isTTY) {
|
|
2847
2811
|
throw new CliError(
|
|
2848
2812
|
"No TTY available for the interactive confirmation. Re-run with --force to skip the prompt (this is the same flag CI uses)."
|
|
2849
2813
|
);
|
|
2850
2814
|
}
|
|
2851
|
-
const confirmed = await
|
|
2815
|
+
const confirmed = await consola23.prompt("Proceed?", { type: "confirm", initial: false });
|
|
2852
2816
|
if (typeof confirmed === "symbol" || !confirmed) {
|
|
2853
2817
|
throw new CliExit(0);
|
|
2854
2818
|
}
|
|
@@ -2857,16 +2821,16 @@ ${consequences.join("\n")}`);
|
|
|
2857
2821
|
const id = encodeURIComponent(idpAgent.email);
|
|
2858
2822
|
if (args.soft) {
|
|
2859
2823
|
await apiFetch(`/api/my-agents/${id}`, { method: "PATCH", body: { isActive: false }, idp });
|
|
2860
|
-
|
|
2824
|
+
consola23.success(`Deactivated IdP agent ${idpAgent.email}`);
|
|
2861
2825
|
} else {
|
|
2862
2826
|
await apiFetch(`/api/my-agents/${id}`, { method: "DELETE", idp });
|
|
2863
|
-
|
|
2827
|
+
consola23.success(`Deleted IdP agent ${idpAgent.email}`);
|
|
2864
2828
|
}
|
|
2865
2829
|
} else {
|
|
2866
|
-
|
|
2830
|
+
consola23.info("No IdP agent to remove (skipped).");
|
|
2867
2831
|
}
|
|
2868
2832
|
if (osUserExists) {
|
|
2869
|
-
|
|
2833
|
+
consola23.start(`Removing OS user ${name}\u2026`);
|
|
2870
2834
|
await getHostPlatform().runPrivilegedBash(
|
|
2871
2835
|
`#!/bin/bash
|
|
2872
2836
|
set -euo pipefail
|
|
@@ -2876,21 +2840,21 @@ if getent passwd ${JSON.stringify(name)} >/dev/null 2>&1; then
|
|
|
2876
2840
|
fi
|
|
2877
2841
|
`
|
|
2878
2842
|
);
|
|
2879
|
-
|
|
2843
|
+
consola23.success(`Removed OS user ${name}.`);
|
|
2880
2844
|
}
|
|
2881
2845
|
try {
|
|
2882
2846
|
removeNestAgent(name);
|
|
2883
2847
|
} catch (err) {
|
|
2884
|
-
|
|
2848
|
+
consola23.warn(`Could not update nest registry: ${err instanceof Error ? err.message : String(err)}`);
|
|
2885
2849
|
}
|
|
2886
|
-
|
|
2850
|
+
consola23.success(`Destroyed ${name}.`);
|
|
2887
2851
|
}
|
|
2888
2852
|
});
|
|
2889
2853
|
|
|
2890
2854
|
// src/commands/agents/list.ts
|
|
2891
|
-
import { defineCommand as
|
|
2892
|
-
import
|
|
2893
|
-
var listAgentsCommand =
|
|
2855
|
+
import { defineCommand as defineCommand27 } from "citty";
|
|
2856
|
+
import consola24 from "consola";
|
|
2857
|
+
var listAgentsCommand = defineCommand27({
|
|
2894
2858
|
meta: {
|
|
2895
2859
|
name: "list",
|
|
2896
2860
|
description: "List agents owned by the current user, with local OS-user status"
|
|
@@ -2942,7 +2906,7 @@ var listAgentsCommand = defineCommand26({
|
|
|
2942
2906
|
return;
|
|
2943
2907
|
}
|
|
2944
2908
|
if (rows.length === 0) {
|
|
2945
|
-
|
|
2909
|
+
consola24.info(args["include-inactive"] ? "No agents found." : "No active agents found. Use --include-inactive to show deactivated.");
|
|
2946
2910
|
return;
|
|
2947
2911
|
}
|
|
2948
2912
|
const nameW = Math.max(4, ...rows.map((r) => r.name.length));
|
|
@@ -2960,10 +2924,10 @@ var listAgentsCommand = defineCommand26({
|
|
|
2960
2924
|
});
|
|
2961
2925
|
|
|
2962
2926
|
// src/commands/agents/register.ts
|
|
2963
|
-
import { existsSync as
|
|
2964
|
-
import { defineCommand as
|
|
2965
|
-
import
|
|
2966
|
-
var registerAgentCommand =
|
|
2927
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
2928
|
+
import { defineCommand as defineCommand28 } from "citty";
|
|
2929
|
+
import consola25 from "consola";
|
|
2930
|
+
var registerAgentCommand = defineCommand28({
|
|
2967
2931
|
meta: {
|
|
2968
2932
|
name: "register",
|
|
2969
2933
|
description: "Register an agent at the IdP using a supplied public key"
|
|
@@ -3008,10 +2972,10 @@ var registerAgentCommand = defineCommand27({
|
|
|
3008
2972
|
throw new CliError("Pass either --public-key or --public-key-file, not both.");
|
|
3009
2973
|
}
|
|
3010
2974
|
if (!publicKey && keyFile) {
|
|
3011
|
-
if (!
|
|
2975
|
+
if (!existsSync7(keyFile)) {
|
|
3012
2976
|
throw new CliError(`Public-key file not found: ${keyFile}`);
|
|
3013
2977
|
}
|
|
3014
|
-
publicKey =
|
|
2978
|
+
publicKey = readFileSync6(keyFile, "utf-8").trim();
|
|
3015
2979
|
}
|
|
3016
2980
|
if (!publicKey) {
|
|
3017
2981
|
throw new CliError('Provide --public-key "<ssh-ed25519 line>" or --public-key-file <path>.');
|
|
@@ -3033,7 +2997,7 @@ var registerAgentCommand = defineCommand27({
|
|
|
3033
2997
|
`);
|
|
3034
2998
|
return;
|
|
3035
2999
|
}
|
|
3036
|
-
|
|
3000
|
+
consola25.success("Agent registered.");
|
|
3037
3001
|
console.log(` Name: ${result.name}`);
|
|
3038
3002
|
console.log(` Email: ${result.email}`);
|
|
3039
3003
|
console.log(` IdP: ${idp}`);
|
|
@@ -3046,19 +3010,19 @@ var registerAgentCommand = defineCommand27({
|
|
|
3046
3010
|
});
|
|
3047
3011
|
|
|
3048
3012
|
// src/commands/agents/run.ts
|
|
3049
|
-
import { existsSync as
|
|
3050
|
-
import { homedir as
|
|
3051
|
-
import { join as
|
|
3052
|
-
import { defineCommand as
|
|
3053
|
-
import
|
|
3013
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
3014
|
+
import { homedir as homedir6 } from "os";
|
|
3015
|
+
import { join as join5 } from "path";
|
|
3016
|
+
import { defineCommand as defineCommand29 } from "citty";
|
|
3017
|
+
import consola26 from "consola";
|
|
3054
3018
|
import { taskTools as taskTools2, runLoop } from "@openape/agent-runtime";
|
|
3055
|
-
var AUTH_PATH =
|
|
3056
|
-
var TASK_CACHE_DIR =
|
|
3019
|
+
var AUTH_PATH = join5(homedir6(), ".config", "apes", "auth.json");
|
|
3020
|
+
var TASK_CACHE_DIR = join5(homedir6(), ".openape", "agent", "tasks");
|
|
3057
3021
|
function readAuth() {
|
|
3058
|
-
if (!
|
|
3022
|
+
if (!existsSync8(AUTH_PATH)) {
|
|
3059
3023
|
throw new CliError(`No agent auth found at ${AUTH_PATH}. Run \`apes agents spawn <name>\` first.`);
|
|
3060
3024
|
}
|
|
3061
|
-
const parsed = JSON.parse(
|
|
3025
|
+
const parsed = JSON.parse(readFileSync7(AUTH_PATH, "utf8"));
|
|
3062
3026
|
if (!parsed.access_token) throw new CliError("auth.json missing access_token");
|
|
3063
3027
|
return parsed;
|
|
3064
3028
|
}
|
|
@@ -3073,7 +3037,7 @@ async function postRunResultToChat(opts) {
|
|
|
3073
3037
|
const ownerLower = opts.ownerEmail.toLowerCase();
|
|
3074
3038
|
const ownerRow = contacts.find((c) => c.peerEmail.toLowerCase() === ownerLower && c.connected && c.roomId);
|
|
3075
3039
|
if (!ownerRow?.roomId) {
|
|
3076
|
-
|
|
3040
|
+
consola26.info("chat DM skipped \u2014 no active room with owner (accept the contact request in chat to enable)");
|
|
3077
3041
|
return;
|
|
3078
3042
|
}
|
|
3079
3043
|
const prefix = opts.status === "ok" ? "\u2705" : "\u274C";
|
|
@@ -3087,33 +3051,33 @@ ${msg}`.slice(0, 9e3);
|
|
|
3087
3051
|
body: JSON.stringify({ body })
|
|
3088
3052
|
});
|
|
3089
3053
|
if (!postRes.ok) {
|
|
3090
|
-
|
|
3054
|
+
consola26.warn(`chat DM post failed: ${postRes.status}`);
|
|
3091
3055
|
}
|
|
3092
3056
|
} catch (err) {
|
|
3093
|
-
|
|
3057
|
+
consola26.warn(`chat DM error: ${err.message}`);
|
|
3094
3058
|
}
|
|
3095
3059
|
}
|
|
3096
3060
|
function readTaskSpec(taskId) {
|
|
3097
|
-
const path2 =
|
|
3098
|
-
if (!
|
|
3061
|
+
const path2 = join5(TASK_CACHE_DIR, `${taskId}.json`);
|
|
3062
|
+
if (!existsSync8(path2)) {
|
|
3099
3063
|
throw new CliError(`No cached task spec at ${path2}. Run \`apes agents sync\` first to pull the task list from troop.`);
|
|
3100
3064
|
}
|
|
3101
|
-
return JSON.parse(
|
|
3065
|
+
return JSON.parse(readFileSync7(path2, "utf8"));
|
|
3102
3066
|
}
|
|
3103
|
-
var AGENT_CONFIG_PATH =
|
|
3067
|
+
var AGENT_CONFIG_PATH = join5(homedir6(), ".openape", "agent", "agent.json");
|
|
3104
3068
|
function readAgentConfig() {
|
|
3105
|
-
if (!
|
|
3069
|
+
if (!existsSync8(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
|
|
3106
3070
|
try {
|
|
3107
|
-
return JSON.parse(
|
|
3071
|
+
return JSON.parse(readFileSync7(AGENT_CONFIG_PATH, "utf8"));
|
|
3108
3072
|
} catch {
|
|
3109
3073
|
return { systemPrompt: "" };
|
|
3110
3074
|
}
|
|
3111
3075
|
}
|
|
3112
3076
|
function readLitellmConfig2(model) {
|
|
3113
|
-
const envPath =
|
|
3077
|
+
const envPath = join5(homedir6(), "litellm", ".env");
|
|
3114
3078
|
const env = {};
|
|
3115
|
-
if (
|
|
3116
|
-
for (const line of
|
|
3079
|
+
if (existsSync8(envPath)) {
|
|
3080
|
+
for (const line of readFileSync7(envPath, "utf8").split(/\r?\n/)) {
|
|
3117
3081
|
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
3118
3082
|
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
3119
3083
|
}
|
|
@@ -3128,7 +3092,7 @@ function readLitellmConfig2(model) {
|
|
|
3128
3092
|
}
|
|
3129
3093
|
return { apiBase, apiKey, model: model || "claude-haiku-4-5" };
|
|
3130
3094
|
}
|
|
3131
|
-
var runAgentCommand =
|
|
3095
|
+
var runAgentCommand = defineCommand29({
|
|
3132
3096
|
meta: {
|
|
3133
3097
|
name: "run",
|
|
3134
3098
|
description: "Execute one task (typically launchd-invoked). Reports the run record to troop."
|
|
@@ -3151,7 +3115,7 @@ var runAgentCommand = defineCommand28({
|
|
|
3151
3115
|
async run({ args }) {
|
|
3152
3116
|
const taskId = args["task-id"];
|
|
3153
3117
|
const auth = readAuth();
|
|
3154
|
-
materializeSecrets({ log: (l) =>
|
|
3118
|
+
materializeSecrets({ log: (l) => consola26.info(l) });
|
|
3155
3119
|
const spec = readTaskSpec(taskId);
|
|
3156
3120
|
const agentCfg = readAgentConfig();
|
|
3157
3121
|
const config = readLitellmConfig2(args.model);
|
|
@@ -3163,7 +3127,7 @@ var runAgentCommand = defineCommand28({
|
|
|
3163
3127
|
}
|
|
3164
3128
|
const troop = new TroopClient(resolveTroopUrl(args["troop-url"]), auth.access_token);
|
|
3165
3129
|
const { id: runId } = await troop.startRun(taskId);
|
|
3166
|
-
|
|
3130
|
+
consola26.info(`Run ${runId} started for task ${taskId}`);
|
|
3167
3131
|
try {
|
|
3168
3132
|
const result = await runLoop({
|
|
3169
3133
|
config,
|
|
@@ -3182,7 +3146,7 @@ var runAgentCommand = defineCommand28({
|
|
|
3182
3146
|
step_count: result.stepCount,
|
|
3183
3147
|
trace: result.trace
|
|
3184
3148
|
});
|
|
3185
|
-
|
|
3149
|
+
consola26.success(`Run ${runId} ${result.status} (${result.stepCount} steps)`);
|
|
3186
3150
|
if (auth.owner_email) {
|
|
3187
3151
|
await postRunResultToChat({
|
|
3188
3152
|
authToken: auth.access_token,
|
|
@@ -3219,18 +3183,18 @@ var runAgentCommand = defineCommand28({
|
|
|
3219
3183
|
});
|
|
3220
3184
|
|
|
3221
3185
|
// src/commands/agents/serve.ts
|
|
3222
|
-
import { existsSync as
|
|
3223
|
-
import { homedir as
|
|
3224
|
-
import { join as
|
|
3186
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
3187
|
+
import { homedir as homedir7 } from "os";
|
|
3188
|
+
import { join as join6 } from "path";
|
|
3225
3189
|
import { createInterface } from "readline";
|
|
3226
|
-
import { defineCommand as
|
|
3190
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
3227
3191
|
import { taskTools as taskTools3, runLoop as runLoop2, RpcSessionMap } from "@openape/agent-runtime";
|
|
3228
|
-
var AUTH_PATH2 =
|
|
3192
|
+
var AUTH_PATH2 = join6(homedir7(), ".config", "apes", "auth.json");
|
|
3229
3193
|
function readLitellmConfig3(model) {
|
|
3230
|
-
const envPath =
|
|
3194
|
+
const envPath = join6(homedir7(), "litellm", ".env");
|
|
3231
3195
|
const env = {};
|
|
3232
|
-
if (
|
|
3233
|
-
for (const line of
|
|
3196
|
+
if (existsSync9(envPath)) {
|
|
3197
|
+
for (const line of readFileSync8(envPath, "utf8").split(/\r?\n/)) {
|
|
3234
3198
|
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
3235
3199
|
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
3236
3200
|
}
|
|
@@ -3247,7 +3211,7 @@ function emit(event) {
|
|
|
3247
3211
|
process.stdout.write(`${JSON.stringify(event)}
|
|
3248
3212
|
`);
|
|
3249
3213
|
}
|
|
3250
|
-
var serveAgentCommand =
|
|
3214
|
+
var serveAgentCommand = defineCommand30({
|
|
3251
3215
|
meta: {
|
|
3252
3216
|
name: "serve",
|
|
3253
3217
|
description: "Long-running stdio RPC server for chat-bridge subprocess use."
|
|
@@ -3262,9 +3226,9 @@ var serveAgentCommand = defineCommand29({
|
|
|
3262
3226
|
if (!args.rpc) {
|
|
3263
3227
|
throw new CliError("apes agents serve currently only supports --rpc mode");
|
|
3264
3228
|
}
|
|
3265
|
-
if (
|
|
3229
|
+
if (existsSync9(AUTH_PATH2)) {
|
|
3266
3230
|
try {
|
|
3267
|
-
JSON.parse(
|
|
3231
|
+
JSON.parse(readFileSync8(AUTH_PATH2, "utf8"));
|
|
3268
3232
|
} catch {
|
|
3269
3233
|
}
|
|
3270
3234
|
}
|
|
@@ -3344,50 +3308,50 @@ async function handleInbound(msg, sessions) {
|
|
|
3344
3308
|
}
|
|
3345
3309
|
|
|
3346
3310
|
// src/commands/agents/spawn.ts
|
|
3347
|
-
import { defineCommand as
|
|
3348
|
-
import
|
|
3311
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
3312
|
+
import consola27 from "consola";
|
|
3349
3313
|
|
|
3350
3314
|
// src/lib/keygen.ts
|
|
3351
|
-
import { Buffer as
|
|
3352
|
-
import { existsSync as
|
|
3315
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
3316
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
3353
3317
|
import { generateKeyPairSync } from "crypto";
|
|
3354
|
-
import { homedir as
|
|
3318
|
+
import { homedir as homedir8 } from "os";
|
|
3355
3319
|
import { dirname, resolve as resolve2 } from "path";
|
|
3356
3320
|
import { generateX25519KeyPair } from "@openape/core";
|
|
3357
3321
|
function resolveKeyPath(p) {
|
|
3358
|
-
return resolve2(p.replace(/^~/,
|
|
3322
|
+
return resolve2(p.replace(/^~/, homedir8()));
|
|
3359
3323
|
}
|
|
3360
3324
|
function buildSshEd25519Line(rawPub) {
|
|
3361
3325
|
const keyTypeStr = "ssh-ed25519";
|
|
3362
|
-
const keyTypeLen =
|
|
3326
|
+
const keyTypeLen = Buffer3.alloc(4);
|
|
3363
3327
|
keyTypeLen.writeUInt32BE(keyTypeStr.length);
|
|
3364
|
-
const pubKeyLen =
|
|
3328
|
+
const pubKeyLen = Buffer3.alloc(4);
|
|
3365
3329
|
pubKeyLen.writeUInt32BE(rawPub.length);
|
|
3366
|
-
const blob =
|
|
3330
|
+
const blob = Buffer3.concat([keyTypeLen, Buffer3.from(keyTypeStr), pubKeyLen, rawPub]);
|
|
3367
3331
|
return `ssh-ed25519 ${blob.toString("base64")}`;
|
|
3368
3332
|
}
|
|
3369
3333
|
function readPublicKey(keyPath) {
|
|
3370
3334
|
const pubPath = `${keyPath}.pub`;
|
|
3371
|
-
if (
|
|
3372
|
-
return
|
|
3335
|
+
if (existsSync10(pubPath)) {
|
|
3336
|
+
return readFileSync9(pubPath, "utf-8").trim();
|
|
3373
3337
|
}
|
|
3374
|
-
const keyContent =
|
|
3338
|
+
const keyContent = readFileSync9(keyPath, "utf-8");
|
|
3375
3339
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
3376
3340
|
const jwk = privateKey.export({ format: "jwk" });
|
|
3377
|
-
const pubBytes =
|
|
3341
|
+
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
3378
3342
|
return buildSshEd25519Line(pubBytes);
|
|
3379
3343
|
}
|
|
3380
3344
|
function generateAndSaveKey(keyPath) {
|
|
3381
3345
|
const resolved = resolveKeyPath(keyPath);
|
|
3382
3346
|
const dir = dirname(resolved);
|
|
3383
|
-
if (!
|
|
3347
|
+
if (!existsSync10(dir)) {
|
|
3384
3348
|
mkdirSync2(dir, { recursive: true });
|
|
3385
3349
|
}
|
|
3386
3350
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
3387
3351
|
const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
|
|
3388
3352
|
writeFileSync4(resolved, privatePem, { mode: 384 });
|
|
3389
3353
|
const jwk = publicKey.export({ format: "jwk" });
|
|
3390
|
-
const pubBytes =
|
|
3354
|
+
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
3391
3355
|
const pubKeyStr = buildSshEd25519Line(pubBytes);
|
|
3392
3356
|
writeFileSync4(`${resolved}.pub`, `${pubKeyStr}
|
|
3393
3357
|
`, { mode: 420 });
|
|
@@ -3397,7 +3361,7 @@ function generateKeyPairInMemory() {
|
|
|
3397
3361
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
3398
3362
|
const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
|
|
3399
3363
|
const jwk = publicKey.export({ format: "jwk" });
|
|
3400
|
-
const pubBytes =
|
|
3364
|
+
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
3401
3365
|
const enc = generateX25519KeyPair();
|
|
3402
3366
|
return {
|
|
3403
3367
|
privatePem,
|
|
@@ -3416,7 +3380,7 @@ function readUidOrNull(name) {
|
|
|
3416
3380
|
return null;
|
|
3417
3381
|
}
|
|
3418
3382
|
}
|
|
3419
|
-
var spawnAgentCommand =
|
|
3383
|
+
var spawnAgentCommand = defineCommand31({
|
|
3420
3384
|
meta: {
|
|
3421
3385
|
name: "spawn",
|
|
3422
3386
|
description: "Provision a local Linux agent end-to-end (OS user, keypair, IdP agent, Claude hook)"
|
|
@@ -3435,14 +3399,6 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3435
3399
|
type: "boolean",
|
|
3436
3400
|
description: "Skip writing ~/.claude/settings.json + the Bash-rewrite hook"
|
|
3437
3401
|
},
|
|
3438
|
-
"claude-token": {
|
|
3439
|
-
type: "string",
|
|
3440
|
-
description: "Claude Code OAuth token (sk-ant-oat01-\u2026) from `claude setup-token`. Visible to ps \u2014 prefer --claude-token-stdin in scripts."
|
|
3441
|
-
},
|
|
3442
|
-
"claude-token-stdin": {
|
|
3443
|
-
type: "boolean",
|
|
3444
|
-
description: "Read the Claude Code OAuth token from stdin (paranoid form of --claude-token)."
|
|
3445
|
-
},
|
|
3446
3402
|
"no-bridge": {
|
|
3447
3403
|
type: "boolean",
|
|
3448
3404
|
description: "Skip the ape-agent runtime install. Default behaviour installs the runtime so the agent answers chat.openape.ai messages (reads LITELLM_API_KEY/BASE_URL from ~/litellm/.env; override via --bridge-key / --bridge-base-url). Use --no-bridge for headless / CI / IdP-only account provisioning where the agent will not run a chat loop."
|
|
@@ -3458,6 +3414,22 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3458
3414
|
"bridge-model": {
|
|
3459
3415
|
type: "string",
|
|
3460
3416
|
description: "Model the bridge sends in chat-completion requests (default: claude-haiku-4-5). Override when fronting a proxy that doesn't route the default \u2014 e.g. ChatGPT-only proxy needs `gpt-5.4`."
|
|
3417
|
+
},
|
|
3418
|
+
"bridge-reasoning-effort": {
|
|
3419
|
+
type: "string",
|
|
3420
|
+
description: "Reasoning/thinking depth for gpt-5.x (minimal|low|medium|high). Tier compute by task difficulty on the same model \u2014 quick-win=low, research=high."
|
|
3421
|
+
},
|
|
3422
|
+
"kind": {
|
|
3423
|
+
type: "string",
|
|
3424
|
+
description: `Agent kind: "user" (default, connects to troop-chat) or "service" (polls an SP backend's task queue and runs each task through the LLM). With "service", --serves is required.`
|
|
3425
|
+
},
|
|
3426
|
+
"serves": {
|
|
3427
|
+
type: "string",
|
|
3428
|
+
description: "For --kind service: base URL of the SP backend this agent serves (e.g. https://zaz.delta-mind.at or http://127.0.0.1:3013). The agent pulls GetNextTask / posts ResolveTask there."
|
|
3429
|
+
},
|
|
3430
|
+
"poll-interval": {
|
|
3431
|
+
type: "string",
|
|
3432
|
+
description: "For --kind service: idle poll interval in ms (default 2000)."
|
|
3461
3433
|
}
|
|
3462
3434
|
},
|
|
3463
3435
|
async run({ args }) {
|
|
@@ -3467,6 +3439,13 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3467
3439
|
`Invalid agent name "${name}". Must match /^[a-z][a-z0-9-]{0,23}$/ \u2014 lowercase letters, digits and hyphens, 1\u201324 chars, must start with a letter.`
|
|
3468
3440
|
);
|
|
3469
3441
|
}
|
|
3442
|
+
if (args.kind != null && args.kind !== "user" && args.kind !== "service")
|
|
3443
|
+
throw new CliError(`Invalid --kind "${String(args.kind)}". Must be "user" or "service".`);
|
|
3444
|
+
const isService = args.kind === "service";
|
|
3445
|
+
const servesUrl = typeof args.serves === "string" ? args.serves.replace(/\/$/, "") : void 0;
|
|
3446
|
+
if (isService && !servesUrl)
|
|
3447
|
+
throw new CliError("--kind service requires --serves <SP base URL> (e.g. https://zaz.delta-mind.at).");
|
|
3448
|
+
const pollMs = typeof args["poll-interval"] === "string" ? Number.parseInt(args["poll-interval"], 10) : void 0;
|
|
3470
3449
|
const auth = loadAuth();
|
|
3471
3450
|
if (!auth) {
|
|
3472
3451
|
throw new CliError("Not authenticated. Run `apes login` first.");
|
|
@@ -3483,12 +3462,12 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3483
3462
|
throw new CliError(`OS user "${existing.name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
|
|
3484
3463
|
}
|
|
3485
3464
|
const homeDir = `/var/lib/openape/homes/${osUsername}`;
|
|
3486
|
-
|
|
3465
|
+
consola27.start(`Generating keypair for ${name}\u2026`);
|
|
3487
3466
|
const { privatePem, publicSshLine, x25519PrivateKey, x25519PublicKey } = generateKeyPairInMemory();
|
|
3488
|
-
|
|
3467
|
+
consola27.start(`Registering agent at ${idp}\u2026`);
|
|
3489
3468
|
const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
|
|
3490
|
-
|
|
3491
|
-
|
|
3469
|
+
consola27.success(`Registered as ${registration.email}`);
|
|
3470
|
+
consola27.start("Issuing agent access token\u2026");
|
|
3492
3471
|
const { token, expiresIn } = await issueAgentToken({
|
|
3493
3472
|
idp,
|
|
3494
3473
|
agentEmail: registration.email,
|
|
@@ -3511,10 +3490,6 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3511
3490
|
ownerEmail: registration.owner
|
|
3512
3491
|
});
|
|
3513
3492
|
const includeClaudeHook = !args["no-claude-hook"];
|
|
3514
|
-
const claudeOauthToken = await resolveClaudeToken({
|
|
3515
|
-
flag: typeof args["claude-token"] === "string" ? args["claude-token"] : void 0,
|
|
3516
|
-
fromStdin: !!args["claude-token-stdin"]
|
|
3517
|
-
});
|
|
3518
3493
|
const withBridge = !args["no-bridge"];
|
|
3519
3494
|
const script = buildSpawnSetupScript({
|
|
3520
3495
|
name,
|
|
@@ -3526,12 +3501,11 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3526
3501
|
x25519PublicKey,
|
|
3527
3502
|
authJson,
|
|
3528
3503
|
claudeSettingsJson: includeClaudeHook ? CLAUDE_SETTINGS_JSON : null,
|
|
3529
|
-
hookScriptSource: includeClaudeHook ? BASH_VIA_APE_SHELL_HOOK_SOURCE : null
|
|
3530
|
-
claudeOauthToken
|
|
3504
|
+
hookScriptSource: includeClaudeHook ? BASH_VIA_APE_SHELL_HOOK_SOURCE : null
|
|
3531
3505
|
});
|
|
3532
|
-
|
|
3506
|
+
consola27.start("Running privileged setup\u2026");
|
|
3533
3507
|
if (process.getuid?.() !== 0) {
|
|
3534
|
-
|
|
3508
|
+
consola27.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
|
|
3535
3509
|
}
|
|
3536
3510
|
await platform.runPrivilegedBash(script);
|
|
3537
3511
|
try {
|
|
@@ -3542,57 +3516,112 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3542
3516
|
home: homeDir,
|
|
3543
3517
|
email: registration.email,
|
|
3544
3518
|
registeredAt: Math.floor(Date.now() / 1e3),
|
|
3545
|
-
|
|
3519
|
+
kind: isService ? "service" : void 0,
|
|
3520
|
+
service: isService && servesUrl ? { spBaseUrl: servesUrl, pollIntervalMs: pollMs != null && Number.isFinite(pollMs) && pollMs > 0 ? pollMs : void 0 } : void 0,
|
|
3521
|
+
// Service agents also carry bridge config — it's the LLM endpoint the
|
|
3522
|
+
// worker forwards to (baseUrl/key/model from --bridge-*).
|
|
3523
|
+
bridge: withBridge || isService ? {
|
|
3546
3524
|
baseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0,
|
|
3547
3525
|
apiKey: typeof args["bridge-key"] === "string" ? args["bridge-key"] : void 0,
|
|
3548
|
-
model: typeof args["bridge-model"] === "string" ? args["bridge-model"] : void 0
|
|
3526
|
+
model: typeof args["bridge-model"] === "string" ? args["bridge-model"] : void 0,
|
|
3527
|
+
reasoningEffort: typeof args["bridge-reasoning-effort"] === "string" ? args["bridge-reasoning-effort"] : void 0
|
|
3549
3528
|
} : void 0
|
|
3550
3529
|
});
|
|
3551
3530
|
} catch (err) {
|
|
3552
|
-
|
|
3531
|
+
consola27.warn(`Could not write to nest registry: ${err instanceof Error ? err.message : String(err)}`);
|
|
3553
3532
|
}
|
|
3554
|
-
|
|
3555
|
-
|
|
3533
|
+
consola27.success(`Agent ${name} spawned.`);
|
|
3534
|
+
consola27.info(`\u{1F517} Troop: https://troop.openape.ai/agents/${name}`);
|
|
3556
3535
|
if (withBridge) {
|
|
3557
|
-
|
|
3558
|
-
|
|
3536
|
+
consola27.info(`On first boot, the bridge will send you a contact request from ${registration.email}.`);
|
|
3537
|
+
consola27.info("Open chat.openape.ai and accept it to start chatting with the agent.");
|
|
3559
3538
|
}
|
|
3560
3539
|
console.log("");
|
|
3561
3540
|
console.log("Run as the agent with:");
|
|
3562
3541
|
console.log(` apes run --as ${name} -- claude --session-name ${name} --dangerously-skip-permissions`);
|
|
3563
3542
|
}
|
|
3564
3543
|
});
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3544
|
+
|
|
3545
|
+
// src/commands/agents/sync.ts
|
|
3546
|
+
import { chownSync, existsSync as existsSync12, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync11, rmSync as rmSync3, statSync, writeFileSync as writeFileSync6 } from "fs";
|
|
3547
|
+
import { homedir as homedir9 } from "os";
|
|
3548
|
+
import { join as join8 } from "path";
|
|
3549
|
+
import { refreshAgentToken } from "@openape/cli-auth";
|
|
3550
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
3551
|
+
import consola29 from "consola";
|
|
3552
|
+
|
|
3553
|
+
// src/lib/recipe-checkout.ts
|
|
3554
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
3555
|
+
import { cpSync, existsSync as existsSync11, readFileSync as readFileSync10, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
3556
|
+
import { join as join7, resolve as resolve3, sep } from "path";
|
|
3557
|
+
import consola28 from "consola";
|
|
3558
|
+
var MARKER_FILE = ".recipe-ref";
|
|
3559
|
+
var GITHUB_PREFIX = "github.com/";
|
|
3560
|
+
function ensureRecipeCheckout(recipeRef, recipeDir, exec2 = execFileSync7) {
|
|
3561
|
+
const at = recipeRef.lastIndexOf("@");
|
|
3562
|
+
if (at <= 0) {
|
|
3563
|
+
consola28.warn(`recipe: malformed recipeRef "${recipeRef}" (expected <owner>/<name>[/<subdir>]@<ref>) \u2014 skipping checkout`);
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
const spec = recipeRef.slice(0, at);
|
|
3567
|
+
const ref = recipeRef.slice(at + 1);
|
|
3568
|
+
const slug = spec.startsWith(GITHUB_PREFIX) ? spec.slice(GITHUB_PREFIX.length) : spec;
|
|
3569
|
+
if (!slug || !ref) {
|
|
3570
|
+
consola28.warn(`recipe: malformed recipeRef "${recipeRef}" (empty slug or ref) \u2014 skipping checkout`);
|
|
3571
|
+
return;
|
|
3572
|
+
}
|
|
3573
|
+
const segments = slug.split("/").filter(Boolean);
|
|
3574
|
+
if (segments.length < 2) {
|
|
3575
|
+
consola28.warn(`recipe: malformed recipeRef "${recipeRef}" (expected <owner>/<name>[/<subdir>]@<ref>) \u2014 skipping checkout`);
|
|
3576
|
+
return;
|
|
3577
|
+
}
|
|
3578
|
+
if (segments.some((s) => s === "." || s === ".." || s.includes("\\"))) {
|
|
3579
|
+
consola28.warn(`recipe: unsafe path segment in recipeRef "${recipeRef}" \u2014 skipping checkout`);
|
|
3580
|
+
return;
|
|
3581
|
+
}
|
|
3582
|
+
const repoSlug = segments.slice(0, 2).join("/");
|
|
3583
|
+
const subdir = segments.slice(2).join("/");
|
|
3584
|
+
const markerPath = join7(recipeDir, MARKER_FILE);
|
|
3585
|
+
if (existsSync11(markerPath)) {
|
|
3586
|
+
try {
|
|
3587
|
+
if (readFileSync10(markerPath, "utf8") === recipeRef) return;
|
|
3588
|
+
} catch {
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
const cloneUrl = `https://github.com/${repoSlug}`;
|
|
3592
|
+
const staging = subdir ? `${recipeDir}.checkout` : recipeDir;
|
|
3593
|
+
try {
|
|
3594
|
+
rmSync2(recipeDir, { recursive: true, force: true });
|
|
3595
|
+
if (subdir)
|
|
3596
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3597
|
+
exec2("git", ["clone", "--depth", "1", "--branch", ref, cloneUrl, staging]);
|
|
3598
|
+
if (subdir) {
|
|
3599
|
+
const src = join7(staging, subdir);
|
|
3600
|
+
if (!resolve3(src).startsWith(resolve3(staging) + sep)) {
|
|
3601
|
+
consola28.warn(`recipe: subdirectory "${subdir}" escapes the checkout dir \u2014 skipping`);
|
|
3602
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
if (!existsSync11(src)) {
|
|
3606
|
+
consola28.warn(`recipe: subdirectory "${subdir}" not found in ${repoSlug}@${ref} \u2014 skipping checkout`);
|
|
3607
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3608
|
+
return;
|
|
3609
|
+
}
|
|
3610
|
+
cpSync(src, recipeDir, { recursive: true });
|
|
3611
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3612
|
+
}
|
|
3613
|
+
writeFileSync5(markerPath, recipeRef);
|
|
3614
|
+
consola28.info(`recipe: checked out ${slug}@${ref} \u2192 ${recipeDir}`);
|
|
3615
|
+
} catch (err) {
|
|
3616
|
+
consola28.warn(`recipe: checkout of ${slug}@${ref} failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
3617
|
+
if (subdir)
|
|
3618
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3584
3619
|
}
|
|
3585
|
-
return raw;
|
|
3586
3620
|
}
|
|
3587
3621
|
|
|
3588
3622
|
// src/commands/agents/sync.ts
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
import { join as join8 } from "path";
|
|
3592
|
-
import { defineCommand as defineCommand31 } from "citty";
|
|
3593
|
-
import consola27 from "consola";
|
|
3594
|
-
var AUTH_PATH3 = join8(homedir10(), ".config", "apes", "auth.json");
|
|
3595
|
-
var TASK_CACHE_DIR2 = join8(homedir10(), ".openape", "agent", "tasks");
|
|
3623
|
+
var AUTH_PATH3 = join8(homedir9(), ".config", "apes", "auth.json");
|
|
3624
|
+
var TASK_CACHE_DIR2 = join8(homedir9(), ".openape", "agent", "tasks");
|
|
3596
3625
|
function readAuthJson() {
|
|
3597
3626
|
if (!existsSync12(AUTH_PATH3)) {
|
|
3598
3627
|
throw new CliError(
|
|
@@ -3623,7 +3652,7 @@ function agentNameFromEmail(email) {
|
|
|
3623
3652
|
}
|
|
3624
3653
|
return before.slice(0, dashIdx);
|
|
3625
3654
|
}
|
|
3626
|
-
var syncAgentCommand =
|
|
3655
|
+
var syncAgentCommand = defineCommand32({
|
|
3627
3656
|
meta: {
|
|
3628
3657
|
name: "sync",
|
|
3629
3658
|
description: "Pull this agent's task list from troop.openape.ai and reconcile launchd plists"
|
|
@@ -3637,6 +3666,21 @@ var syncAgentCommand = defineCommand31({
|
|
|
3637
3666
|
async run({ args }) {
|
|
3638
3667
|
const auth = readAuthJson();
|
|
3639
3668
|
const agentName = agentNameFromEmail(auth.email);
|
|
3669
|
+
const refreshed = await refreshAgentToken({ ...auth, expires_at: auth.expires_at ?? 0 }).catch(() => null);
|
|
3670
|
+
if (refreshed?.access_token && refreshed.access_token !== auth.access_token) {
|
|
3671
|
+
auth.access_token = refreshed.access_token;
|
|
3672
|
+
if (typeof refreshed.expires_at === "number") auth.expires_at = refreshed.expires_at;
|
|
3673
|
+
try {
|
|
3674
|
+
const { uid, gid } = statSync(AUTH_PATH3);
|
|
3675
|
+
writeFileSync6(AUTH_PATH3, `${JSON.stringify(auth, null, 2)}
|
|
3676
|
+
`);
|
|
3677
|
+
try {
|
|
3678
|
+
chownSync(AUTH_PATH3, uid, gid);
|
|
3679
|
+
} catch {
|
|
3680
|
+
}
|
|
3681
|
+
} catch {
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3640
3684
|
const troopUrl = resolveTroopUrl(args["troop-url"]);
|
|
3641
3685
|
const client = new TroopClient(troopUrl, auth.access_token);
|
|
3642
3686
|
const platform = getHostPlatform();
|
|
@@ -3648,7 +3692,7 @@ var syncAgentCommand = defineCommand31({
|
|
|
3648
3692
|
if (!auth.owner_email) {
|
|
3649
3693
|
throw new CliError(`${AUTH_PATH3} is missing owner_email \u2014 re-run \`apes agents spawn\` to update.`);
|
|
3650
3694
|
}
|
|
3651
|
-
|
|
3695
|
+
consola29.start(`Syncing ${agentName} (${host}, hostId ${hostId.slice(0, 8)}\u2026) with ${troopUrl}`);
|
|
3652
3696
|
const pubkeyX25519 = readAgentEncryptionPublicKey() ?? void 0;
|
|
3653
3697
|
const sync = await client.sync({
|
|
3654
3698
|
hostname: host,
|
|
@@ -3656,17 +3700,17 @@ var syncAgentCommand = defineCommand31({
|
|
|
3656
3700
|
ownerEmail: auth.owner_email,
|
|
3657
3701
|
...pubkeyX25519 ? { pubkeyX25519 } : {}
|
|
3658
3702
|
});
|
|
3659
|
-
|
|
3660
|
-
if (!pubkeyX25519)
|
|
3661
|
-
const { system_prompt: systemPrompt, tools, skills, tasks } = await client.listTasks();
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3703
|
+
consola29.info(sync.first_sync ? "\u2713 first sync \u2014 agent registered" : "\u2713 presence updated");
|
|
3704
|
+
if (!pubkeyX25519) consola29.warn("No X25519 public key found on disk \u2014 sealed capability secrets cannot be bound until the agent is re-spawned.");
|
|
3705
|
+
const { system_prompt: systemPrompt, tools, skills, tasks, recipe_ref: recipeRef } = await client.listTasks();
|
|
3706
|
+
consola29.info(`Pulled ${tasks.length} task${tasks.length === 1 ? "" : "s"}`);
|
|
3707
|
+
consola29.info(`Tools enabled: ${tools.length === 0 ? "(none)" : tools.join(", ")}`);
|
|
3708
|
+
consola29.info(`Skills: ${skills.length === 0 ? "(none)" : skills.map((s) => s.name).join(", ")}`);
|
|
3665
3709
|
let agentUid = null;
|
|
3666
3710
|
let agentGid = null;
|
|
3667
3711
|
if (process.geteuid?.() === 0) {
|
|
3668
3712
|
try {
|
|
3669
|
-
const homeStat = statSync(
|
|
3713
|
+
const homeStat = statSync(homedir9());
|
|
3670
3714
|
agentUid = homeStat.uid;
|
|
3671
3715
|
agentGid = homeStat.gid;
|
|
3672
3716
|
} catch {
|
|
@@ -3680,23 +3724,24 @@ var syncAgentCommand = defineCommand31({
|
|
|
3680
3724
|
}
|
|
3681
3725
|
}
|
|
3682
3726
|
}
|
|
3683
|
-
const agentDir = join8(
|
|
3727
|
+
const agentDir = join8(homedir9(), ".openape", "agent");
|
|
3684
3728
|
mkdirSync3(agentDir, { recursive: true });
|
|
3685
|
-
chownToAgent(join8(
|
|
3729
|
+
chownToAgent(join8(homedir9(), ".openape"));
|
|
3686
3730
|
chownToAgent(agentDir);
|
|
3687
3731
|
const agentJsonPath = join8(agentDir, "agent.json");
|
|
3688
|
-
|
|
3732
|
+
writeFileSync6(
|
|
3689
3733
|
agentJsonPath,
|
|
3690
|
-
`${JSON.stringify({ systemPrompt, tools }, null, 2)}
|
|
3734
|
+
`${JSON.stringify({ systemPrompt, tools, ...recipeRef ? { recipeRef } : {} }, null, 2)}
|
|
3691
3735
|
`,
|
|
3692
3736
|
{ mode: 384 }
|
|
3693
3737
|
);
|
|
3694
3738
|
chownToAgent(agentJsonPath);
|
|
3739
|
+
if (recipeRef) ensureRecipeCheckout(recipeRef, join8(homedir9(), "recipe"));
|
|
3695
3740
|
mkdirSync3(TASK_CACHE_DIR2, { recursive: true });
|
|
3696
3741
|
chownToAgent(TASK_CACHE_DIR2);
|
|
3697
3742
|
for (const task of tasks) {
|
|
3698
3743
|
const path2 = join8(TASK_CACHE_DIR2, `${task.taskId}.json`);
|
|
3699
|
-
|
|
3744
|
+
writeFileSync6(path2, `${JSON.stringify(task, null, 2)}
|
|
3700
3745
|
`, { mode: 384 });
|
|
3701
3746
|
chownToAgent(path2);
|
|
3702
3747
|
}
|
|
@@ -3705,10 +3750,10 @@ var syncAgentCommand = defineCommand31({
|
|
|
3705
3750
|
chownToAgent(skillsDir);
|
|
3706
3751
|
const incomingNames = new Set(skills.map((s) => s.name));
|
|
3707
3752
|
try {
|
|
3708
|
-
for (const entry of
|
|
3753
|
+
for (const entry of readdirSync(skillsDir)) {
|
|
3709
3754
|
if (incomingNames.has(entry)) continue;
|
|
3710
3755
|
try {
|
|
3711
|
-
|
|
3756
|
+
rmSync3(join8(skillsDir, entry), { recursive: true, force: true });
|
|
3712
3757
|
} catch {
|
|
3713
3758
|
}
|
|
3714
3759
|
}
|
|
@@ -3719,16 +3764,16 @@ var syncAgentCommand = defineCommand31({
|
|
|
3719
3764
|
mkdirSync3(skillDir, { recursive: true });
|
|
3720
3765
|
chownToAgent(skillDir);
|
|
3721
3766
|
const skillPath = join8(skillDir, "SKILL.md");
|
|
3722
|
-
|
|
3767
|
+
writeFileSync6(skillPath, skill.body.endsWith("\n") ? skill.body : `${skill.body}
|
|
3723
3768
|
`, { mode: 384 });
|
|
3724
3769
|
chownToAgent(skillPath);
|
|
3725
3770
|
}
|
|
3726
|
-
|
|
3771
|
+
consola29.success("Sync complete.");
|
|
3727
3772
|
}
|
|
3728
3773
|
});
|
|
3729
3774
|
|
|
3730
3775
|
// src/commands/agents/index.ts
|
|
3731
|
-
var agentsCommand =
|
|
3776
|
+
var agentsCommand = defineCommand33({
|
|
3732
3777
|
meta: {
|
|
3733
3778
|
name: "agents",
|
|
3734
3779
|
description: "Manage owned agents (register, spawn, list, destroy, allow, sync, run, serve, code, cleanup-orphans)"
|
|
@@ -3748,22 +3793,22 @@ var agentsCommand = defineCommand32({
|
|
|
3748
3793
|
});
|
|
3749
3794
|
|
|
3750
3795
|
// src/commands/nest/index.ts
|
|
3751
|
-
import { defineCommand as
|
|
3796
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
3752
3797
|
|
|
3753
3798
|
// src/commands/nest/authorize.ts
|
|
3754
|
-
import { execFileSync as
|
|
3799
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
3755
3800
|
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
3756
3801
|
import { join as join10 } from "path";
|
|
3757
|
-
import { defineCommand as
|
|
3758
|
-
import
|
|
3802
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
3803
|
+
import consola31 from "consola";
|
|
3759
3804
|
|
|
3760
3805
|
// src/commands/nest/enroll.ts
|
|
3761
|
-
import { hostname as hostname4, homedir as
|
|
3762
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as
|
|
3806
|
+
import { hostname as hostname4, homedir as homedir10 } from "os";
|
|
3807
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, chmodSync } from "fs";
|
|
3763
3808
|
import { join as join9 } from "path";
|
|
3764
|
-
import { defineCommand as
|
|
3765
|
-
import
|
|
3766
|
-
var NEST_DATA_DIR = join9(
|
|
3809
|
+
import { defineCommand as defineCommand34 } from "citty";
|
|
3810
|
+
import consola30 from "consola";
|
|
3811
|
+
var NEST_DATA_DIR = join9(homedir10(), ".openape", "nest");
|
|
3767
3812
|
function nestAgentName() {
|
|
3768
3813
|
const raw = hostname4().toLowerCase();
|
|
3769
3814
|
const head = raw.split(".")[0] ?? raw;
|
|
@@ -3771,7 +3816,7 @@ function nestAgentName() {
|
|
|
3771
3816
|
const trimmed = safe.slice(0, 16);
|
|
3772
3817
|
return `nest-${trimmed || "host"}`;
|
|
3773
3818
|
}
|
|
3774
|
-
var enrollNestCommand =
|
|
3819
|
+
var enrollNestCommand = defineCommand34({
|
|
3775
3820
|
meta: {
|
|
3776
3821
|
name: "enroll",
|
|
3777
3822
|
description: "Register the local nest as a DDISA agent at the IdP. One-time per machine. Required before `apes nest authorize` so YOLO-policies have a target identity."
|
|
@@ -3804,17 +3849,17 @@ var enrollNestCommand = defineCommand33({
|
|
|
3804
3849
|
const configDir = join9(NEST_DATA_DIR, ".config", "apes");
|
|
3805
3850
|
mkdirSync4(sshDir, { recursive: true });
|
|
3806
3851
|
mkdirSync4(configDir, { recursive: true });
|
|
3807
|
-
|
|
3852
|
+
consola30.start(`Generating keypair for ${name}\u2026`);
|
|
3808
3853
|
const { privatePem, publicSshLine } = generateKeyPairInMemory();
|
|
3809
|
-
|
|
3854
|
+
writeFileSync7(join9(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
|
|
3810
3855
|
`, { mode: 384 });
|
|
3811
|
-
|
|
3856
|
+
writeFileSync7(join9(sshDir, "id_ed25519.pub"), `${publicSshLine}
|
|
3812
3857
|
`, { mode: 420 });
|
|
3813
3858
|
chmodSync(sshDir, 448);
|
|
3814
|
-
|
|
3859
|
+
consola30.start(`Registering nest at ${idp}\u2026`);
|
|
3815
3860
|
const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
|
|
3816
|
-
|
|
3817
|
-
|
|
3861
|
+
consola30.success(`Registered as ${registration.email}`);
|
|
3862
|
+
consola30.start("Issuing nest access token\u2026");
|
|
3818
3863
|
const { token, expiresIn } = await issueAgentToken({
|
|
3819
3864
|
idp,
|
|
3820
3865
|
agentEmail: registration.email,
|
|
@@ -3828,13 +3873,13 @@ var enrollNestCommand = defineCommand33({
|
|
|
3828
3873
|
keyPath: join9(sshDir, "id_ed25519"),
|
|
3829
3874
|
ownerEmail: ownerAuth.email
|
|
3830
3875
|
});
|
|
3831
|
-
|
|
3876
|
+
writeFileSync7(authPath, authJson, { mode: 384 });
|
|
3832
3877
|
chmodSync(configDir, 448);
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3878
|
+
consola30.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
|
|
3879
|
+
consola30.info("");
|
|
3880
|
+
consola30.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
|
|
3881
|
+
consola30.info("");
|
|
3882
|
+
consola30.info(" apes nest authorize");
|
|
3838
3883
|
}
|
|
3839
3884
|
});
|
|
3840
3885
|
|
|
@@ -3881,7 +3926,7 @@ var DEFAULT_ALLOW_PATTERNS = [
|
|
|
3881
3926
|
"pm2 delete openape-bridge-*",
|
|
3882
3927
|
"pm2 jlist"
|
|
3883
3928
|
];
|
|
3884
|
-
var authorizeNestCommand =
|
|
3929
|
+
var authorizeNestCommand = defineCommand35({
|
|
3885
3930
|
meta: {
|
|
3886
3931
|
name: "authorize",
|
|
3887
3932
|
description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts (wraps `apes yolo set`)"
|
|
@@ -3903,8 +3948,8 @@ var authorizeNestCommand = defineCommand34({
|
|
|
3903
3948
|
}
|
|
3904
3949
|
const nestAuth = JSON.parse(readFileSync12(nestAuthPath, "utf8"));
|
|
3905
3950
|
if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
|
|
3906
|
-
const
|
|
3907
|
-
|
|
3951
|
+
const allow2 = args.allow ?? DEFAULT_ALLOW_PATTERNS.join(",");
|
|
3952
|
+
consola31.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
|
|
3908
3953
|
const cmdArgs = [
|
|
3909
3954
|
"yolo",
|
|
3910
3955
|
"set",
|
|
@@ -3912,26 +3957,26 @@ var authorizeNestCommand = defineCommand34({
|
|
|
3912
3957
|
"--mode",
|
|
3913
3958
|
"allow-list",
|
|
3914
3959
|
"--allow",
|
|
3915
|
-
|
|
3960
|
+
allow2
|
|
3916
3961
|
];
|
|
3917
3962
|
if (typeof args["expires-in"] === "string" && args["expires-in"]) {
|
|
3918
3963
|
cmdArgs.push("--expires-in", args["expires-in"]);
|
|
3919
3964
|
}
|
|
3920
3965
|
try {
|
|
3921
|
-
|
|
3966
|
+
execFileSync8("apes", cmdArgs, { stdio: "inherit" });
|
|
3922
3967
|
} catch (err) {
|
|
3923
3968
|
throw new CliError(err instanceof Error ? err.message : String(err));
|
|
3924
3969
|
}
|
|
3925
|
-
|
|
3926
|
-
|
|
3970
|
+
consola31.success("Nest-driven agent lifecycle is now zero-prompt.");
|
|
3971
|
+
consola31.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
|
|
3927
3972
|
}
|
|
3928
3973
|
});
|
|
3929
3974
|
|
|
3930
3975
|
// src/commands/nest/destroy.ts
|
|
3931
|
-
import { execFileSync as
|
|
3932
|
-
import { defineCommand as
|
|
3933
|
-
import
|
|
3934
|
-
var destroyNestCommand =
|
|
3976
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
3977
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
3978
|
+
import consola32 from "consola";
|
|
3979
|
+
var destroyNestCommand = defineCommand36({
|
|
3935
3980
|
meta: {
|
|
3936
3981
|
name: "destroy",
|
|
3937
3982
|
description: "Destroy a local agent. Wraps `apes run --as root -- apes agents destroy <name>`; the Nest watches its registry and pm2-deletes the bridge automatically."
|
|
@@ -3942,8 +3987,8 @@ var destroyNestCommand = defineCommand35({
|
|
|
3942
3987
|
async run({ args }) {
|
|
3943
3988
|
const name = String(args.name);
|
|
3944
3989
|
try {
|
|
3945
|
-
|
|
3946
|
-
|
|
3990
|
+
execFileSync9("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
|
|
3991
|
+
consola32.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
|
|
3947
3992
|
} catch (err) {
|
|
3948
3993
|
const status = err.status ?? 1;
|
|
3949
3994
|
throw new CliExit(status);
|
|
@@ -3952,11 +3997,11 @@ var destroyNestCommand = defineCommand35({
|
|
|
3952
3997
|
});
|
|
3953
3998
|
|
|
3954
3999
|
// src/commands/nest/install.ts
|
|
3955
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as
|
|
3956
|
-
import { homedir as
|
|
4000
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
|
|
4001
|
+
import { homedir as homedir11 } from "os";
|
|
3957
4002
|
import { dirname as dirname2, join as join11 } from "path";
|
|
3958
|
-
import { defineCommand as
|
|
3959
|
-
import
|
|
4003
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
4004
|
+
import consola33 from "consola";
|
|
3960
4005
|
|
|
3961
4006
|
// src/commands/nest/apes-agents-adapter.ts
|
|
3962
4007
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4022,7 +4067,7 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
4022
4067
|
|
|
4023
4068
|
// src/commands/nest/install.ts
|
|
4024
4069
|
function installAdapter2() {
|
|
4025
|
-
const target = join11(
|
|
4070
|
+
const target = join11(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4026
4071
|
mkdirSync5(dirname2(target), { recursive: true });
|
|
4027
4072
|
let existing = "";
|
|
4028
4073
|
try {
|
|
@@ -4030,12 +4075,12 @@ function installAdapter2() {
|
|
|
4030
4075
|
} catch {
|
|
4031
4076
|
}
|
|
4032
4077
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4033
|
-
|
|
4034
|
-
|
|
4078
|
+
writeFileSync8(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4079
|
+
consola33.success(`Wrote shapes adapter ${target}`);
|
|
4035
4080
|
return true;
|
|
4036
4081
|
}
|
|
4037
4082
|
function writeBridgeModelDefault(model) {
|
|
4038
|
-
for (const envDir of [join11(
|
|
4083
|
+
for (const envDir of [join11(homedir11(), "litellm"), join11(NEST_DATA_DIR, "litellm")]) {
|
|
4039
4084
|
const envFile = join11(envDir, ".env");
|
|
4040
4085
|
mkdirSync5(envDir, { recursive: true });
|
|
4041
4086
|
let lines = [];
|
|
@@ -4044,13 +4089,13 @@ function writeBridgeModelDefault(model) {
|
|
|
4044
4089
|
}
|
|
4045
4090
|
lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
|
|
4046
4091
|
while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
|
|
4047
|
-
|
|
4092
|
+
writeFileSync8(envFile, `${lines.join("\n")}
|
|
4048
4093
|
`, { mode: 384 });
|
|
4049
4094
|
}
|
|
4050
4095
|
}
|
|
4051
4096
|
function findBinary(name) {
|
|
4052
4097
|
for (const dir of [
|
|
4053
|
-
join11(
|
|
4098
|
+
join11(homedir11(), ".bun", "bin"),
|
|
4054
4099
|
"/opt/homebrew/bin",
|
|
4055
4100
|
"/usr/local/bin",
|
|
4056
4101
|
"/usr/bin"
|
|
@@ -4060,7 +4105,7 @@ function findBinary(name) {
|
|
|
4060
4105
|
}
|
|
4061
4106
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4062
4107
|
}
|
|
4063
|
-
var installNestCommand =
|
|
4108
|
+
var installNestCommand = defineCommand37({
|
|
4064
4109
|
meta: {
|
|
4065
4110
|
name: "install",
|
|
4066
4111
|
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
@@ -4076,20 +4121,20 @@ var installNestCommand = defineCommand36({
|
|
|
4076
4121
|
}
|
|
4077
4122
|
},
|
|
4078
4123
|
async run({ args }) {
|
|
4079
|
-
const homeDir =
|
|
4124
|
+
const homeDir = homedir11();
|
|
4080
4125
|
const port = Number(args.port ?? 9091);
|
|
4081
4126
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
4082
4127
|
throw new Error(`invalid port ${port}`);
|
|
4083
4128
|
}
|
|
4084
4129
|
const nestBin = findBinary("openape-nest");
|
|
4085
4130
|
const apesBin = findBinary("apes");
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4131
|
+
consola33.info(`Installing nest supervisor`);
|
|
4132
|
+
consola33.info(` nest binary: ${nestBin}`);
|
|
4133
|
+
consola33.info(` apes binary: ${apesBin}`);
|
|
4134
|
+
consola33.info(` HTTP port: ${port}`);
|
|
4090
4135
|
if (typeof args["bridge-model"] === "string" && args["bridge-model"]) {
|
|
4091
4136
|
writeBridgeModelDefault(args["bridge-model"]);
|
|
4092
|
-
|
|
4137
|
+
consola33.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
|
|
4093
4138
|
}
|
|
4094
4139
|
installAdapter2();
|
|
4095
4140
|
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
@@ -4100,21 +4145,21 @@ var installNestCommand = defineCommand36({
|
|
|
4100
4145
|
nestHome: NEST_DATA_DIR,
|
|
4101
4146
|
port
|
|
4102
4147
|
});
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4148
|
+
consola33.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4149
|
+
consola33.info("");
|
|
4150
|
+
consola33.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
4151
|
+
consola33.info("");
|
|
4152
|
+
consola33.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
4153
|
+
consola33.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
4154
|
+
consola33.info("");
|
|
4155
|
+
consola33.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
4111
4156
|
}
|
|
4112
4157
|
});
|
|
4113
4158
|
|
|
4114
4159
|
// src/commands/nest/list.ts
|
|
4115
|
-
import { defineCommand as
|
|
4116
|
-
import
|
|
4117
|
-
var listNestCommand =
|
|
4160
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
4161
|
+
import consola34 from "consola";
|
|
4162
|
+
var listNestCommand = defineCommand38({
|
|
4118
4163
|
meta: {
|
|
4119
4164
|
name: "list",
|
|
4120
4165
|
description: "List agents registered with the local nest. Reads /var/openape/nest/agents.json directly."
|
|
@@ -4129,22 +4174,22 @@ var listNestCommand = defineCommand37({
|
|
|
4129
4174
|
return;
|
|
4130
4175
|
}
|
|
4131
4176
|
if (reg.agents.length === 0) {
|
|
4132
|
-
|
|
4177
|
+
consola34.info("(no agents registered with this nest)");
|
|
4133
4178
|
return;
|
|
4134
4179
|
}
|
|
4135
|
-
|
|
4180
|
+
consola34.info(`${reg.agents.length} agent(s) registered with this nest:`);
|
|
4136
4181
|
for (const a of reg.agents) {
|
|
4137
4182
|
const bridge = a.bridge ? " bridge=on" : "";
|
|
4138
|
-
|
|
4183
|
+
consola34.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
4139
4184
|
}
|
|
4140
4185
|
}
|
|
4141
4186
|
});
|
|
4142
4187
|
|
|
4143
4188
|
// src/commands/nest/spawn.ts
|
|
4144
|
-
import { execFileSync as
|
|
4145
|
-
import { defineCommand as
|
|
4146
|
-
import
|
|
4147
|
-
var spawnNestCommand =
|
|
4189
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
4190
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
4191
|
+
import consola35 from "consola";
|
|
4192
|
+
var spawnNestCommand = defineCommand39({
|
|
4148
4193
|
meta: {
|
|
4149
4194
|
name: "spawn",
|
|
4150
4195
|
description: "Spawn a new agent locally. Wraps `apes run --as root -- apes agents spawn <name>`; the Nest watches its registry and starts the bridge in pm2 automatically."
|
|
@@ -4174,8 +4219,8 @@ var spawnNestCommand = defineCommand38({
|
|
|
4174
4219
|
if (typeof args["bridge-base-url"] === "string") apesArgs.push("--bridge-base-url", args["bridge-base-url"]);
|
|
4175
4220
|
if (typeof args["bridge-model"] === "string") apesArgs.push("--bridge-model", args["bridge-model"]);
|
|
4176
4221
|
try {
|
|
4177
|
-
|
|
4178
|
-
|
|
4222
|
+
execFileSync10("apes", apesArgs, { stdio: "inherit" });
|
|
4223
|
+
consola35.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
|
|
4179
4224
|
} catch (err) {
|
|
4180
4225
|
const status = err.status ?? 1;
|
|
4181
4226
|
throw new CliExit(status);
|
|
@@ -4184,22 +4229,22 @@ var spawnNestCommand = defineCommand38({
|
|
|
4184
4229
|
});
|
|
4185
4230
|
|
|
4186
4231
|
// src/commands/nest/uninstall.ts
|
|
4187
|
-
import { defineCommand as
|
|
4188
|
-
import
|
|
4189
|
-
var uninstallNestCommand =
|
|
4232
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
4233
|
+
import consola36 from "consola";
|
|
4234
|
+
var uninstallNestCommand = defineCommand40({
|
|
4190
4235
|
meta: {
|
|
4191
4236
|
name: "uninstall",
|
|
4192
4237
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4193
4238
|
},
|
|
4194
4239
|
async run() {
|
|
4195
4240
|
await getHostPlatform().uninstallNestSupervisor();
|
|
4196
|
-
|
|
4197
|
-
|
|
4241
|
+
consola36.success("Nest daemon stopped + supervisor unit removed");
|
|
4242
|
+
consola36.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4198
4243
|
}
|
|
4199
4244
|
});
|
|
4200
4245
|
|
|
4201
4246
|
// src/commands/nest/index.ts
|
|
4202
|
-
var nestCommand =
|
|
4247
|
+
var nestCommand = defineCommand41({
|
|
4203
4248
|
meta: {
|
|
4204
4249
|
name: "nest",
|
|
4205
4250
|
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."
|
|
@@ -4216,12 +4261,12 @@ var nestCommand = defineCommand40({
|
|
|
4216
4261
|
});
|
|
4217
4262
|
|
|
4218
4263
|
// src/commands/yolo/index.ts
|
|
4219
|
-
import { defineCommand as
|
|
4264
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
4220
4265
|
|
|
4221
4266
|
// src/commands/yolo/clear.ts
|
|
4222
|
-
import { defineCommand as
|
|
4223
|
-
import
|
|
4224
|
-
var yoloClearCommand =
|
|
4267
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
4268
|
+
import consola37 from "consola";
|
|
4269
|
+
var yoloClearCommand = defineCommand42({
|
|
4225
4270
|
meta: {
|
|
4226
4271
|
name: "clear",
|
|
4227
4272
|
description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
|
|
@@ -4250,15 +4295,15 @@ var yoloClearCommand = defineCommand41({
|
|
|
4250
4295
|
const text = await res.text().catch(() => "");
|
|
4251
4296
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
4252
4297
|
}
|
|
4253
|
-
|
|
4298
|
+
consola37.success(`YOLO-policy cleared on ${email}`);
|
|
4254
4299
|
}
|
|
4255
4300
|
});
|
|
4256
4301
|
|
|
4257
4302
|
// src/commands/yolo/set.ts
|
|
4258
|
-
import { defineCommand as
|
|
4259
|
-
import
|
|
4303
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
4304
|
+
import consola38 from "consola";
|
|
4260
4305
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
4261
|
-
var yoloSetCommand =
|
|
4306
|
+
var yoloSetCommand = defineCommand43({
|
|
4262
4307
|
meta: {
|
|
4263
4308
|
name: "set",
|
|
4264
4309
|
description: "Write a YOLO-policy on a DDISA agent you own"
|
|
@@ -4306,12 +4351,12 @@ var yoloSetCommand = defineCommand42({
|
|
|
4306
4351
|
const denyPatterns = parseList(args.deny);
|
|
4307
4352
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
4308
4353
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
if (allowPatterns.length)
|
|
4312
|
-
if (denyPatterns.length)
|
|
4313
|
-
if (denyRiskThreshold)
|
|
4314
|
-
if (expiresAt)
|
|
4354
|
+
consola38.info(`Setting YOLO-policy on ${email}`);
|
|
4355
|
+
consola38.info(` mode: ${mode}`);
|
|
4356
|
+
if (allowPatterns.length) consola38.info(` allow_patterns: ${allowPatterns.join(", ")}`);
|
|
4357
|
+
if (denyPatterns.length) consola38.info(` deny_patterns: ${denyPatterns.join(", ")}`);
|
|
4358
|
+
if (denyRiskThreshold) consola38.info(` deny_risk: ${denyRiskThreshold}`);
|
|
4359
|
+
if (expiresAt) consola38.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
4315
4360
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
4316
4361
|
const res = await fetch(url, {
|
|
4317
4362
|
method: "PUT",
|
|
@@ -4331,7 +4376,7 @@ var yoloSetCommand = defineCommand42({
|
|
|
4331
4376
|
const text = await res.text().catch(() => "");
|
|
4332
4377
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
4333
4378
|
}
|
|
4334
|
-
|
|
4379
|
+
consola38.success(`YOLO-policy applied to ${email}`);
|
|
4335
4380
|
}
|
|
4336
4381
|
});
|
|
4337
4382
|
function parseList(s) {
|
|
@@ -4349,9 +4394,9 @@ function parseExpiresIn(s) {
|
|
|
4349
4394
|
}
|
|
4350
4395
|
|
|
4351
4396
|
// src/commands/yolo/show.ts
|
|
4352
|
-
import { defineCommand as
|
|
4353
|
-
import
|
|
4354
|
-
var yoloShowCommand =
|
|
4397
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
4398
|
+
import consola39 from "consola";
|
|
4399
|
+
var yoloShowCommand = defineCommand44({
|
|
4355
4400
|
meta: {
|
|
4356
4401
|
name: "show",
|
|
4357
4402
|
description: "Print the YOLO-policy currently set on a DDISA agent"
|
|
@@ -4388,17 +4433,17 @@ var yoloShowCommand = defineCommand43({
|
|
|
4388
4433
|
console.log(JSON.stringify(policy, null, 2));
|
|
4389
4434
|
return;
|
|
4390
4435
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4436
|
+
consola39.info(`YOLO-policy for ${email}`);
|
|
4437
|
+
consola39.info(` mode: ${policy.mode}`);
|
|
4438
|
+
consola39.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
|
|
4439
|
+
consola39.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
|
|
4440
|
+
consola39.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
|
|
4441
|
+
consola39.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
|
|
4397
4442
|
}
|
|
4398
4443
|
});
|
|
4399
4444
|
|
|
4400
4445
|
// src/commands/yolo/index.ts
|
|
4401
|
-
var yoloCommand =
|
|
4446
|
+
var yoloCommand = defineCommand45({
|
|
4402
4447
|
meta: {
|
|
4403
4448
|
name: "yolo",
|
|
4404
4449
|
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)."
|
|
@@ -4411,15 +4456,15 @@ var yoloCommand = defineCommand44({
|
|
|
4411
4456
|
});
|
|
4412
4457
|
|
|
4413
4458
|
// src/commands/adapter/index.ts
|
|
4414
|
-
import { defineCommand as
|
|
4415
|
-
import
|
|
4416
|
-
var adapterCommand =
|
|
4459
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
4460
|
+
import consola40 from "consola";
|
|
4461
|
+
var adapterCommand = defineCommand46({
|
|
4417
4462
|
meta: {
|
|
4418
4463
|
name: "adapter",
|
|
4419
4464
|
description: "Manage CLI adapters"
|
|
4420
4465
|
},
|
|
4421
4466
|
subCommands: {
|
|
4422
|
-
list:
|
|
4467
|
+
list: defineCommand46({
|
|
4423
4468
|
meta: {
|
|
4424
4469
|
name: "list",
|
|
4425
4470
|
description: "List available adapters"
|
|
@@ -4450,7 +4495,7 @@ var adapterCommand = defineCommand45({
|
|
|
4450
4495
|
`);
|
|
4451
4496
|
return;
|
|
4452
4497
|
}
|
|
4453
|
-
|
|
4498
|
+
consola40.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4454
4499
|
for (const a of index2.adapters) {
|
|
4455
4500
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4456
4501
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4472,7 +4517,7 @@ var adapterCommand = defineCommand45({
|
|
|
4472
4517
|
return;
|
|
4473
4518
|
}
|
|
4474
4519
|
if (local.length === 0) {
|
|
4475
|
-
|
|
4520
|
+
consola40.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4476
4521
|
return;
|
|
4477
4522
|
}
|
|
4478
4523
|
for (const a of local) {
|
|
@@ -4480,7 +4525,7 @@ var adapterCommand = defineCommand45({
|
|
|
4480
4525
|
}
|
|
4481
4526
|
}
|
|
4482
4527
|
}),
|
|
4483
|
-
install:
|
|
4528
|
+
install: defineCommand46({
|
|
4484
4529
|
meta: {
|
|
4485
4530
|
name: "install",
|
|
4486
4531
|
description: "Install an adapter from the registry"
|
|
@@ -4509,24 +4554,24 @@ var adapterCommand = defineCommand45({
|
|
|
4509
4554
|
for (const id of ids) {
|
|
4510
4555
|
const entry = findAdapter(index, id);
|
|
4511
4556
|
if (!entry) {
|
|
4512
|
-
|
|
4557
|
+
consola40.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4513
4558
|
continue;
|
|
4514
4559
|
}
|
|
4515
4560
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4516
4561
|
if (conflicts.length > 0) {
|
|
4517
4562
|
for (const c of conflicts) {
|
|
4518
|
-
|
|
4519
|
-
|
|
4563
|
+
consola40.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
|
|
4564
|
+
consola40.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
|
|
4520
4565
|
}
|
|
4521
4566
|
}
|
|
4522
4567
|
const result = await installAdapter(entry, { local });
|
|
4523
4568
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4524
|
-
|
|
4525
|
-
|
|
4569
|
+
consola40.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
4570
|
+
consola40.info(`Digest: ${result.digest}`);
|
|
4526
4571
|
}
|
|
4527
4572
|
}
|
|
4528
4573
|
}),
|
|
4529
|
-
remove:
|
|
4574
|
+
remove: defineCommand46({
|
|
4530
4575
|
meta: {
|
|
4531
4576
|
name: "remove",
|
|
4532
4577
|
description: "Remove an installed adapter"
|
|
@@ -4549,9 +4594,9 @@ var adapterCommand = defineCommand45({
|
|
|
4549
4594
|
let failed = false;
|
|
4550
4595
|
for (const id of ids) {
|
|
4551
4596
|
if (removeAdapter(id, local)) {
|
|
4552
|
-
|
|
4597
|
+
consola40.success(`Removed adapter: ${id}`);
|
|
4553
4598
|
} else {
|
|
4554
|
-
|
|
4599
|
+
consola40.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4555
4600
|
failed = true;
|
|
4556
4601
|
}
|
|
4557
4602
|
}
|
|
@@ -4559,7 +4604,7 @@ var adapterCommand = defineCommand45({
|
|
|
4559
4604
|
throw new CliError("Some adapters could not be removed");
|
|
4560
4605
|
}
|
|
4561
4606
|
}),
|
|
4562
|
-
info:
|
|
4607
|
+
info: defineCommand46({
|
|
4563
4608
|
meta: {
|
|
4564
4609
|
name: "info",
|
|
4565
4610
|
description: "Show detailed adapter information"
|
|
@@ -4601,7 +4646,7 @@ var adapterCommand = defineCommand45({
|
|
|
4601
4646
|
}
|
|
4602
4647
|
}
|
|
4603
4648
|
}),
|
|
4604
|
-
search:
|
|
4649
|
+
search: defineCommand46({
|
|
4605
4650
|
meta: {
|
|
4606
4651
|
name: "search",
|
|
4607
4652
|
description: "Search adapters in the registry"
|
|
@@ -4633,7 +4678,7 @@ var adapterCommand = defineCommand45({
|
|
|
4633
4678
|
return;
|
|
4634
4679
|
}
|
|
4635
4680
|
if (results.length === 0) {
|
|
4636
|
-
|
|
4681
|
+
consola40.info(`No adapters matching "${query}"`);
|
|
4637
4682
|
return;
|
|
4638
4683
|
}
|
|
4639
4684
|
for (const a of results) {
|
|
@@ -4642,7 +4687,7 @@ var adapterCommand = defineCommand45({
|
|
|
4642
4687
|
}
|
|
4643
4688
|
}
|
|
4644
4689
|
}),
|
|
4645
|
-
update:
|
|
4690
|
+
update: defineCommand46({
|
|
4646
4691
|
meta: {
|
|
4647
4692
|
name: "update",
|
|
4648
4693
|
description: "Update installed adapters"
|
|
@@ -4668,33 +4713,33 @@ var adapterCommand = defineCommand45({
|
|
|
4668
4713
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4669
4714
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4670
4715
|
if (targets.length === 0) {
|
|
4671
|
-
|
|
4716
|
+
consola40.info("No adapters installed to update.");
|
|
4672
4717
|
return;
|
|
4673
4718
|
}
|
|
4674
4719
|
for (const id of targets) {
|
|
4675
4720
|
const entry = findAdapter(index, id);
|
|
4676
4721
|
if (!entry) {
|
|
4677
|
-
|
|
4722
|
+
consola40.warn(`${id}: not found in registry, skipping`);
|
|
4678
4723
|
continue;
|
|
4679
4724
|
}
|
|
4680
4725
|
const localDigest = getInstalledDigest(id, false);
|
|
4681
4726
|
if (localDigest === entry.digest) {
|
|
4682
|
-
|
|
4727
|
+
consola40.info(`${id}: already up to date`);
|
|
4683
4728
|
continue;
|
|
4684
4729
|
}
|
|
4685
4730
|
if (localDigest && !args.yes) {
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4731
|
+
consola40.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
4732
|
+
consola40.info(` Old: ${localDigest}`);
|
|
4733
|
+
consola40.info(` New: ${entry.digest}`);
|
|
4734
|
+
consola40.info(" Use --yes to confirm");
|
|
4690
4735
|
continue;
|
|
4691
4736
|
}
|
|
4692
4737
|
const result = await installAdapter(entry);
|
|
4693
|
-
|
|
4738
|
+
consola40.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4694
4739
|
}
|
|
4695
4740
|
}
|
|
4696
4741
|
}),
|
|
4697
|
-
verify:
|
|
4742
|
+
verify: defineCommand46({
|
|
4698
4743
|
meta: {
|
|
4699
4744
|
name: "verify",
|
|
4700
4745
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4727,7 +4772,7 @@ var adapterCommand = defineCommand45({
|
|
|
4727
4772
|
if (!localDigest)
|
|
4728
4773
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4729
4774
|
if (localDigest === entry.digest) {
|
|
4730
|
-
|
|
4775
|
+
consola40.success(`${id}: digest matches registry`);
|
|
4731
4776
|
} else {
|
|
4732
4777
|
console.log(` Local: ${localDigest}`);
|
|
4733
4778
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4739,11 +4784,11 @@ var adapterCommand = defineCommand45({
|
|
|
4739
4784
|
});
|
|
4740
4785
|
|
|
4741
4786
|
// src/commands/run.ts
|
|
4742
|
-
import { execFileSync as
|
|
4787
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
4743
4788
|
import { hostname as hostname5 } from "os";
|
|
4744
4789
|
import { basename } from "path";
|
|
4745
|
-
import { defineCommand as
|
|
4746
|
-
import
|
|
4790
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
4791
|
+
import consola41 from "consola";
|
|
4747
4792
|
function resolveRunAsTarget(runAs) {
|
|
4748
4793
|
if (!runAs) return runAs;
|
|
4749
4794
|
if (!AGENT_NAME_REGEX.test(runAs)) return runAs;
|
|
@@ -4799,7 +4844,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4799
4844
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4800
4845
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4801
4846
|
if (mode === "human") {
|
|
4802
|
-
|
|
4847
|
+
consola41.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4803
4848
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4804
4849
|
console.log(` Check status: ${statusCmd}`);
|
|
4805
4850
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4809,7 +4854,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4809
4854
|
return;
|
|
4810
4855
|
}
|
|
4811
4856
|
const maxMin = getPollMaxMinutes();
|
|
4812
|
-
|
|
4857
|
+
consola41.success(`Grant ${grant.id} created (pending approval)`);
|
|
4813
4858
|
console.log(` Approve: ${approveUrl}`);
|
|
4814
4859
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4815
4860
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4831,7 +4876,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4831
4876
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4832
4877
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4833
4878
|
}
|
|
4834
|
-
var runCommand =
|
|
4879
|
+
var runCommand = defineCommand47({
|
|
4835
4880
|
meta: {
|
|
4836
4881
|
name: "run",
|
|
4837
4882
|
description: "Execute a grant-secured command"
|
|
@@ -4937,7 +4982,7 @@ async function runShellMode(command, args) {
|
|
|
4937
4982
|
}
|
|
4938
4983
|
} catch {
|
|
4939
4984
|
}
|
|
4940
|
-
|
|
4985
|
+
consola41.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
4941
4986
|
const grant = await apiFetch(grantsUrl, {
|
|
4942
4987
|
method: "POST",
|
|
4943
4988
|
body: {
|
|
@@ -4957,8 +5002,8 @@ async function runShellMode(command, args) {
|
|
|
4957
5002
|
host: targetHost
|
|
4958
5003
|
});
|
|
4959
5004
|
if (shouldWaitForGrant(args)) {
|
|
4960
|
-
|
|
4961
|
-
|
|
5005
|
+
consola41.info(`Grant requested: ${grant.id}`);
|
|
5006
|
+
consola41.info("Waiting for approval...");
|
|
4962
5007
|
const maxWait = 3e5;
|
|
4963
5008
|
const interval = 3e3;
|
|
4964
5009
|
const start = Date.now();
|
|
@@ -4989,13 +5034,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4989
5034
|
try {
|
|
4990
5035
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
4991
5036
|
} catch (err) {
|
|
4992
|
-
|
|
5037
|
+
consola41.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
4993
5038
|
return false;
|
|
4994
5039
|
}
|
|
4995
5040
|
try {
|
|
4996
5041
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4997
5042
|
if (existingGrantId) {
|
|
4998
|
-
|
|
5043
|
+
consola41.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
4999
5044
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5000
5045
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5001
5046
|
return true;
|
|
@@ -5003,7 +5048,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5003
5048
|
} catch {
|
|
5004
5049
|
}
|
|
5005
5050
|
const approval = args.approval ?? "once";
|
|
5006
|
-
|
|
5051
|
+
consola41.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
5007
5052
|
const grant = await createShapesGrant(resolved, {
|
|
5008
5053
|
idp,
|
|
5009
5054
|
approval,
|
|
@@ -5011,8 +5056,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5011
5056
|
});
|
|
5012
5057
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5013
5058
|
const n = grant.similar_grants.similar_grants.length;
|
|
5014
|
-
|
|
5015
|
-
|
|
5059
|
+
consola41.info("");
|
|
5060
|
+
consola41.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
5016
5061
|
}
|
|
5017
5062
|
notifyGrantPending({
|
|
5018
5063
|
grantId: grant.id,
|
|
@@ -5022,8 +5067,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5022
5067
|
host: args.host || hostname5()
|
|
5023
5068
|
});
|
|
5024
5069
|
if (shouldWaitForGrant(args)) {
|
|
5025
|
-
|
|
5026
|
-
|
|
5070
|
+
consola41.info(`Grant requested: ${grant.id}`);
|
|
5071
|
+
consola41.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5027
5072
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5028
5073
|
if (status !== "approved")
|
|
5029
5074
|
throw new CliError(`Grant ${status}`);
|
|
@@ -5039,7 +5084,7 @@ function execShellCommand(command) {
|
|
|
5039
5084
|
throw new CliError("No command to execute");
|
|
5040
5085
|
try {
|
|
5041
5086
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5042
|
-
|
|
5087
|
+
execFileSync11(command[0], command.slice(1), {
|
|
5043
5088
|
stdio: "inherit",
|
|
5044
5089
|
env: inheritedEnv
|
|
5045
5090
|
});
|
|
@@ -5097,7 +5142,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5097
5142
|
try {
|
|
5098
5143
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5099
5144
|
if (existingGrantId) {
|
|
5100
|
-
|
|
5145
|
+
consola41.info(`Reusing existing grant: ${existingGrantId}`);
|
|
5101
5146
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5102
5147
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5103
5148
|
return;
|
|
@@ -5111,17 +5156,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5111
5156
|
});
|
|
5112
5157
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5113
5158
|
const n = grant.similar_grants.similar_grants.length;
|
|
5114
|
-
|
|
5115
|
-
|
|
5159
|
+
consola41.info("");
|
|
5160
|
+
consola41.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
5116
5161
|
if (grant.similar_grants.widened_details?.length) {
|
|
5117
5162
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
5118
|
-
|
|
5163
|
+
consola41.info(` Broader scope: ${wider}`);
|
|
5119
5164
|
}
|
|
5120
|
-
|
|
5165
|
+
consola41.info("");
|
|
5121
5166
|
}
|
|
5122
5167
|
if (shouldWaitForGrant(args)) {
|
|
5123
|
-
|
|
5124
|
-
|
|
5168
|
+
consola41.info(`Grant requested: ${grant.id}`);
|
|
5169
|
+
consola41.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5125
5170
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5126
5171
|
if (status !== "approved")
|
|
5127
5172
|
throw new Error(`Grant ${status}`);
|
|
@@ -5154,7 +5199,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5154
5199
|
const { authz_jwt: authz_jwt2 } = await apiFetch(`${grantsUrl}/${reusableId}/token`, { method: "POST" });
|
|
5155
5200
|
return executeWithGrantToken({ audience, command, args, token: authz_jwt2 });
|
|
5156
5201
|
}
|
|
5157
|
-
|
|
5202
|
+
consola41.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5158
5203
|
const grant = await apiFetch(grantsUrl, {
|
|
5159
5204
|
method: "POST",
|
|
5160
5205
|
body: {
|
|
@@ -5171,9 +5216,9 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5171
5216
|
printPendingGrantInfo(grant, idp);
|
|
5172
5217
|
throw new CliExit(getAsyncExitCode());
|
|
5173
5218
|
}
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5219
|
+
consola41.success(`Grant requested: ${grant.id}`);
|
|
5220
|
+
consola41.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5221
|
+
consola41.info("Waiting for approval...");
|
|
5177
5222
|
const maxWait = 15 * 60 * 1e3;
|
|
5178
5223
|
const interval = 3e3;
|
|
5179
5224
|
const start = Date.now();
|
|
@@ -5181,7 +5226,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5181
5226
|
while (Date.now() - start < maxWait) {
|
|
5182
5227
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5183
5228
|
if (status.status === "approved") {
|
|
5184
|
-
|
|
5229
|
+
consola41.success("Grant approved!");
|
|
5185
5230
|
approved = true;
|
|
5186
5231
|
break;
|
|
5187
5232
|
}
|
|
@@ -5196,7 +5241,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5196
5241
|
`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.`
|
|
5197
5242
|
);
|
|
5198
5243
|
}
|
|
5199
|
-
|
|
5244
|
+
consola41.info("Fetching grant token...");
|
|
5200
5245
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5201
5246
|
method: "POST"
|
|
5202
5247
|
});
|
|
@@ -5205,10 +5250,10 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5205
5250
|
function executeWithGrantToken(opts) {
|
|
5206
5251
|
const { audience, command, args, token } = opts;
|
|
5207
5252
|
if (audience === "escapes") {
|
|
5208
|
-
|
|
5253
|
+
consola41.info(`Executing: ${command.join(" ")}`);
|
|
5209
5254
|
try {
|
|
5210
5255
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5211
|
-
|
|
5256
|
+
execFileSync11(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
|
|
5212
5257
|
stdio: "inherit",
|
|
5213
5258
|
env: inheritedEnv
|
|
5214
5259
|
});
|
|
@@ -5245,10 +5290,10 @@ async function findReusableAudienceGrant(opts) {
|
|
|
5245
5290
|
// src/commands/proxy.ts
|
|
5246
5291
|
import { spawn as spawn2 } from "child_process";
|
|
5247
5292
|
import { existsSync as existsSync17 } from "fs";
|
|
5248
|
-
import { homedir as
|
|
5293
|
+
import { homedir as homedir12 } from "os";
|
|
5249
5294
|
import { join as join14 } from "path";
|
|
5250
|
-
import { defineCommand as
|
|
5251
|
-
import
|
|
5295
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
5296
|
+
import consola42 from "consola";
|
|
5252
5297
|
|
|
5253
5298
|
// src/proxy/config.ts
|
|
5254
5299
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5282,10 +5327,10 @@ note = "VPC-internal hostname suffix"
|
|
|
5282
5327
|
|
|
5283
5328
|
// src/proxy/local-proxy.ts
|
|
5284
5329
|
import { spawn } from "child_process";
|
|
5285
|
-
import { mkdtempSync as mkdtempSync2, rmSync as
|
|
5330
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
5286
5331
|
import { createRequire } from "module";
|
|
5287
5332
|
import { tmpdir as tmpdir2 } from "os";
|
|
5288
|
-
import { dirname as dirname3, join as join12, resolve as
|
|
5333
|
+
import { dirname as dirname3, join as join12, resolve as resolve4 } from "path";
|
|
5289
5334
|
var require2 = createRequire(import.meta.url);
|
|
5290
5335
|
function findProxyBin() {
|
|
5291
5336
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -5294,12 +5339,12 @@ function findProxyBin() {
|
|
|
5294
5339
|
if (!binRel) {
|
|
5295
5340
|
throw new Error("@openape/proxy is missing the openape-proxy bin entry");
|
|
5296
5341
|
}
|
|
5297
|
-
return
|
|
5342
|
+
return resolve4(dirname3(pkgPath), binRel);
|
|
5298
5343
|
}
|
|
5299
5344
|
async function startEphemeralProxy(configToml) {
|
|
5300
5345
|
const tmpDir = mkdtempSync2(join12(tmpdir2(), "openape-proxy-"));
|
|
5301
5346
|
const configPath = join12(tmpDir, "config.toml");
|
|
5302
|
-
|
|
5347
|
+
writeFileSync9(configPath, configToml, { mode: 384 });
|
|
5303
5348
|
const binPath = findProxyBin();
|
|
5304
5349
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
5305
5350
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5307,7 +5352,7 @@ async function startEphemeralProxy(configToml) {
|
|
|
5307
5352
|
});
|
|
5308
5353
|
const cleanupTmp = () => {
|
|
5309
5354
|
try {
|
|
5310
|
-
|
|
5355
|
+
rmSync4(tmpDir, { recursive: true, force: true });
|
|
5311
5356
|
} catch {
|
|
5312
5357
|
}
|
|
5313
5358
|
};
|
|
@@ -5381,7 +5426,7 @@ function waitForListenLine(child) {
|
|
|
5381
5426
|
}
|
|
5382
5427
|
|
|
5383
5428
|
// src/proxy/trust-bundle.ts
|
|
5384
|
-
import { existsSync as existsSync16, mkdtempSync as mkdtempSync3, readFileSync as readFileSync14, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as
|
|
5429
|
+
import { existsSync as existsSync16, mkdtempSync as mkdtempSync3, readFileSync as readFileSync14, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
|
|
5385
5430
|
import { tmpdir as tmpdir3 } from "os";
|
|
5386
5431
|
import { join as join13 } from "path";
|
|
5387
5432
|
var CANDIDATES = [
|
|
@@ -5407,7 +5452,7 @@ function buildTrustBundle(opts) {
|
|
|
5407
5452
|
const path2 = join13(dir, "bundle.pem");
|
|
5408
5453
|
const sys = readFileSync14(opts.systemCaPath, "utf-8");
|
|
5409
5454
|
const local = readFileSync14(opts.localCaPath, "utf-8");
|
|
5410
|
-
|
|
5455
|
+
writeFileSync10(path2, `${sys.trimEnd()}
|
|
5411
5456
|
${local.trimEnd()}
|
|
5412
5457
|
`, { mode: 384 });
|
|
5413
5458
|
return {
|
|
@@ -5436,10 +5481,10 @@ function resolveProxyConfigOptions() {
|
|
|
5436
5481
|
77
|
|
5437
5482
|
);
|
|
5438
5483
|
}
|
|
5439
|
-
|
|
5484
|
+
consola42.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5440
5485
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5441
5486
|
}
|
|
5442
|
-
var proxyCommand =
|
|
5487
|
+
var proxyCommand = defineCommand48({
|
|
5443
5488
|
meta: {
|
|
5444
5489
|
name: "proxy",
|
|
5445
5490
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -5463,8 +5508,8 @@ var proxyCommand = defineCommand47({
|
|
|
5463
5508
|
let close = null;
|
|
5464
5509
|
if (reuseHostPort) {
|
|
5465
5510
|
proxyUrl = `http://${reuseHostPort}`;
|
|
5466
|
-
|
|
5467
|
-
const localCaPath = join14(
|
|
5511
|
+
consola42.info(`[apes proxy] using long-running daemon at ${proxyUrl}`);
|
|
5512
|
+
const localCaPath = join14(homedir12(), ".openape", "proxy", "ca.crt");
|
|
5468
5513
|
if (!existsSync17(localCaPath)) {
|
|
5469
5514
|
throw new CliError(
|
|
5470
5515
|
`OPENAPE_PROXY is set but no local CA found at ${localCaPath}. Start the daemon (sudo -u <agent> apes proxy --global < secrets.toml) first.`
|
|
@@ -5474,15 +5519,15 @@ var proxyCommand = defineCommand47({
|
|
|
5474
5519
|
systemCaPath: detectSystemCaPath(),
|
|
5475
5520
|
localCaPath
|
|
5476
5521
|
});
|
|
5477
|
-
|
|
5522
|
+
consola42.debug(`[apes proxy] trust bundle: ${bundle.path}`);
|
|
5478
5523
|
} else if (reuseUrl) {
|
|
5479
5524
|
proxyUrl = reuseUrl;
|
|
5480
|
-
|
|
5525
|
+
consola42.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5481
5526
|
} else {
|
|
5482
5527
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5483
5528
|
proxyUrl = ephemeral.url;
|
|
5484
5529
|
close = ephemeral.close;
|
|
5485
|
-
|
|
5530
|
+
consola42.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5486
5531
|
}
|
|
5487
5532
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5488
5533
|
const childEnv = {
|
|
@@ -5523,7 +5568,7 @@ var proxyCommand = defineCommand47({
|
|
|
5523
5568
|
else resolveExit(code ?? 0);
|
|
5524
5569
|
});
|
|
5525
5570
|
child.once("error", (err) => {
|
|
5526
|
-
|
|
5571
|
+
consola42.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5527
5572
|
resolveExit(127);
|
|
5528
5573
|
});
|
|
5529
5574
|
});
|
|
@@ -5540,8 +5585,8 @@ function signalNumber(signal) {
|
|
|
5540
5585
|
}
|
|
5541
5586
|
|
|
5542
5587
|
// src/commands/explain.ts
|
|
5543
|
-
import { defineCommand as
|
|
5544
|
-
var explainCommand =
|
|
5588
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
5589
|
+
var explainCommand = defineCommand49({
|
|
5545
5590
|
meta: {
|
|
5546
5591
|
name: "explain",
|
|
5547
5592
|
description: "Show what permission a command would need"
|
|
@@ -5579,9 +5624,9 @@ var explainCommand = defineCommand48({
|
|
|
5579
5624
|
});
|
|
5580
5625
|
|
|
5581
5626
|
// src/commands/config/get.ts
|
|
5582
|
-
import { defineCommand as
|
|
5583
|
-
import
|
|
5584
|
-
var configGetCommand =
|
|
5627
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
5628
|
+
import consola43 from "consola";
|
|
5629
|
+
var configGetCommand = defineCommand50({
|
|
5585
5630
|
meta: {
|
|
5586
5631
|
name: "get",
|
|
5587
5632
|
description: "Get a configuration value"
|
|
@@ -5601,7 +5646,7 @@ var configGetCommand = defineCommand49({
|
|
|
5601
5646
|
if (idp)
|
|
5602
5647
|
console.log(idp);
|
|
5603
5648
|
else
|
|
5604
|
-
|
|
5649
|
+
consola43.info("No IdP configured.");
|
|
5605
5650
|
break;
|
|
5606
5651
|
}
|
|
5607
5652
|
case "email": {
|
|
@@ -5609,7 +5654,7 @@ var configGetCommand = defineCommand49({
|
|
|
5609
5654
|
if (auth?.email)
|
|
5610
5655
|
console.log(auth.email);
|
|
5611
5656
|
else
|
|
5612
|
-
|
|
5657
|
+
consola43.info("Not logged in.");
|
|
5613
5658
|
break;
|
|
5614
5659
|
}
|
|
5615
5660
|
default: {
|
|
@@ -5622,7 +5667,7 @@ var configGetCommand = defineCommand49({
|
|
|
5622
5667
|
if (sectionObj && field in sectionObj) {
|
|
5623
5668
|
console.log(sectionObj[field]);
|
|
5624
5669
|
} else {
|
|
5625
|
-
|
|
5670
|
+
consola43.info(`Key "${key}" not set.`);
|
|
5626
5671
|
}
|
|
5627
5672
|
} else {
|
|
5628
5673
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5633,9 +5678,9 @@ var configGetCommand = defineCommand49({
|
|
|
5633
5678
|
});
|
|
5634
5679
|
|
|
5635
5680
|
// src/commands/config/set.ts
|
|
5636
|
-
import { defineCommand as
|
|
5637
|
-
import
|
|
5638
|
-
var configSetCommand =
|
|
5681
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
5682
|
+
import consola44 from "consola";
|
|
5683
|
+
var configSetCommand = defineCommand51({
|
|
5639
5684
|
meta: {
|
|
5640
5685
|
name: "set",
|
|
5641
5686
|
description: "Set a configuration value"
|
|
@@ -5671,12 +5716,12 @@ var configSetCommand = defineCommand50({
|
|
|
5671
5716
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5672
5717
|
}
|
|
5673
5718
|
saveConfig(config);
|
|
5674
|
-
|
|
5719
|
+
consola44.success(`Set ${key} = ${value}`);
|
|
5675
5720
|
}
|
|
5676
5721
|
});
|
|
5677
5722
|
|
|
5678
5723
|
// src/commands/fetch/index.ts
|
|
5679
|
-
import { defineCommand as
|
|
5724
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
5680
5725
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5681
5726
|
const token = getAuthToken();
|
|
5682
5727
|
if (!token) {
|
|
@@ -5712,13 +5757,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5712
5757
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5713
5758
|
}
|
|
5714
5759
|
}
|
|
5715
|
-
var fetchCommand =
|
|
5760
|
+
var fetchCommand = defineCommand52({
|
|
5716
5761
|
meta: {
|
|
5717
5762
|
name: "fetch",
|
|
5718
5763
|
description: "Make authenticated HTTP requests"
|
|
5719
5764
|
},
|
|
5720
5765
|
subCommands: {
|
|
5721
|
-
get:
|
|
5766
|
+
get: defineCommand52({
|
|
5722
5767
|
meta: {
|
|
5723
5768
|
name: "get",
|
|
5724
5769
|
description: "GET request with auth token"
|
|
@@ -5744,7 +5789,7 @@ var fetchCommand = defineCommand51({
|
|
|
5744
5789
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5745
5790
|
}
|
|
5746
5791
|
}),
|
|
5747
|
-
post:
|
|
5792
|
+
post: defineCommand52({
|
|
5748
5793
|
meta: {
|
|
5749
5794
|
name: "post",
|
|
5750
5795
|
description: "POST request with auth token"
|
|
@@ -5783,8 +5828,8 @@ var fetchCommand = defineCommand51({
|
|
|
5783
5828
|
});
|
|
5784
5829
|
|
|
5785
5830
|
// src/commands/mcp/index.ts
|
|
5786
|
-
import { defineCommand as
|
|
5787
|
-
var mcpCommand =
|
|
5831
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
5832
|
+
var mcpCommand = defineCommand53({
|
|
5788
5833
|
meta: {
|
|
5789
5834
|
name: "mcp",
|
|
5790
5835
|
description: "Start MCP server for AI agents"
|
|
@@ -5807,18 +5852,18 @@ var mcpCommand = defineCommand52({
|
|
|
5807
5852
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5808
5853
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5809
5854
|
}
|
|
5810
|
-
const { startMcpServer } = await import("./server-
|
|
5855
|
+
const { startMcpServer } = await import("./server-32CG5WG2.js");
|
|
5811
5856
|
await startMcpServer(transport, port);
|
|
5812
5857
|
}
|
|
5813
5858
|
});
|
|
5814
5859
|
|
|
5815
5860
|
// src/commands/init/index.ts
|
|
5816
|
-
import { existsSync as existsSync18, copyFileSync, writeFileSync as
|
|
5861
|
+
import { existsSync as existsSync18, copyFileSync, writeFileSync as writeFileSync11 } from "fs";
|
|
5817
5862
|
import { randomBytes } from "crypto";
|
|
5818
|
-
import { execFileSync as
|
|
5863
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
5819
5864
|
import { join as join15 } from "path";
|
|
5820
|
-
import { defineCommand as
|
|
5821
|
-
import
|
|
5865
|
+
import { defineCommand as defineCommand54 } from "citty";
|
|
5866
|
+
import consola45 from "consola";
|
|
5822
5867
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5823
5868
|
async function downloadTemplate(repo, targetDir) {
|
|
5824
5869
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
@@ -5827,28 +5872,28 @@ async function downloadTemplate(repo, targetDir) {
|
|
|
5827
5872
|
function installDeps(dir) {
|
|
5828
5873
|
const hasLockFile = (name) => existsSync18(join15(dir, name));
|
|
5829
5874
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
5830
|
-
|
|
5875
|
+
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5831
5876
|
} else if (hasLockFile("bun.lockb")) {
|
|
5832
|
-
|
|
5877
|
+
execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5833
5878
|
} else {
|
|
5834
|
-
|
|
5879
|
+
execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5835
5880
|
}
|
|
5836
5881
|
}
|
|
5837
5882
|
async function promptChoice(message, choices) {
|
|
5838
|
-
const result = await
|
|
5883
|
+
const result = await consola45.prompt(message, { type: "select", options: choices });
|
|
5839
5884
|
if (typeof result === "symbol") {
|
|
5840
5885
|
throw new CliExit(0);
|
|
5841
5886
|
}
|
|
5842
5887
|
return result;
|
|
5843
5888
|
}
|
|
5844
5889
|
async function promptText(message, defaultValue) {
|
|
5845
|
-
const result = await
|
|
5890
|
+
const result = await consola45.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5846
5891
|
if (typeof result === "symbol") {
|
|
5847
5892
|
throw new CliExit(0);
|
|
5848
5893
|
}
|
|
5849
5894
|
return result || defaultValue || "";
|
|
5850
5895
|
}
|
|
5851
|
-
var initCommand =
|
|
5896
|
+
var initCommand = defineCommand54({
|
|
5852
5897
|
meta: {
|
|
5853
5898
|
name: "init",
|
|
5854
5899
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5893,20 +5938,20 @@ async function initSP(targetDir) {
|
|
|
5893
5938
|
if (existsSync18(join15(dir, "package.json"))) {
|
|
5894
5939
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5895
5940
|
}
|
|
5896
|
-
|
|
5941
|
+
consola45.start("Scaffolding SP starter...");
|
|
5897
5942
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5898
|
-
|
|
5899
|
-
|
|
5943
|
+
consola45.success("Scaffolded from openape-sp-starter");
|
|
5944
|
+
consola45.start("Installing dependencies...");
|
|
5900
5945
|
installDeps(dir);
|
|
5901
|
-
|
|
5946
|
+
consola45.success("Dependencies installed");
|
|
5902
5947
|
const envExample = join15(dir, ".env.example");
|
|
5903
5948
|
const envFile = join15(dir, ".env");
|
|
5904
5949
|
if (existsSync18(envExample) && !existsSync18(envFile)) {
|
|
5905
5950
|
copyFileSync(envExample, envFile);
|
|
5906
|
-
|
|
5951
|
+
consola45.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5907
5952
|
}
|
|
5908
5953
|
console.log("");
|
|
5909
|
-
|
|
5954
|
+
consola45.box([
|
|
5910
5955
|
`cd ${dir}`,
|
|
5911
5956
|
"npm run dev",
|
|
5912
5957
|
"",
|
|
@@ -5925,15 +5970,15 @@ async function initIdP(targetDir) {
|
|
|
5925
5970
|
"s3 (S3-compatible)"
|
|
5926
5971
|
]);
|
|
5927
5972
|
const adminEmail = await promptText("Admin email");
|
|
5928
|
-
|
|
5973
|
+
consola45.start("Scaffolding IdP starter...");
|
|
5929
5974
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5930
|
-
|
|
5931
|
-
|
|
5975
|
+
consola45.success("Scaffolded from openape-idp-starter");
|
|
5976
|
+
consola45.start("Installing dependencies...");
|
|
5932
5977
|
installDeps(dir);
|
|
5933
|
-
|
|
5978
|
+
consola45.success("Dependencies installed");
|
|
5934
5979
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5935
5980
|
const managementToken = randomBytes(32).toString("hex");
|
|
5936
|
-
|
|
5981
|
+
consola45.success("Secrets generated");
|
|
5937
5982
|
const isLocalhost = domain === "localhost";
|
|
5938
5983
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5939
5984
|
const envContent = [
|
|
@@ -5947,11 +5992,11 @@ async function initIdP(targetDir) {
|
|
|
5947
5992
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
5948
5993
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
5949
5994
|
].join("\n");
|
|
5950
|
-
|
|
5995
|
+
writeFileSync11(join15(dir, ".env"), `${envContent}
|
|
5951
5996
|
`, { mode: 384 });
|
|
5952
|
-
|
|
5997
|
+
consola45.success(".env created");
|
|
5953
5998
|
console.log("");
|
|
5954
|
-
|
|
5999
|
+
consola45.box([
|
|
5955
6000
|
`cd ${dir}`,
|
|
5956
6001
|
"npm run dev",
|
|
5957
6002
|
"",
|
|
@@ -5967,12 +6012,12 @@ async function initIdP(targetDir) {
|
|
|
5967
6012
|
}
|
|
5968
6013
|
|
|
5969
6014
|
// src/commands/enroll.ts
|
|
5970
|
-
import { Buffer as
|
|
6015
|
+
import { Buffer as Buffer4 } from "buffer";
|
|
5971
6016
|
import { existsSync as existsSync19, readFileSync as readFileSync15 } from "fs";
|
|
5972
6017
|
import { execFile as execFile2 } from "child_process";
|
|
5973
6018
|
import { sign as sign2 } from "crypto";
|
|
5974
|
-
import { defineCommand as
|
|
5975
|
-
import
|
|
6019
|
+
import { defineCommand as defineCommand55 } from "citty";
|
|
6020
|
+
import consola46 from "consola";
|
|
5976
6021
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5977
6022
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5978
6023
|
var POLL_INTERVAL = 3e3;
|
|
@@ -5998,7 +6043,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
5998
6043
|
});
|
|
5999
6044
|
if (challengeResp.ok) {
|
|
6000
6045
|
const { challenge } = await challengeResp.json();
|
|
6001
|
-
const signature = sign2(null,
|
|
6046
|
+
const signature = sign2(null, Buffer4.from(challenge), privateKey).toString("base64");
|
|
6002
6047
|
const authResp = await fetch(authenticateUrl, {
|
|
6003
6048
|
method: "POST",
|
|
6004
6049
|
headers: { "Content-Type": "application/json" },
|
|
@@ -6011,11 +6056,11 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
6011
6056
|
}
|
|
6012
6057
|
} catch {
|
|
6013
6058
|
}
|
|
6014
|
-
await new Promise((
|
|
6059
|
+
await new Promise((resolve5) => setTimeout(resolve5, POLL_INTERVAL));
|
|
6015
6060
|
}
|
|
6016
6061
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
6017
6062
|
}
|
|
6018
|
-
var enrollCommand =
|
|
6063
|
+
var enrollCommand = defineCommand55({
|
|
6019
6064
|
meta: {
|
|
6020
6065
|
name: "enroll",
|
|
6021
6066
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -6035,18 +6080,18 @@ var enrollCommand = defineCommand54({
|
|
|
6035
6080
|
}
|
|
6036
6081
|
},
|
|
6037
6082
|
async run({ args }) {
|
|
6038
|
-
const idp = args.idp || await
|
|
6083
|
+
const idp = args.idp || await consola46.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
|
|
6039
6084
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6040
6085
|
return r;
|
|
6041
6086
|
}) || DEFAULT_IDP_URL2;
|
|
6042
|
-
const agentName = args.name || await
|
|
6087
|
+
const agentName = args.name || await consola46.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
6043
6088
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6044
6089
|
return r;
|
|
6045
6090
|
});
|
|
6046
6091
|
if (!agentName) {
|
|
6047
6092
|
throw new CliError("Agent name is required.");
|
|
6048
6093
|
}
|
|
6049
|
-
const keyPath = args.key || await
|
|
6094
|
+
const keyPath = args.key || await consola46.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
|
|
6050
6095
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6051
6096
|
return r;
|
|
6052
6097
|
}) || DEFAULT_KEY_PATH;
|
|
@@ -6054,19 +6099,19 @@ var enrollCommand = defineCommand54({
|
|
|
6054
6099
|
let publicKey;
|
|
6055
6100
|
if (existsSync19(resolvedKey)) {
|
|
6056
6101
|
publicKey = readPublicKey(resolvedKey);
|
|
6057
|
-
|
|
6102
|
+
consola46.success(`Using existing key ${keyPath}`);
|
|
6058
6103
|
} else {
|
|
6059
|
-
|
|
6104
|
+
consola46.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
6060
6105
|
publicKey = generateAndSaveKey(keyPath);
|
|
6061
|
-
|
|
6106
|
+
consola46.success(`Key pair generated at ${keyPath}`);
|
|
6062
6107
|
}
|
|
6063
6108
|
const encodedKey = encodeURIComponent(publicKey);
|
|
6064
6109
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
6065
|
-
|
|
6066
|
-
|
|
6110
|
+
consola46.info("Opening browser for enrollment...");
|
|
6111
|
+
consola46.info(`\u2192 ${idp}/enroll`);
|
|
6067
6112
|
openBrowser2(enrollUrl);
|
|
6068
6113
|
console.log("");
|
|
6069
|
-
const agentEmail = await
|
|
6114
|
+
const agentEmail = await consola46.prompt(
|
|
6070
6115
|
"Agent email (shown in browser after enrollment)",
|
|
6071
6116
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
6072
6117
|
).then((r) => {
|
|
@@ -6076,7 +6121,7 @@ var enrollCommand = defineCommand54({
|
|
|
6076
6121
|
if (!agentEmail) {
|
|
6077
6122
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
6078
6123
|
}
|
|
6079
|
-
|
|
6124
|
+
consola46.start("Verifying enrollment...");
|
|
6080
6125
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
6081
6126
|
saveAuth({
|
|
6082
6127
|
idp,
|
|
@@ -6088,18 +6133,18 @@ var enrollCommand = defineCommand54({
|
|
|
6088
6133
|
config.defaults = { ...config.defaults, idp };
|
|
6089
6134
|
config.agent = { key: keyPath, email: agentEmail };
|
|
6090
6135
|
saveConfig(config);
|
|
6091
|
-
|
|
6092
|
-
|
|
6136
|
+
consola46.success(`Agent enrolled as ${agentEmail}`);
|
|
6137
|
+
consola46.success("Config saved to ~/.config/apes/");
|
|
6093
6138
|
console.log("");
|
|
6094
|
-
|
|
6139
|
+
consola46.info("Verify with: apes whoami");
|
|
6095
6140
|
}
|
|
6096
6141
|
});
|
|
6097
6142
|
|
|
6098
6143
|
// src/commands/register-user.ts
|
|
6099
6144
|
import { existsSync as existsSync20, readFileSync as readFileSync16 } from "fs";
|
|
6100
|
-
import { defineCommand as
|
|
6101
|
-
import
|
|
6102
|
-
var registerUserCommand =
|
|
6145
|
+
import { defineCommand as defineCommand56 } from "citty";
|
|
6146
|
+
import consola47 from "consola";
|
|
6147
|
+
var registerUserCommand = defineCommand56({
|
|
6103
6148
|
meta: {
|
|
6104
6149
|
name: "register-user",
|
|
6105
6150
|
description: "Register a sub-user with SSH key"
|
|
@@ -6154,18 +6199,18 @@ var registerUserCommand = defineCommand55({
|
|
|
6154
6199
|
...userType ? { type: userType } : {}
|
|
6155
6200
|
}
|
|
6156
6201
|
});
|
|
6157
|
-
|
|
6202
|
+
consola47.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
6158
6203
|
}
|
|
6159
6204
|
});
|
|
6160
6205
|
|
|
6161
6206
|
// src/commands/utils/index.ts
|
|
6162
|
-
import { defineCommand as
|
|
6207
|
+
import { defineCommand as defineCommand58 } from "citty";
|
|
6163
6208
|
|
|
6164
6209
|
// src/commands/utils/dig.ts
|
|
6165
|
-
import { defineCommand as
|
|
6166
|
-
import
|
|
6210
|
+
import { defineCommand as defineCommand57 } from "citty";
|
|
6211
|
+
import consola48 from "consola";
|
|
6167
6212
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
6168
|
-
var digCommand =
|
|
6213
|
+
var digCommand = defineCommand57({
|
|
6169
6214
|
meta: {
|
|
6170
6215
|
name: "dig",
|
|
6171
6216
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -6238,12 +6283,12 @@ var digCommand = defineCommand56({
|
|
|
6238
6283
|
console.log(` domain: ${domain}`);
|
|
6239
6284
|
console.log("");
|
|
6240
6285
|
if (!result.ddisa.found) {
|
|
6241
|
-
|
|
6286
|
+
consola48.warn(`No DDISA record at _ddisa.${domain}`);
|
|
6242
6287
|
if (result.hint) console.log(`
|
|
6243
6288
|
${result.hint}`);
|
|
6244
6289
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6245
6290
|
}
|
|
6246
|
-
|
|
6291
|
+
consola48.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
6247
6292
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
6248
6293
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
6249
6294
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -6253,13 +6298,13 @@ ${result.hint}`);
|
|
|
6253
6298
|
return;
|
|
6254
6299
|
}
|
|
6255
6300
|
if (result.idpDiscovery.ok) {
|
|
6256
|
-
|
|
6301
|
+
consola48.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
6257
6302
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
6258
6303
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
6259
6304
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
6260
6305
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
6261
6306
|
} else {
|
|
6262
|
-
|
|
6307
|
+
consola48.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
6263
6308
|
if (result.hint) console.log(`
|
|
6264
6309
|
${result.hint}`);
|
|
6265
6310
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -6268,7 +6313,7 @@ ${result.hint}`);
|
|
|
6268
6313
|
});
|
|
6269
6314
|
|
|
6270
6315
|
// src/commands/utils/index.ts
|
|
6271
|
-
var utilsCommand =
|
|
6316
|
+
var utilsCommand = defineCommand58({
|
|
6272
6317
|
meta: {
|
|
6273
6318
|
name: "utils",
|
|
6274
6319
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -6279,12 +6324,12 @@ var utilsCommand = defineCommand57({
|
|
|
6279
6324
|
});
|
|
6280
6325
|
|
|
6281
6326
|
// src/commands/sessions/index.ts
|
|
6282
|
-
import { defineCommand as
|
|
6327
|
+
import { defineCommand as defineCommand61 } from "citty";
|
|
6283
6328
|
|
|
6284
6329
|
// src/commands/sessions/list.ts
|
|
6285
|
-
import { defineCommand as
|
|
6286
|
-
import
|
|
6287
|
-
var sessionsListCommand =
|
|
6330
|
+
import { defineCommand as defineCommand59 } from "citty";
|
|
6331
|
+
import consola49 from "consola";
|
|
6332
|
+
var sessionsListCommand = defineCommand59({
|
|
6288
6333
|
meta: {
|
|
6289
6334
|
name: "list",
|
|
6290
6335
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6302,7 +6347,7 @@ var sessionsListCommand = defineCommand58({
|
|
|
6302
6347
|
return;
|
|
6303
6348
|
}
|
|
6304
6349
|
if (result.data.length === 0) {
|
|
6305
|
-
|
|
6350
|
+
consola49.info("No active sessions.");
|
|
6306
6351
|
return;
|
|
6307
6352
|
}
|
|
6308
6353
|
for (const f of result.data) {
|
|
@@ -6314,9 +6359,9 @@ var sessionsListCommand = defineCommand58({
|
|
|
6314
6359
|
});
|
|
6315
6360
|
|
|
6316
6361
|
// src/commands/sessions/remove.ts
|
|
6317
|
-
import { defineCommand as
|
|
6318
|
-
import
|
|
6319
|
-
var sessionsRemoveCommand =
|
|
6362
|
+
import { defineCommand as defineCommand60 } from "citty";
|
|
6363
|
+
import consola50 from "consola";
|
|
6364
|
+
var sessionsRemoveCommand = defineCommand60({
|
|
6320
6365
|
meta: {
|
|
6321
6366
|
name: "remove",
|
|
6322
6367
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6332,12 +6377,12 @@ var sessionsRemoveCommand = defineCommand59({
|
|
|
6332
6377
|
const id = String(args.familyId).trim();
|
|
6333
6378
|
if (!id) throw new CliError("familyId required");
|
|
6334
6379
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6335
|
-
|
|
6380
|
+
consola50.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6336
6381
|
}
|
|
6337
6382
|
});
|
|
6338
6383
|
|
|
6339
6384
|
// src/commands/sessions/index.ts
|
|
6340
|
-
var sessionsCommand =
|
|
6385
|
+
var sessionsCommand = defineCommand61({
|
|
6341
6386
|
meta: {
|
|
6342
6387
|
name: "sessions",
|
|
6343
6388
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6349,10 +6394,10 @@ var sessionsCommand = defineCommand60({
|
|
|
6349
6394
|
});
|
|
6350
6395
|
|
|
6351
6396
|
// src/commands/dns-check.ts
|
|
6352
|
-
import { defineCommand as
|
|
6353
|
-
import
|
|
6397
|
+
import { defineCommand as defineCommand62 } from "citty";
|
|
6398
|
+
import consola51 from "consola";
|
|
6354
6399
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6355
|
-
var dnsCheckCommand =
|
|
6400
|
+
var dnsCheckCommand = defineCommand62({
|
|
6356
6401
|
meta: {
|
|
6357
6402
|
name: "dns-check",
|
|
6358
6403
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6366,7 +6411,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6366
6411
|
},
|
|
6367
6412
|
async run({ args }) {
|
|
6368
6413
|
const domain = args.domain;
|
|
6369
|
-
|
|
6414
|
+
consola51.start(`Checking _ddisa.${domain}...`);
|
|
6370
6415
|
try {
|
|
6371
6416
|
const result = await resolveDDISA3(domain);
|
|
6372
6417
|
if (!result) {
|
|
@@ -6375,7 +6420,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6375
6420
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
6376
6421
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6377
6422
|
}
|
|
6378
|
-
|
|
6423
|
+
consola51.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
6379
6424
|
console.log("");
|
|
6380
6425
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
6381
6426
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -6384,14 +6429,14 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6384
6429
|
if (result.priority !== void 0)
|
|
6385
6430
|
console.log(` Priority: ${result.priority}`);
|
|
6386
6431
|
console.log("");
|
|
6387
|
-
|
|
6432
|
+
consola51.start(`Verifying IdP at ${result.idp}...`);
|
|
6388
6433
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
6389
6434
|
if (!discoResp.ok) {
|
|
6390
|
-
|
|
6435
|
+
consola51.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
6391
6436
|
return;
|
|
6392
6437
|
}
|
|
6393
6438
|
const disco = await discoResp.json();
|
|
6394
|
-
|
|
6439
|
+
consola51.success(`IdP is reachable`);
|
|
6395
6440
|
console.log(` Issuer: ${disco.issuer}`);
|
|
6396
6441
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
6397
6442
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -6409,7 +6454,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6409
6454
|
// src/commands/health.ts
|
|
6410
6455
|
import { exec } from "child_process";
|
|
6411
6456
|
import { promisify } from "util";
|
|
6412
|
-
import { defineCommand as
|
|
6457
|
+
import { defineCommand as defineCommand63 } from "citty";
|
|
6413
6458
|
var execAsync = promisify(exec);
|
|
6414
6459
|
async function resolveApeShellPath() {
|
|
6415
6460
|
try {
|
|
@@ -6445,7 +6490,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6445
6490
|
}
|
|
6446
6491
|
}
|
|
6447
6492
|
async function runHealth(args) {
|
|
6448
|
-
const version = true ? "1.
|
|
6493
|
+
const version = true ? "1.31.1" : "0.0.0";
|
|
6449
6494
|
const auth = loadAuth();
|
|
6450
6495
|
if (!auth) {
|
|
6451
6496
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6508,7 +6553,7 @@ async function runHealth(args) {
|
|
|
6508
6553
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
6509
6554
|
}
|
|
6510
6555
|
}
|
|
6511
|
-
var healthCommand =
|
|
6556
|
+
var healthCommand = defineCommand63({
|
|
6512
6557
|
meta: {
|
|
6513
6558
|
name: "health",
|
|
6514
6559
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -6526,8 +6571,8 @@ var healthCommand = defineCommand62({
|
|
|
6526
6571
|
});
|
|
6527
6572
|
|
|
6528
6573
|
// src/commands/workflows.ts
|
|
6529
|
-
import { defineCommand as
|
|
6530
|
-
import
|
|
6574
|
+
import { defineCommand as defineCommand64 } from "citty";
|
|
6575
|
+
import consola52 from "consola";
|
|
6531
6576
|
|
|
6532
6577
|
// src/guides/index.ts
|
|
6533
6578
|
var guides = [
|
|
@@ -6577,7 +6622,7 @@ var guides = [
|
|
|
6577
6622
|
];
|
|
6578
6623
|
|
|
6579
6624
|
// src/commands/workflows.ts
|
|
6580
|
-
var workflowsCommand =
|
|
6625
|
+
var workflowsCommand = defineCommand64({
|
|
6581
6626
|
meta: {
|
|
6582
6627
|
name: "workflows",
|
|
6583
6628
|
description: "Discover workflow guides"
|
|
@@ -6598,7 +6643,7 @@ var workflowsCommand = defineCommand63({
|
|
|
6598
6643
|
if (args.id) {
|
|
6599
6644
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6600
6645
|
if (!guide) {
|
|
6601
|
-
|
|
6646
|
+
consola52.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6602
6647
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6603
6648
|
}
|
|
6604
6649
|
if (args.json) {
|
|
@@ -6638,13 +6683,13 @@ var workflowsCommand = defineCommand63({
|
|
|
6638
6683
|
});
|
|
6639
6684
|
|
|
6640
6685
|
// src/version-check.ts
|
|
6641
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as
|
|
6642
|
-
import { homedir as
|
|
6686
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync12 } from "fs";
|
|
6687
|
+
import { homedir as homedir13 } from "os";
|
|
6643
6688
|
import { join as join16 } from "path";
|
|
6644
|
-
import
|
|
6689
|
+
import consola53 from "consola";
|
|
6645
6690
|
var PACKAGE_NAME = "@openape/apes";
|
|
6646
6691
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6647
|
-
var CACHE_FILE = join16(
|
|
6692
|
+
var CACHE_FILE = join16(homedir13(), ".config", "apes", ".version-check.json");
|
|
6648
6693
|
function readCache() {
|
|
6649
6694
|
if (!existsSync21(CACHE_FILE)) return null;
|
|
6650
6695
|
try {
|
|
@@ -6655,9 +6700,9 @@ function readCache() {
|
|
|
6655
6700
|
}
|
|
6656
6701
|
function writeCache(entry) {
|
|
6657
6702
|
try {
|
|
6658
|
-
const dir = join16(
|
|
6703
|
+
const dir = join16(homedir13(), ".config", "apes");
|
|
6659
6704
|
if (!existsSync21(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
6660
|
-
|
|
6705
|
+
writeFileSync12(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
6661
6706
|
} catch {
|
|
6662
6707
|
}
|
|
6663
6708
|
}
|
|
@@ -6686,7 +6731,7 @@ async function fetchLatestVersion() {
|
|
|
6686
6731
|
}
|
|
6687
6732
|
function warnIfBehind(currentVersion, latest) {
|
|
6688
6733
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6689
|
-
|
|
6734
|
+
consola53.warn(
|
|
6690
6735
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
6691
6736
|
);
|
|
6692
6737
|
}
|
|
@@ -6718,10 +6763,10 @@ if (shellRewrite) {
|
|
|
6718
6763
|
if (shellRewrite.action === "rewrite") {
|
|
6719
6764
|
process.argv = shellRewrite.argv;
|
|
6720
6765
|
} else if (shellRewrite.action === "version") {
|
|
6721
|
-
console.log(`ape-shell ${"1.
|
|
6766
|
+
console.log(`ape-shell ${"1.31.1"} (OpenApe DDISA shell wrapper)`);
|
|
6722
6767
|
process.exit(0);
|
|
6723
6768
|
} else if (shellRewrite.action === "help") {
|
|
6724
|
-
console.log(`ape-shell ${"1.
|
|
6769
|
+
console.log(`ape-shell ${"1.31.1"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6725
6770
|
console.log("");
|
|
6726
6771
|
console.log("Usage:");
|
|
6727
6772
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6736,7 +6781,7 @@ if (shellRewrite) {
|
|
|
6736
6781
|
console.log(" --help, -h Show this help message");
|
|
6737
6782
|
process.exit(0);
|
|
6738
6783
|
} else if (shellRewrite.action === "interactive") {
|
|
6739
|
-
const { runInteractiveShell } = await import("./orchestrator-
|
|
6784
|
+
const { runInteractiveShell } = await import("./orchestrator-6PZXCE54.js");
|
|
6740
6785
|
await runInteractiveShell();
|
|
6741
6786
|
process.exit(0);
|
|
6742
6787
|
} else {
|
|
@@ -6745,7 +6790,7 @@ if (shellRewrite) {
|
|
|
6745
6790
|
}
|
|
6746
6791
|
}
|
|
6747
6792
|
var debug = process.argv.includes("--debug");
|
|
6748
|
-
var grantsCommand =
|
|
6793
|
+
var grantsCommand = defineCommand65({
|
|
6749
6794
|
meta: {
|
|
6750
6795
|
name: "grants",
|
|
6751
6796
|
description: "Grant management"
|
|
@@ -6763,10 +6808,11 @@ var grantsCommand = defineCommand64({
|
|
|
6763
6808
|
token: tokenCommand,
|
|
6764
6809
|
delegate: delegateCommand,
|
|
6765
6810
|
delegations: delegationsCommand,
|
|
6766
|
-
"delegation-revoke": delegationRevokeCommand
|
|
6811
|
+
"delegation-revoke": delegationRevokeCommand,
|
|
6812
|
+
llm: llmCommand
|
|
6767
6813
|
}
|
|
6768
6814
|
});
|
|
6769
|
-
var configCommand =
|
|
6815
|
+
var configCommand = defineCommand65({
|
|
6770
6816
|
meta: {
|
|
6771
6817
|
name: "config",
|
|
6772
6818
|
description: "Configuration management"
|
|
@@ -6776,10 +6822,10 @@ var configCommand = defineCommand64({
|
|
|
6776
6822
|
set: configSetCommand
|
|
6777
6823
|
}
|
|
6778
6824
|
});
|
|
6779
|
-
var main =
|
|
6825
|
+
var main = defineCommand65({
|
|
6780
6826
|
meta: {
|
|
6781
6827
|
name: "apes",
|
|
6782
|
-
version: "1.
|
|
6828
|
+
version: "1.31.1",
|
|
6783
6829
|
description: "Unified CLI for OpenApe"
|
|
6784
6830
|
},
|
|
6785
6831
|
subCommands: {
|
|
@@ -6831,26 +6877,26 @@ async function maybeRefreshAuth() {
|
|
|
6831
6877
|
const { loadAuth: loadAuth2 } = await import("./config-MOB5DJ6H.js");
|
|
6832
6878
|
if (!loadAuth2()) return;
|
|
6833
6879
|
try {
|
|
6834
|
-
const { ensureFreshToken } = await import("./http-
|
|
6880
|
+
const { ensureFreshToken } = await import("./http-SILH37L7.js");
|
|
6835
6881
|
await ensureFreshToken();
|
|
6836
6882
|
} catch {
|
|
6837
6883
|
}
|
|
6838
6884
|
}
|
|
6839
6885
|
await maybeRefreshAuth();
|
|
6840
|
-
await maybeWarnStaleVersion("1.
|
|
6886
|
+
await maybeWarnStaleVersion("1.31.1").catch(() => {
|
|
6841
6887
|
});
|
|
6842
6888
|
runMain(main).catch((err) => {
|
|
6843
6889
|
if (err instanceof CliExit) {
|
|
6844
6890
|
process.exit(err.exitCode);
|
|
6845
6891
|
}
|
|
6846
6892
|
if (err instanceof CliError) {
|
|
6847
|
-
|
|
6893
|
+
consola54.error(err.message);
|
|
6848
6894
|
process.exit(err.exitCode);
|
|
6849
6895
|
}
|
|
6850
6896
|
if (debug) {
|
|
6851
|
-
|
|
6897
|
+
consola54.error(err);
|
|
6852
6898
|
} else {
|
|
6853
|
-
|
|
6899
|
+
consola54.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6854
6900
|
}
|
|
6855
6901
|
process.exit(1);
|
|
6856
6902
|
});
|