@openape/apes 1.31.0 → 1.31.2
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/cli.js +646 -648
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +30 -2
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/{server-MDXOGP2U.js → server-OSX5LE4W.js} +2 -2
- package/package.json +5 -5
- package/dist/chunk-QMMRZPD2.js +0 -47
- package/dist/chunk-QMMRZPD2.js.map +0 -1
- /package/dist/{server-MDXOGP2U.js.map → server-OSX5LE4W.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
|
|
@@ -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,7 +98,7 @@ 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
104
|
import { Buffer } from "buffer";
|
|
@@ -381,7 +384,7 @@ async function loginWithPKCE(idp) {
|
|
|
381
384
|
consola2.success(`Logged in as ${payload.email || payload.sub}`);
|
|
382
385
|
}
|
|
383
386
|
async function loginWithKey(idp, keyPath, agentEmail) {
|
|
384
|
-
const { readFileSync:
|
|
387
|
+
const { readFileSync: readFileSync18 } = await import("fs");
|
|
385
388
|
const { sign: sign3 } = await import("crypto");
|
|
386
389
|
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
|
|
387
390
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
@@ -395,7 +398,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
|
|
|
395
398
|
throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
|
|
396
399
|
}
|
|
397
400
|
const { challenge } = await challengeResp.json();
|
|
398
|
-
const keyContent =
|
|
401
|
+
const keyContent = readFileSync18(keyPath, "utf-8");
|
|
399
402
|
const privateKey = loadEd25519PrivateKey2(keyContent);
|
|
400
403
|
const signature = sign3(null, Buffer.from(challenge), privateKey).toString("base64");
|
|
401
404
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -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,12 +2039,12 @@ 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
2050
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -2403,7 +2469,7 @@ function getHostPlatform() {
|
|
|
2403
2469
|
}
|
|
2404
2470
|
|
|
2405
2471
|
// src/commands/agents/allow.ts
|
|
2406
|
-
var allowAgentCommand =
|
|
2472
|
+
var allowAgentCommand = defineCommand23({
|
|
2407
2473
|
meta: {
|
|
2408
2474
|
name: "allow",
|
|
2409
2475
|
description: "Add a peer to the agent's contact-allowlist so the bridge auto-accepts that peer's contact request."
|
|
@@ -2460,9 +2526,9 @@ print("ok")
|
|
|
2460
2526
|
PY
|
|
2461
2527
|
chmod 600 "$F"
|
|
2462
2528
|
`;
|
|
2463
|
-
|
|
2529
|
+
consola20.start(`Adding ${email} to ${agent}'s allowlist\u2026`);
|
|
2464
2530
|
execFileSync6(apes, ["run", "--as", agent, "--wait", "--", "bash", "-c", script], { stdio: "inherit" });
|
|
2465
|
-
|
|
2531
|
+
consola20.success(`${agent} will auto-accept future contact requests from ${email} (within ~30s of next bridge connect).`);
|
|
2466
2532
|
}
|
|
2467
2533
|
});
|
|
2468
2534
|
function shQuote2(s) {
|
|
@@ -2470,9 +2536,9 @@ function shQuote2(s) {
|
|
|
2470
2536
|
}
|
|
2471
2537
|
|
|
2472
2538
|
// src/commands/agents/cleanup-orphans.ts
|
|
2473
|
-
import { defineCommand as
|
|
2474
|
-
import
|
|
2475
|
-
var cleanupOrphansCommand =
|
|
2539
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
2540
|
+
import consola21 from "consola";
|
|
2541
|
+
var cleanupOrphansCommand = defineCommand24({
|
|
2476
2542
|
meta: {
|
|
2477
2543
|
name: "cleanup-orphans",
|
|
2478
2544
|
description: "Report agent-user tombstones. Linux userdel is atomic, so there are normally none."
|
|
@@ -2490,115 +2556,25 @@ var cleanupOrphansCommand = defineCommand23({
|
|
|
2490
2556
|
async run() {
|
|
2491
2557
|
const orphans = getHostPlatform().listOrphanAgentUsers();
|
|
2492
2558
|
if (orphans.length === 0) {
|
|
2493
|
-
|
|
2559
|
+
consola21.success("No agent tombstones \u2014 userdel is clean on Linux.");
|
|
2494
2560
|
return;
|
|
2495
2561
|
}
|
|
2496
|
-
|
|
2562
|
+
consola21.warn(`Found ${orphans.length} unexpected agent tombstone${orphans.length === 1 ? "" : "s"}:`);
|
|
2497
2563
|
for (const o of orphans) {
|
|
2498
2564
|
console.log(` \u2022 ${o.name}${o.uid !== null ? ` (uid=${o.uid})` : ""} \u2014 was ${o.homeDir}`);
|
|
2499
2565
|
}
|
|
2500
|
-
|
|
2566
|
+
consola21.info("Remove each one manually with `userdel -r <name>`.");
|
|
2501
2567
|
}
|
|
2502
2568
|
});
|
|
2503
2569
|
|
|
2504
2570
|
// src/commands/agents/code.ts
|
|
2505
|
-
import { existsSync as
|
|
2506
|
-
import { homedir as
|
|
2507
|
-
import { join as
|
|
2571
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
2572
|
+
import { homedir as homedir4 } from "os";
|
|
2573
|
+
import { join as join3 } from "path";
|
|
2508
2574
|
import process3 from "process";
|
|
2509
|
-
import { defineCommand as
|
|
2510
|
-
import { consola as
|
|
2575
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
2576
|
+
import { consola as consola22 } from "consola";
|
|
2511
2577
|
import { taskTools, runApeShell, runCodingTask, buildIssueGet, detectForge, createLlmReviewer, createLlmRiskAssessor, resolveMergePolicy } from "@openape/agent-runtime";
|
|
2512
|
-
|
|
2513
|
-
// src/lib/agent-secrets-runtime.ts
|
|
2514
|
-
import { existsSync as existsSync5, mkdirSync, readdirSync, readFileSync as readFileSync4, statSync, watch, writeFileSync as writeFileSync3 } from "fs";
|
|
2515
|
-
import { homedir as homedir4 } from "os";
|
|
2516
|
-
import { dirname, join as join3 } from "path";
|
|
2517
|
-
import { openString } from "@openape/core";
|
|
2518
|
-
var CONFIG_DIR2 = join3(homedir4(), ".config", "openape");
|
|
2519
|
-
var SECRETS_DIR = join3(CONFIG_DIR2, "secrets.d");
|
|
2520
|
-
var X25519_KEY_PATH = join3(CONFIG_DIR2, "agent-x25519.key");
|
|
2521
|
-
var X25519_PUBKEY_PATH = `${X25519_KEY_PATH}.pub`;
|
|
2522
|
-
function envNameFromFile(file) {
|
|
2523
|
-
if (!file.endsWith(".blob")) return null;
|
|
2524
|
-
const env = file.slice(0, -".blob".length);
|
|
2525
|
-
return /^[A-Z][A-Z0-9_]*$/.test(env) ? env : null;
|
|
2526
|
-
}
|
|
2527
|
-
function readAgentEncryptionKey(keyPath = X25519_KEY_PATH) {
|
|
2528
|
-
if (!existsSync5(keyPath)) return null;
|
|
2529
|
-
const k = readFileSync4(keyPath, "utf8").trim();
|
|
2530
|
-
return k.length > 0 ? k : null;
|
|
2531
|
-
}
|
|
2532
|
-
function readAgentEncryptionPublicKey(pubPath = X25519_PUBKEY_PATH) {
|
|
2533
|
-
if (!existsSync5(pubPath)) return null;
|
|
2534
|
-
const k = readFileSync4(pubPath, "utf8").trim();
|
|
2535
|
-
return k.length > 0 ? k : null;
|
|
2536
|
-
}
|
|
2537
|
-
function materializeSecrets(opts = {}) {
|
|
2538
|
-
const dir = opts.dir ?? SECRETS_DIR;
|
|
2539
|
-
const env = opts.env ?? process.env;
|
|
2540
|
-
const log = opts.log ?? (() => {
|
|
2541
|
-
});
|
|
2542
|
-
const applied = [];
|
|
2543
|
-
const failed = [];
|
|
2544
|
-
const key = readAgentEncryptionKey(opts.keyPath);
|
|
2545
|
-
const files = key && existsSync5(dir) ? readdirSync(dir) : [];
|
|
2546
|
-
for (const file of files) {
|
|
2547
|
-
const name = envNameFromFile(file);
|
|
2548
|
-
if (!name) continue;
|
|
2549
|
-
try {
|
|
2550
|
-
const box = JSON.parse(readFileSync4(join3(dir, file), "utf8"));
|
|
2551
|
-
const plaintext = openString(box, key);
|
|
2552
|
-
const target = typeof box.materializeTo === "string" ? box.materializeTo : null;
|
|
2553
|
-
if (target) {
|
|
2554
|
-
const blobMtime = statSync(join3(dir, file)).mtimeMs;
|
|
2555
|
-
if (!existsSync5(target) || statSync(target).mtimeMs < blobMtime) {
|
|
2556
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
2557
|
-
writeFileSync3(target, plaintext, { mode: 384 });
|
|
2558
|
-
}
|
|
2559
|
-
} else {
|
|
2560
|
-
env[name] = plaintext;
|
|
2561
|
-
}
|
|
2562
|
-
applied.push(name);
|
|
2563
|
-
} catch (e) {
|
|
2564
|
-
failed.push(file);
|
|
2565
|
-
log(`secrets: failed to open ${file}: ${e.message}`);
|
|
2566
|
-
}
|
|
2567
|
-
}
|
|
2568
|
-
const live = new Set(applied);
|
|
2569
|
-
for (const prev of opts.previouslyApplied ?? []) {
|
|
2570
|
-
if (!live.has(prev)) {
|
|
2571
|
-
delete env[prev];
|
|
2572
|
-
log(`secrets: revoked ${prev}`);
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
return { applied, failed };
|
|
2576
|
-
}
|
|
2577
|
-
function startSecretsWatcher(opts = {}) {
|
|
2578
|
-
const dir = opts.dir ?? SECRETS_DIR;
|
|
2579
|
-
const log = opts.log ?? (() => {
|
|
2580
|
-
});
|
|
2581
|
-
let appliedNames = /* @__PURE__ */ new Set();
|
|
2582
|
-
const run = () => {
|
|
2583
|
-
const r = materializeSecrets({ ...opts, previouslyApplied: appliedNames });
|
|
2584
|
-
appliedNames = new Set(r.applied);
|
|
2585
|
-
};
|
|
2586
|
-
run();
|
|
2587
|
-
if (!existsSync5(dir)) return () => {
|
|
2588
|
-
};
|
|
2589
|
-
let timer = null;
|
|
2590
|
-
const watcher = watch(dir, () => {
|
|
2591
|
-
if (timer) clearTimeout(timer);
|
|
2592
|
-
timer = setTimeout(run, 150);
|
|
2593
|
-
});
|
|
2594
|
-
watcher.on("error", (err) => log(`secrets: watcher error: ${err.message}`));
|
|
2595
|
-
return () => {
|
|
2596
|
-
if (timer) clearTimeout(timer);
|
|
2597
|
-
watcher.close();
|
|
2598
|
-
};
|
|
2599
|
-
}
|
|
2600
|
-
|
|
2601
|
-
// src/commands/agents/code.ts
|
|
2602
2578
|
var CliError2 = class extends Error {
|
|
2603
2579
|
};
|
|
2604
2580
|
var CODING_TOOLS = ["file.read", "file.write", "file.edit", "bash", "git.worktree", "verify", "forge.issue.get", "forge.pr.status"];
|
|
@@ -2611,9 +2587,9 @@ var DEFAULT_PERSONA = [
|
|
|
2611
2587
|
].join(" ");
|
|
2612
2588
|
function readLitellmConfig(model) {
|
|
2613
2589
|
const env = {};
|
|
2614
|
-
const envPath =
|
|
2615
|
-
if (
|
|
2616
|
-
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")) {
|
|
2617
2593
|
const line = raw.trim();
|
|
2618
2594
|
const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);
|
|
2619
2595
|
if (m) env[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
|
|
@@ -2629,11 +2605,11 @@ function readLitellmConfig(model) {
|
|
|
2629
2605
|
return { apiBase, apiKey, model: model || process3.env.APE_CHAT_BRIDGE_MODEL || "claude-haiku-4-5" };
|
|
2630
2606
|
}
|
|
2631
2607
|
function readPersona(file) {
|
|
2632
|
-
if (file &&
|
|
2633
|
-
const agentJson =
|
|
2634
|
-
if (
|
|
2608
|
+
if (file && existsSync5(file)) return readFileSync4(file, "utf8");
|
|
2609
|
+
const agentJson = join3(homedir4(), ".openape", "agent", "agent.json");
|
|
2610
|
+
if (existsSync5(agentJson)) {
|
|
2635
2611
|
try {
|
|
2636
|
-
const p = JSON.parse(
|
|
2612
|
+
const p = JSON.parse(readFileSync4(agentJson, "utf8"));
|
|
2637
2613
|
if (p.systemPrompt?.trim()) return p.systemPrompt;
|
|
2638
2614
|
} catch {
|
|
2639
2615
|
}
|
|
@@ -2648,7 +2624,7 @@ async function fetchIssue(forge, ref, repo) {
|
|
|
2648
2624
|
const title = j.title ?? j.fields?.["System.Title"] ?? `issue ${ref}`;
|
|
2649
2625
|
return { number, title, body: j.body };
|
|
2650
2626
|
}
|
|
2651
|
-
var codeAgentCommand =
|
|
2627
|
+
var codeAgentCommand = defineCommand25({
|
|
2652
2628
|
meta: {
|
|
2653
2629
|
name: "code",
|
|
2654
2630
|
description: "Run a coding task: issue to worktree to edit to verify to PR (policy-gated merge). The agent never self-merges."
|
|
@@ -2667,7 +2643,7 @@ var codeAgentCommand = defineCommand24({
|
|
|
2667
2643
|
const forge = args.forge || detectForge(repo);
|
|
2668
2644
|
try {
|
|
2669
2645
|
const { applied } = materializeSecrets();
|
|
2670
|
-
if (applied.length > 0)
|
|
2646
|
+
if (applied.length > 0) consola22.info(`Capabilities available: ${applied.join(", ")}`);
|
|
2671
2647
|
} catch {
|
|
2672
2648
|
}
|
|
2673
2649
|
const config = readLitellmConfig(args.model);
|
|
@@ -2683,22 +2659,22 @@ var codeAgentCommand = defineCommand24({
|
|
|
2683
2659
|
resolvePolicy: (worktree) => resolveMergePolicy(worktree),
|
|
2684
2660
|
reviewer: createLlmReviewer(config),
|
|
2685
2661
|
riskAssessor: createLlmRiskAssessor(config),
|
|
2686
|
-
log: (l) =>
|
|
2662
|
+
log: (l) => consola22.info(l),
|
|
2687
2663
|
streamAggregate
|
|
2688
2664
|
};
|
|
2689
2665
|
const refs = [];
|
|
2690
2666
|
if (args["poll-label"]) {
|
|
2691
2667
|
const slug = repo.replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
2692
|
-
const
|
|
2693
|
-
if (
|
|
2694
|
-
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)}`);
|
|
2695
2671
|
}
|
|
2696
|
-
if (
|
|
2672
|
+
if (list2.stdout.trim() === "" && /gh auth login|GH_TOKEN/i.test(list2.stderr)) {
|
|
2697
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.");
|
|
2698
2674
|
}
|
|
2699
|
-
refs.push(...
|
|
2675
|
+
refs.push(...list2.stdout.split("\n").map((s) => s.trim()).filter((s) => /^\d+$/.test(s)));
|
|
2700
2676
|
if (refs.length === 0) {
|
|
2701
|
-
|
|
2677
|
+
consola22.info("no open issues with that label");
|
|
2702
2678
|
return;
|
|
2703
2679
|
}
|
|
2704
2680
|
} else if (args.issue) {
|
|
@@ -2708,9 +2684,9 @@ var codeAgentCommand = defineCommand24({
|
|
|
2708
2684
|
}
|
|
2709
2685
|
for (const ref of refs) {
|
|
2710
2686
|
const issue = await fetchIssue(forge, ref, repo);
|
|
2711
|
-
|
|
2687
|
+
consola22.start(`coding issue #${issue.number}: ${issue.title}`);
|
|
2712
2688
|
const result = await runCodingTask({ issue, repo, forge }, deps);
|
|
2713
|
-
|
|
2689
|
+
consola22.box(`#${issue.number} -> ${result.outcome}
|
|
2714
2690
|
PR: ${result.prRef ?? "(none)"}
|
|
2715
2691
|
${result.reason}`);
|
|
2716
2692
|
}
|
|
@@ -2718,27 +2694,27 @@ ${result.reason}`);
|
|
|
2718
2694
|
});
|
|
2719
2695
|
|
|
2720
2696
|
// src/commands/agents/destroy.ts
|
|
2721
|
-
import { defineCommand as
|
|
2722
|
-
import
|
|
2697
|
+
import { defineCommand as defineCommand26 } from "citty";
|
|
2698
|
+
import consola23 from "consola";
|
|
2723
2699
|
|
|
2724
2700
|
// src/lib/nest-registry.ts
|
|
2725
|
-
import { existsSync as
|
|
2726
|
-
import { homedir as
|
|
2727
|
-
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";
|
|
2728
2704
|
function resolveRegistryPath() {
|
|
2729
2705
|
if (process.env.OPENAPE_NEST_REGISTRY_PATH) return process.env.OPENAPE_NEST_REGISTRY_PATH;
|
|
2730
|
-
if (
|
|
2731
|
-
if (
|
|
2732
|
-
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");
|
|
2733
2709
|
}
|
|
2734
2710
|
function emptyRegistry() {
|
|
2735
2711
|
return { version: 1, agents: [] };
|
|
2736
2712
|
}
|
|
2737
2713
|
function readNestRegistry() {
|
|
2738
2714
|
const path2 = resolveRegistryPath();
|
|
2739
|
-
if (!
|
|
2715
|
+
if (!existsSync6(path2)) return emptyRegistry();
|
|
2740
2716
|
try {
|
|
2741
|
-
const parsed = JSON.parse(
|
|
2717
|
+
const parsed = JSON.parse(readFileSync5(path2, "utf8"));
|
|
2742
2718
|
if (parsed?.version !== 1 || !Array.isArray(parsed.agents)) return emptyRegistry();
|
|
2743
2719
|
return parsed;
|
|
2744
2720
|
} catch {
|
|
@@ -2749,10 +2725,10 @@ function writeNestRegistry(reg) {
|
|
|
2749
2725
|
const path2 = resolveRegistryPath();
|
|
2750
2726
|
const dir = path2.replace(/\/agents\.json$/, "");
|
|
2751
2727
|
try {
|
|
2752
|
-
|
|
2728
|
+
mkdirSync(dir, { recursive: true });
|
|
2753
2729
|
} catch {
|
|
2754
2730
|
}
|
|
2755
|
-
|
|
2731
|
+
writeFileSync3(path2, `${JSON.stringify(reg, null, 2)}
|
|
2756
2732
|
`, { mode: 432 });
|
|
2757
2733
|
}
|
|
2758
2734
|
function upsertNestAgent(entry) {
|
|
@@ -2772,7 +2748,7 @@ function removeNestAgent(name) {
|
|
|
2772
2748
|
}
|
|
2773
2749
|
|
|
2774
2750
|
// src/commands/agents/destroy.ts
|
|
2775
|
-
var destroyAgentCommand =
|
|
2751
|
+
var destroyAgentCommand = defineCommand26({
|
|
2776
2752
|
meta: {
|
|
2777
2753
|
name: "destroy",
|
|
2778
2754
|
description: "Tear down an agent: remove the OS user, hard-delete IdP agent, drop all SSH keys"
|
|
@@ -2817,7 +2793,7 @@ var destroyAgentCommand = defineCommand25({
|
|
|
2817
2793
|
const osUser = getHostPlatform().lookupAgentUser(name);
|
|
2818
2794
|
const osUserExists = !args["keep-os-user"] && osUser !== null;
|
|
2819
2795
|
if (!idpExists && !osUserExists) {
|
|
2820
|
-
|
|
2796
|
+
consola23.info(`Nothing to destroy: no IdP agent and no OS user for "${name}".`);
|
|
2821
2797
|
return;
|
|
2822
2798
|
}
|
|
2823
2799
|
if (!args.force) {
|
|
@@ -2829,14 +2805,14 @@ var destroyAgentCommand = defineCommand25({
|
|
|
2829
2805
|
if (idpExists) {
|
|
2830
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`);
|
|
2831
2807
|
}
|
|
2832
|
-
|
|
2808
|
+
consola23.warn(`About to destroy "${name}":
|
|
2833
2809
|
${consequences.join("\n")}`);
|
|
2834
2810
|
if (!process.stdin.isTTY) {
|
|
2835
2811
|
throw new CliError(
|
|
2836
2812
|
"No TTY available for the interactive confirmation. Re-run with --force to skip the prompt (this is the same flag CI uses)."
|
|
2837
2813
|
);
|
|
2838
2814
|
}
|
|
2839
|
-
const confirmed = await
|
|
2815
|
+
const confirmed = await consola23.prompt("Proceed?", { type: "confirm", initial: false });
|
|
2840
2816
|
if (typeof confirmed === "symbol" || !confirmed) {
|
|
2841
2817
|
throw new CliExit(0);
|
|
2842
2818
|
}
|
|
@@ -2845,16 +2821,16 @@ ${consequences.join("\n")}`);
|
|
|
2845
2821
|
const id = encodeURIComponent(idpAgent.email);
|
|
2846
2822
|
if (args.soft) {
|
|
2847
2823
|
await apiFetch(`/api/my-agents/${id}`, { method: "PATCH", body: { isActive: false }, idp });
|
|
2848
|
-
|
|
2824
|
+
consola23.success(`Deactivated IdP agent ${idpAgent.email}`);
|
|
2849
2825
|
} else {
|
|
2850
2826
|
await apiFetch(`/api/my-agents/${id}`, { method: "DELETE", idp });
|
|
2851
|
-
|
|
2827
|
+
consola23.success(`Deleted IdP agent ${idpAgent.email}`);
|
|
2852
2828
|
}
|
|
2853
2829
|
} else {
|
|
2854
|
-
|
|
2830
|
+
consola23.info("No IdP agent to remove (skipped).");
|
|
2855
2831
|
}
|
|
2856
2832
|
if (osUserExists) {
|
|
2857
|
-
|
|
2833
|
+
consola23.start(`Removing OS user ${name}\u2026`);
|
|
2858
2834
|
await getHostPlatform().runPrivilegedBash(
|
|
2859
2835
|
`#!/bin/bash
|
|
2860
2836
|
set -euo pipefail
|
|
@@ -2864,21 +2840,21 @@ if getent passwd ${JSON.stringify(name)} >/dev/null 2>&1; then
|
|
|
2864
2840
|
fi
|
|
2865
2841
|
`
|
|
2866
2842
|
);
|
|
2867
|
-
|
|
2843
|
+
consola23.success(`Removed OS user ${name}.`);
|
|
2868
2844
|
}
|
|
2869
2845
|
try {
|
|
2870
2846
|
removeNestAgent(name);
|
|
2871
2847
|
} catch (err) {
|
|
2872
|
-
|
|
2848
|
+
consola23.warn(`Could not update nest registry: ${err instanceof Error ? err.message : String(err)}`);
|
|
2873
2849
|
}
|
|
2874
|
-
|
|
2850
|
+
consola23.success(`Destroyed ${name}.`);
|
|
2875
2851
|
}
|
|
2876
2852
|
});
|
|
2877
2853
|
|
|
2878
2854
|
// src/commands/agents/list.ts
|
|
2879
|
-
import { defineCommand as
|
|
2880
|
-
import
|
|
2881
|
-
var listAgentsCommand =
|
|
2855
|
+
import { defineCommand as defineCommand27 } from "citty";
|
|
2856
|
+
import consola24 from "consola";
|
|
2857
|
+
var listAgentsCommand = defineCommand27({
|
|
2882
2858
|
meta: {
|
|
2883
2859
|
name: "list",
|
|
2884
2860
|
description: "List agents owned by the current user, with local OS-user status"
|
|
@@ -2930,7 +2906,7 @@ var listAgentsCommand = defineCommand26({
|
|
|
2930
2906
|
return;
|
|
2931
2907
|
}
|
|
2932
2908
|
if (rows.length === 0) {
|
|
2933
|
-
|
|
2909
|
+
consola24.info(args["include-inactive"] ? "No agents found." : "No active agents found. Use --include-inactive to show deactivated.");
|
|
2934
2910
|
return;
|
|
2935
2911
|
}
|
|
2936
2912
|
const nameW = Math.max(4, ...rows.map((r) => r.name.length));
|
|
@@ -2948,10 +2924,10 @@ var listAgentsCommand = defineCommand26({
|
|
|
2948
2924
|
});
|
|
2949
2925
|
|
|
2950
2926
|
// src/commands/agents/register.ts
|
|
2951
|
-
import { existsSync as
|
|
2952
|
-
import { defineCommand as
|
|
2953
|
-
import
|
|
2954
|
-
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({
|
|
2955
2931
|
meta: {
|
|
2956
2932
|
name: "register",
|
|
2957
2933
|
description: "Register an agent at the IdP using a supplied public key"
|
|
@@ -2996,10 +2972,10 @@ var registerAgentCommand = defineCommand27({
|
|
|
2996
2972
|
throw new CliError("Pass either --public-key or --public-key-file, not both.");
|
|
2997
2973
|
}
|
|
2998
2974
|
if (!publicKey && keyFile) {
|
|
2999
|
-
if (!
|
|
2975
|
+
if (!existsSync7(keyFile)) {
|
|
3000
2976
|
throw new CliError(`Public-key file not found: ${keyFile}`);
|
|
3001
2977
|
}
|
|
3002
|
-
publicKey =
|
|
2978
|
+
publicKey = readFileSync6(keyFile, "utf-8").trim();
|
|
3003
2979
|
}
|
|
3004
2980
|
if (!publicKey) {
|
|
3005
2981
|
throw new CliError('Provide --public-key "<ssh-ed25519 line>" or --public-key-file <path>.');
|
|
@@ -3021,7 +2997,7 @@ var registerAgentCommand = defineCommand27({
|
|
|
3021
2997
|
`);
|
|
3022
2998
|
return;
|
|
3023
2999
|
}
|
|
3024
|
-
|
|
3000
|
+
consola25.success("Agent registered.");
|
|
3025
3001
|
console.log(` Name: ${result.name}`);
|
|
3026
3002
|
console.log(` Email: ${result.email}`);
|
|
3027
3003
|
console.log(` IdP: ${idp}`);
|
|
@@ -3034,19 +3010,19 @@ var registerAgentCommand = defineCommand27({
|
|
|
3034
3010
|
});
|
|
3035
3011
|
|
|
3036
3012
|
// src/commands/agents/run.ts
|
|
3037
|
-
import { existsSync as
|
|
3038
|
-
import { homedir as
|
|
3039
|
-
import { join as
|
|
3040
|
-
import { defineCommand as
|
|
3041
|
-
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";
|
|
3042
3018
|
import { taskTools as taskTools2, runLoop } from "@openape/agent-runtime";
|
|
3043
|
-
var AUTH_PATH =
|
|
3044
|
-
var TASK_CACHE_DIR =
|
|
3019
|
+
var AUTH_PATH = join5(homedir6(), ".config", "apes", "auth.json");
|
|
3020
|
+
var TASK_CACHE_DIR = join5(homedir6(), ".openape", "agent", "tasks");
|
|
3045
3021
|
function readAuth() {
|
|
3046
|
-
if (!
|
|
3022
|
+
if (!existsSync8(AUTH_PATH)) {
|
|
3047
3023
|
throw new CliError(`No agent auth found at ${AUTH_PATH}. Run \`apes agents spawn <name>\` first.`);
|
|
3048
3024
|
}
|
|
3049
|
-
const parsed = JSON.parse(
|
|
3025
|
+
const parsed = JSON.parse(readFileSync7(AUTH_PATH, "utf8"));
|
|
3050
3026
|
if (!parsed.access_token) throw new CliError("auth.json missing access_token");
|
|
3051
3027
|
return parsed;
|
|
3052
3028
|
}
|
|
@@ -3061,7 +3037,7 @@ async function postRunResultToChat(opts) {
|
|
|
3061
3037
|
const ownerLower = opts.ownerEmail.toLowerCase();
|
|
3062
3038
|
const ownerRow = contacts.find((c) => c.peerEmail.toLowerCase() === ownerLower && c.connected && c.roomId);
|
|
3063
3039
|
if (!ownerRow?.roomId) {
|
|
3064
|
-
|
|
3040
|
+
consola26.info("chat DM skipped \u2014 no active room with owner (accept the contact request in chat to enable)");
|
|
3065
3041
|
return;
|
|
3066
3042
|
}
|
|
3067
3043
|
const prefix = opts.status === "ok" ? "\u2705" : "\u274C";
|
|
@@ -3075,33 +3051,33 @@ ${msg}`.slice(0, 9e3);
|
|
|
3075
3051
|
body: JSON.stringify({ body })
|
|
3076
3052
|
});
|
|
3077
3053
|
if (!postRes.ok) {
|
|
3078
|
-
|
|
3054
|
+
consola26.warn(`chat DM post failed: ${postRes.status}`);
|
|
3079
3055
|
}
|
|
3080
3056
|
} catch (err) {
|
|
3081
|
-
|
|
3057
|
+
consola26.warn(`chat DM error: ${err.message}`);
|
|
3082
3058
|
}
|
|
3083
3059
|
}
|
|
3084
3060
|
function readTaskSpec(taskId) {
|
|
3085
|
-
const path2 =
|
|
3086
|
-
if (!
|
|
3061
|
+
const path2 = join5(TASK_CACHE_DIR, `${taskId}.json`);
|
|
3062
|
+
if (!existsSync8(path2)) {
|
|
3087
3063
|
throw new CliError(`No cached task spec at ${path2}. Run \`apes agents sync\` first to pull the task list from troop.`);
|
|
3088
3064
|
}
|
|
3089
|
-
return JSON.parse(
|
|
3065
|
+
return JSON.parse(readFileSync7(path2, "utf8"));
|
|
3090
3066
|
}
|
|
3091
|
-
var AGENT_CONFIG_PATH =
|
|
3067
|
+
var AGENT_CONFIG_PATH = join5(homedir6(), ".openape", "agent", "agent.json");
|
|
3092
3068
|
function readAgentConfig() {
|
|
3093
|
-
if (!
|
|
3069
|
+
if (!existsSync8(AGENT_CONFIG_PATH)) return { systemPrompt: "" };
|
|
3094
3070
|
try {
|
|
3095
|
-
return JSON.parse(
|
|
3071
|
+
return JSON.parse(readFileSync7(AGENT_CONFIG_PATH, "utf8"));
|
|
3096
3072
|
} catch {
|
|
3097
3073
|
return { systemPrompt: "" };
|
|
3098
3074
|
}
|
|
3099
3075
|
}
|
|
3100
3076
|
function readLitellmConfig2(model) {
|
|
3101
|
-
const envPath =
|
|
3077
|
+
const envPath = join5(homedir6(), "litellm", ".env");
|
|
3102
3078
|
const env = {};
|
|
3103
|
-
if (
|
|
3104
|
-
for (const line of
|
|
3079
|
+
if (existsSync8(envPath)) {
|
|
3080
|
+
for (const line of readFileSync7(envPath, "utf8").split(/\r?\n/)) {
|
|
3105
3081
|
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
3106
3082
|
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
3107
3083
|
}
|
|
@@ -3116,7 +3092,7 @@ function readLitellmConfig2(model) {
|
|
|
3116
3092
|
}
|
|
3117
3093
|
return { apiBase, apiKey, model: model || "claude-haiku-4-5" };
|
|
3118
3094
|
}
|
|
3119
|
-
var runAgentCommand =
|
|
3095
|
+
var runAgentCommand = defineCommand29({
|
|
3120
3096
|
meta: {
|
|
3121
3097
|
name: "run",
|
|
3122
3098
|
description: "Execute one task (typically launchd-invoked). Reports the run record to troop."
|
|
@@ -3139,7 +3115,7 @@ var runAgentCommand = defineCommand28({
|
|
|
3139
3115
|
async run({ args }) {
|
|
3140
3116
|
const taskId = args["task-id"];
|
|
3141
3117
|
const auth = readAuth();
|
|
3142
|
-
materializeSecrets({ log: (l) =>
|
|
3118
|
+
materializeSecrets({ log: (l) => consola26.info(l) });
|
|
3143
3119
|
const spec = readTaskSpec(taskId);
|
|
3144
3120
|
const agentCfg = readAgentConfig();
|
|
3145
3121
|
const config = readLitellmConfig2(args.model);
|
|
@@ -3151,7 +3127,7 @@ var runAgentCommand = defineCommand28({
|
|
|
3151
3127
|
}
|
|
3152
3128
|
const troop = new TroopClient(resolveTroopUrl(args["troop-url"]), auth.access_token);
|
|
3153
3129
|
const { id: runId } = await troop.startRun(taskId);
|
|
3154
|
-
|
|
3130
|
+
consola26.info(`Run ${runId} started for task ${taskId}`);
|
|
3155
3131
|
try {
|
|
3156
3132
|
const result = await runLoop({
|
|
3157
3133
|
config,
|
|
@@ -3170,7 +3146,7 @@ var runAgentCommand = defineCommand28({
|
|
|
3170
3146
|
step_count: result.stepCount,
|
|
3171
3147
|
trace: result.trace
|
|
3172
3148
|
});
|
|
3173
|
-
|
|
3149
|
+
consola26.success(`Run ${runId} ${result.status} (${result.stepCount} steps)`);
|
|
3174
3150
|
if (auth.owner_email) {
|
|
3175
3151
|
await postRunResultToChat({
|
|
3176
3152
|
authToken: auth.access_token,
|
|
@@ -3207,18 +3183,18 @@ var runAgentCommand = defineCommand28({
|
|
|
3207
3183
|
});
|
|
3208
3184
|
|
|
3209
3185
|
// src/commands/agents/serve.ts
|
|
3210
|
-
import { existsSync as
|
|
3211
|
-
import { homedir as
|
|
3212
|
-
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";
|
|
3213
3189
|
import { createInterface } from "readline";
|
|
3214
|
-
import { defineCommand as
|
|
3190
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
3215
3191
|
import { taskTools as taskTools3, runLoop as runLoop2, RpcSessionMap } from "@openape/agent-runtime";
|
|
3216
|
-
var AUTH_PATH2 =
|
|
3192
|
+
var AUTH_PATH2 = join6(homedir7(), ".config", "apes", "auth.json");
|
|
3217
3193
|
function readLitellmConfig3(model) {
|
|
3218
|
-
const envPath =
|
|
3194
|
+
const envPath = join6(homedir7(), "litellm", ".env");
|
|
3219
3195
|
const env = {};
|
|
3220
|
-
if (
|
|
3221
|
-
for (const line of
|
|
3196
|
+
if (existsSync9(envPath)) {
|
|
3197
|
+
for (const line of readFileSync8(envPath, "utf8").split(/\r?\n/)) {
|
|
3222
3198
|
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
3223
3199
|
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
3224
3200
|
}
|
|
@@ -3235,7 +3211,7 @@ function emit(event) {
|
|
|
3235
3211
|
process.stdout.write(`${JSON.stringify(event)}
|
|
3236
3212
|
`);
|
|
3237
3213
|
}
|
|
3238
|
-
var serveAgentCommand =
|
|
3214
|
+
var serveAgentCommand = defineCommand30({
|
|
3239
3215
|
meta: {
|
|
3240
3216
|
name: "serve",
|
|
3241
3217
|
description: "Long-running stdio RPC server for chat-bridge subprocess use."
|
|
@@ -3250,9 +3226,9 @@ var serveAgentCommand = defineCommand29({
|
|
|
3250
3226
|
if (!args.rpc) {
|
|
3251
3227
|
throw new CliError("apes agents serve currently only supports --rpc mode");
|
|
3252
3228
|
}
|
|
3253
|
-
if (
|
|
3229
|
+
if (existsSync9(AUTH_PATH2)) {
|
|
3254
3230
|
try {
|
|
3255
|
-
JSON.parse(
|
|
3231
|
+
JSON.parse(readFileSync8(AUTH_PATH2, "utf8"));
|
|
3256
3232
|
} catch {
|
|
3257
3233
|
}
|
|
3258
3234
|
}
|
|
@@ -3332,18 +3308,18 @@ async function handleInbound(msg, sessions) {
|
|
|
3332
3308
|
}
|
|
3333
3309
|
|
|
3334
3310
|
// src/commands/agents/spawn.ts
|
|
3335
|
-
import { defineCommand as
|
|
3336
|
-
import
|
|
3311
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
3312
|
+
import consola27 from "consola";
|
|
3337
3313
|
|
|
3338
3314
|
// src/lib/keygen.ts
|
|
3339
3315
|
import { Buffer as Buffer3 } from "buffer";
|
|
3340
|
-
import { existsSync as
|
|
3316
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
3341
3317
|
import { generateKeyPairSync } from "crypto";
|
|
3342
|
-
import { homedir as
|
|
3343
|
-
import { dirname
|
|
3318
|
+
import { homedir as homedir8 } from "os";
|
|
3319
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
3344
3320
|
import { generateX25519KeyPair } from "@openape/core";
|
|
3345
3321
|
function resolveKeyPath(p) {
|
|
3346
|
-
return resolve2(p.replace(/^~/,
|
|
3322
|
+
return resolve2(p.replace(/^~/, homedir8()));
|
|
3347
3323
|
}
|
|
3348
3324
|
function buildSshEd25519Line(rawPub) {
|
|
3349
3325
|
const keyTypeStr = "ssh-ed25519";
|
|
@@ -3356,10 +3332,10 @@ function buildSshEd25519Line(rawPub) {
|
|
|
3356
3332
|
}
|
|
3357
3333
|
function readPublicKey(keyPath) {
|
|
3358
3334
|
const pubPath = `${keyPath}.pub`;
|
|
3359
|
-
if (
|
|
3360
|
-
return
|
|
3335
|
+
if (existsSync10(pubPath)) {
|
|
3336
|
+
return readFileSync9(pubPath, "utf-8").trim();
|
|
3361
3337
|
}
|
|
3362
|
-
const keyContent =
|
|
3338
|
+
const keyContent = readFileSync9(keyPath, "utf-8");
|
|
3363
3339
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
3364
3340
|
const jwk = privateKey.export({ format: "jwk" });
|
|
3365
3341
|
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
@@ -3367,17 +3343,17 @@ function readPublicKey(keyPath) {
|
|
|
3367
3343
|
}
|
|
3368
3344
|
function generateAndSaveKey(keyPath) {
|
|
3369
3345
|
const resolved = resolveKeyPath(keyPath);
|
|
3370
|
-
const dir =
|
|
3371
|
-
if (!
|
|
3372
|
-
|
|
3346
|
+
const dir = dirname(resolved);
|
|
3347
|
+
if (!existsSync10(dir)) {
|
|
3348
|
+
mkdirSync2(dir, { recursive: true });
|
|
3373
3349
|
}
|
|
3374
3350
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
3375
3351
|
const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
|
|
3376
|
-
|
|
3352
|
+
writeFileSync4(resolved, privatePem, { mode: 384 });
|
|
3377
3353
|
const jwk = publicKey.export({ format: "jwk" });
|
|
3378
3354
|
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
3379
3355
|
const pubKeyStr = buildSshEd25519Line(pubBytes);
|
|
3380
|
-
|
|
3356
|
+
writeFileSync4(`${resolved}.pub`, `${pubKeyStr}
|
|
3381
3357
|
`, { mode: 420 });
|
|
3382
3358
|
return pubKeyStr;
|
|
3383
3359
|
}
|
|
@@ -3404,7 +3380,7 @@ function readUidOrNull(name) {
|
|
|
3404
3380
|
return null;
|
|
3405
3381
|
}
|
|
3406
3382
|
}
|
|
3407
|
-
var spawnAgentCommand =
|
|
3383
|
+
var spawnAgentCommand = defineCommand31({
|
|
3408
3384
|
meta: {
|
|
3409
3385
|
name: "spawn",
|
|
3410
3386
|
description: "Provision a local Linux agent end-to-end (OS user, keypair, IdP agent, Claude hook)"
|
|
@@ -3439,6 +3415,10 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3439
3415
|
type: "string",
|
|
3440
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`."
|
|
3441
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
|
+
},
|
|
3442
3422
|
"kind": {
|
|
3443
3423
|
type: "string",
|
|
3444
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.`
|
|
@@ -3482,12 +3462,12 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3482
3462
|
throw new CliError(`OS user "${existing.name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
|
|
3483
3463
|
}
|
|
3484
3464
|
const homeDir = `/var/lib/openape/homes/${osUsername}`;
|
|
3485
|
-
|
|
3465
|
+
consola27.start(`Generating keypair for ${name}\u2026`);
|
|
3486
3466
|
const { privatePem, publicSshLine, x25519PrivateKey, x25519PublicKey } = generateKeyPairInMemory();
|
|
3487
|
-
|
|
3467
|
+
consola27.start(`Registering agent at ${idp}\u2026`);
|
|
3488
3468
|
const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
|
|
3489
|
-
|
|
3490
|
-
|
|
3469
|
+
consola27.success(`Registered as ${registration.email}`);
|
|
3470
|
+
consola27.start("Issuing agent access token\u2026");
|
|
3491
3471
|
const { token, expiresIn } = await issueAgentToken({
|
|
3492
3472
|
idp,
|
|
3493
3473
|
agentEmail: registration.email,
|
|
@@ -3523,9 +3503,9 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3523
3503
|
claudeSettingsJson: includeClaudeHook ? CLAUDE_SETTINGS_JSON : null,
|
|
3524
3504
|
hookScriptSource: includeClaudeHook ? BASH_VIA_APE_SHELL_HOOK_SOURCE : null
|
|
3525
3505
|
});
|
|
3526
|
-
|
|
3506
|
+
consola27.start("Running privileged setup\u2026");
|
|
3527
3507
|
if (process.getuid?.() !== 0) {
|
|
3528
|
-
|
|
3508
|
+
consola27.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
|
|
3529
3509
|
}
|
|
3530
3510
|
await platform.runPrivilegedBash(script);
|
|
3531
3511
|
try {
|
|
@@ -3543,17 +3523,18 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3543
3523
|
bridge: withBridge || isService ? {
|
|
3544
3524
|
baseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0,
|
|
3545
3525
|
apiKey: typeof args["bridge-key"] === "string" ? args["bridge-key"] : void 0,
|
|
3546
|
-
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
|
|
3547
3528
|
} : void 0
|
|
3548
3529
|
});
|
|
3549
3530
|
} catch (err) {
|
|
3550
|
-
|
|
3531
|
+
consola27.warn(`Could not write to nest registry: ${err instanceof Error ? err.message : String(err)}`);
|
|
3551
3532
|
}
|
|
3552
|
-
|
|
3553
|
-
|
|
3533
|
+
consola27.success(`Agent ${name} spawned.`);
|
|
3534
|
+
consola27.info(`\u{1F517} Troop: https://troop.openape.ai/agents/${name}`);
|
|
3554
3535
|
if (withBridge) {
|
|
3555
|
-
|
|
3556
|
-
|
|
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.");
|
|
3557
3538
|
}
|
|
3558
3539
|
console.log("");
|
|
3559
3540
|
console.log("Run as the agent with:");
|
|
@@ -3562,47 +3543,48 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3562
3543
|
});
|
|
3563
3544
|
|
|
3564
3545
|
// src/commands/agents/sync.ts
|
|
3565
|
-
import { chownSync, existsSync as
|
|
3566
|
-
import { homedir as
|
|
3567
|
-
import { join as
|
|
3568
|
-
import {
|
|
3569
|
-
import
|
|
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";
|
|
3570
3552
|
|
|
3571
3553
|
// src/lib/recipe-checkout.ts
|
|
3572
3554
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
3573
|
-
import { cpSync, existsSync as
|
|
3574
|
-
import { join as
|
|
3575
|
-
import
|
|
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";
|
|
3576
3558
|
var MARKER_FILE = ".recipe-ref";
|
|
3577
3559
|
var GITHUB_PREFIX = "github.com/";
|
|
3578
3560
|
function ensureRecipeCheckout(recipeRef, recipeDir, exec2 = execFileSync7) {
|
|
3579
3561
|
const at = recipeRef.lastIndexOf("@");
|
|
3580
3562
|
if (at <= 0) {
|
|
3581
|
-
|
|
3563
|
+
consola28.warn(`recipe: malformed recipeRef "${recipeRef}" (expected <owner>/<name>[/<subdir>]@<ref>) \u2014 skipping checkout`);
|
|
3582
3564
|
return;
|
|
3583
3565
|
}
|
|
3584
3566
|
const spec = recipeRef.slice(0, at);
|
|
3585
3567
|
const ref = recipeRef.slice(at + 1);
|
|
3586
3568
|
const slug = spec.startsWith(GITHUB_PREFIX) ? spec.slice(GITHUB_PREFIX.length) : spec;
|
|
3587
3569
|
if (!slug || !ref) {
|
|
3588
|
-
|
|
3570
|
+
consola28.warn(`recipe: malformed recipeRef "${recipeRef}" (empty slug or ref) \u2014 skipping checkout`);
|
|
3589
3571
|
return;
|
|
3590
3572
|
}
|
|
3591
3573
|
const segments = slug.split("/").filter(Boolean);
|
|
3592
3574
|
if (segments.length < 2) {
|
|
3593
|
-
|
|
3575
|
+
consola28.warn(`recipe: malformed recipeRef "${recipeRef}" (expected <owner>/<name>[/<subdir>]@<ref>) \u2014 skipping checkout`);
|
|
3594
3576
|
return;
|
|
3595
3577
|
}
|
|
3596
3578
|
if (segments.some((s) => s === "." || s === ".." || s.includes("\\"))) {
|
|
3597
|
-
|
|
3579
|
+
consola28.warn(`recipe: unsafe path segment in recipeRef "${recipeRef}" \u2014 skipping checkout`);
|
|
3598
3580
|
return;
|
|
3599
3581
|
}
|
|
3600
3582
|
const repoSlug = segments.slice(0, 2).join("/");
|
|
3601
3583
|
const subdir = segments.slice(2).join("/");
|
|
3602
|
-
const markerPath =
|
|
3603
|
-
if (
|
|
3584
|
+
const markerPath = join7(recipeDir, MARKER_FILE);
|
|
3585
|
+
if (existsSync11(markerPath)) {
|
|
3604
3586
|
try {
|
|
3605
|
-
if (
|
|
3587
|
+
if (readFileSync10(markerPath, "utf8") === recipeRef) return;
|
|
3606
3588
|
} catch {
|
|
3607
3589
|
}
|
|
3608
3590
|
}
|
|
@@ -3614,39 +3596,39 @@ function ensureRecipeCheckout(recipeRef, recipeDir, exec2 = execFileSync7) {
|
|
|
3614
3596
|
rmSync2(staging, { recursive: true, force: true });
|
|
3615
3597
|
exec2("git", ["clone", "--depth", "1", "--branch", ref, cloneUrl, staging]);
|
|
3616
3598
|
if (subdir) {
|
|
3617
|
-
const src =
|
|
3599
|
+
const src = join7(staging, subdir);
|
|
3618
3600
|
if (!resolve3(src).startsWith(resolve3(staging) + sep)) {
|
|
3619
|
-
|
|
3601
|
+
consola28.warn(`recipe: subdirectory "${subdir}" escapes the checkout dir \u2014 skipping`);
|
|
3620
3602
|
rmSync2(staging, { recursive: true, force: true });
|
|
3621
3603
|
return;
|
|
3622
3604
|
}
|
|
3623
|
-
if (!
|
|
3624
|
-
|
|
3605
|
+
if (!existsSync11(src)) {
|
|
3606
|
+
consola28.warn(`recipe: subdirectory "${subdir}" not found in ${repoSlug}@${ref} \u2014 skipping checkout`);
|
|
3625
3607
|
rmSync2(staging, { recursive: true, force: true });
|
|
3626
3608
|
return;
|
|
3627
3609
|
}
|
|
3628
3610
|
cpSync(src, recipeDir, { recursive: true });
|
|
3629
3611
|
rmSync2(staging, { recursive: true, force: true });
|
|
3630
3612
|
}
|
|
3631
|
-
|
|
3632
|
-
|
|
3613
|
+
writeFileSync5(markerPath, recipeRef);
|
|
3614
|
+
consola28.info(`recipe: checked out ${slug}@${ref} \u2192 ${recipeDir}`);
|
|
3633
3615
|
} catch (err) {
|
|
3634
|
-
|
|
3616
|
+
consola28.warn(`recipe: checkout of ${slug}@${ref} failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
3635
3617
|
if (subdir)
|
|
3636
3618
|
rmSync2(staging, { recursive: true, force: true });
|
|
3637
3619
|
}
|
|
3638
3620
|
}
|
|
3639
3621
|
|
|
3640
3622
|
// src/commands/agents/sync.ts
|
|
3641
|
-
var AUTH_PATH3 =
|
|
3642
|
-
var TASK_CACHE_DIR2 =
|
|
3623
|
+
var AUTH_PATH3 = join8(homedir9(), ".config", "apes", "auth.json");
|
|
3624
|
+
var TASK_CACHE_DIR2 = join8(homedir9(), ".openape", "agent", "tasks");
|
|
3643
3625
|
function readAuthJson() {
|
|
3644
|
-
if (!
|
|
3626
|
+
if (!existsSync12(AUTH_PATH3)) {
|
|
3645
3627
|
throw new CliError(
|
|
3646
3628
|
`No agent auth found at ${AUTH_PATH3}. Run \`apes agents spawn <name>\` to provision an agent first.`
|
|
3647
3629
|
);
|
|
3648
3630
|
}
|
|
3649
|
-
const raw =
|
|
3631
|
+
const raw = readFileSync11(AUTH_PATH3, "utf8");
|
|
3650
3632
|
let parsed;
|
|
3651
3633
|
try {
|
|
3652
3634
|
parsed = JSON.parse(raw);
|
|
@@ -3670,7 +3652,7 @@ function agentNameFromEmail(email) {
|
|
|
3670
3652
|
}
|
|
3671
3653
|
return before.slice(0, dashIdx);
|
|
3672
3654
|
}
|
|
3673
|
-
var syncAgentCommand =
|
|
3655
|
+
var syncAgentCommand = defineCommand32({
|
|
3674
3656
|
meta: {
|
|
3675
3657
|
name: "sync",
|
|
3676
3658
|
description: "Pull this agent's task list from troop.openape.ai and reconcile launchd plists"
|
|
@@ -3684,6 +3666,21 @@ var syncAgentCommand = defineCommand31({
|
|
|
3684
3666
|
async run({ args }) {
|
|
3685
3667
|
const auth = readAuthJson();
|
|
3686
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
|
+
}
|
|
3687
3684
|
const troopUrl = resolveTroopUrl(args["troop-url"]);
|
|
3688
3685
|
const client = new TroopClient(troopUrl, auth.access_token);
|
|
3689
3686
|
const platform = getHostPlatform();
|
|
@@ -3695,7 +3692,7 @@ var syncAgentCommand = defineCommand31({
|
|
|
3695
3692
|
if (!auth.owner_email) {
|
|
3696
3693
|
throw new CliError(`${AUTH_PATH3} is missing owner_email \u2014 re-run \`apes agents spawn\` to update.`);
|
|
3697
3694
|
}
|
|
3698
|
-
|
|
3695
|
+
consola29.start(`Syncing ${agentName} (${host}, hostId ${hostId.slice(0, 8)}\u2026) with ${troopUrl}`);
|
|
3699
3696
|
const pubkeyX25519 = readAgentEncryptionPublicKey() ?? void 0;
|
|
3700
3697
|
const sync = await client.sync({
|
|
3701
3698
|
hostname: host,
|
|
@@ -3703,17 +3700,17 @@ var syncAgentCommand = defineCommand31({
|
|
|
3703
3700
|
ownerEmail: auth.owner_email,
|
|
3704
3701
|
...pubkeyX25519 ? { pubkeyX25519 } : {}
|
|
3705
3702
|
});
|
|
3706
|
-
|
|
3707
|
-
if (!pubkeyX25519)
|
|
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.");
|
|
3708
3705
|
const { system_prompt: systemPrompt, tools, skills, tasks, recipe_ref: recipeRef } = await client.listTasks();
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
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(", ")}`);
|
|
3712
3709
|
let agentUid = null;
|
|
3713
3710
|
let agentGid = null;
|
|
3714
3711
|
if (process.geteuid?.() === 0) {
|
|
3715
3712
|
try {
|
|
3716
|
-
const homeStat =
|
|
3713
|
+
const homeStat = statSync(homedir9());
|
|
3717
3714
|
agentUid = homeStat.uid;
|
|
3718
3715
|
agentGid = homeStat.gid;
|
|
3719
3716
|
} catch {
|
|
@@ -3727,56 +3724,56 @@ var syncAgentCommand = defineCommand31({
|
|
|
3727
3724
|
}
|
|
3728
3725
|
}
|
|
3729
3726
|
}
|
|
3730
|
-
const agentDir =
|
|
3731
|
-
|
|
3732
|
-
chownToAgent(
|
|
3727
|
+
const agentDir = join8(homedir9(), ".openape", "agent");
|
|
3728
|
+
mkdirSync3(agentDir, { recursive: true });
|
|
3729
|
+
chownToAgent(join8(homedir9(), ".openape"));
|
|
3733
3730
|
chownToAgent(agentDir);
|
|
3734
|
-
const agentJsonPath =
|
|
3735
|
-
|
|
3731
|
+
const agentJsonPath = join8(agentDir, "agent.json");
|
|
3732
|
+
writeFileSync6(
|
|
3736
3733
|
agentJsonPath,
|
|
3737
3734
|
`${JSON.stringify({ systemPrompt, tools, ...recipeRef ? { recipeRef } : {} }, null, 2)}
|
|
3738
3735
|
`,
|
|
3739
3736
|
{ mode: 384 }
|
|
3740
3737
|
);
|
|
3741
3738
|
chownToAgent(agentJsonPath);
|
|
3742
|
-
if (recipeRef) ensureRecipeCheckout(recipeRef,
|
|
3743
|
-
|
|
3739
|
+
if (recipeRef) ensureRecipeCheckout(recipeRef, join8(homedir9(), "recipe"));
|
|
3740
|
+
mkdirSync3(TASK_CACHE_DIR2, { recursive: true });
|
|
3744
3741
|
chownToAgent(TASK_CACHE_DIR2);
|
|
3745
3742
|
for (const task of tasks) {
|
|
3746
|
-
const path2 =
|
|
3747
|
-
|
|
3743
|
+
const path2 = join8(TASK_CACHE_DIR2, `${task.taskId}.json`);
|
|
3744
|
+
writeFileSync6(path2, `${JSON.stringify(task, null, 2)}
|
|
3748
3745
|
`, { mode: 384 });
|
|
3749
3746
|
chownToAgent(path2);
|
|
3750
3747
|
}
|
|
3751
|
-
const skillsDir =
|
|
3752
|
-
|
|
3748
|
+
const skillsDir = join8(agentDir, "skills");
|
|
3749
|
+
mkdirSync3(skillsDir, { recursive: true });
|
|
3753
3750
|
chownToAgent(skillsDir);
|
|
3754
3751
|
const incomingNames = new Set(skills.map((s) => s.name));
|
|
3755
3752
|
try {
|
|
3756
|
-
for (const entry of
|
|
3753
|
+
for (const entry of readdirSync(skillsDir)) {
|
|
3757
3754
|
if (incomingNames.has(entry)) continue;
|
|
3758
3755
|
try {
|
|
3759
|
-
rmSync3(
|
|
3756
|
+
rmSync3(join8(skillsDir, entry), { recursive: true, force: true });
|
|
3760
3757
|
} catch {
|
|
3761
3758
|
}
|
|
3762
3759
|
}
|
|
3763
3760
|
} catch {
|
|
3764
3761
|
}
|
|
3765
3762
|
for (const skill of skills) {
|
|
3766
|
-
const skillDir =
|
|
3767
|
-
|
|
3763
|
+
const skillDir = join8(skillsDir, skill.name);
|
|
3764
|
+
mkdirSync3(skillDir, { recursive: true });
|
|
3768
3765
|
chownToAgent(skillDir);
|
|
3769
|
-
const skillPath =
|
|
3770
|
-
|
|
3766
|
+
const skillPath = join8(skillDir, "SKILL.md");
|
|
3767
|
+
writeFileSync6(skillPath, skill.body.endsWith("\n") ? skill.body : `${skill.body}
|
|
3771
3768
|
`, { mode: 384 });
|
|
3772
3769
|
chownToAgent(skillPath);
|
|
3773
3770
|
}
|
|
3774
|
-
|
|
3771
|
+
consola29.success("Sync complete.");
|
|
3775
3772
|
}
|
|
3776
3773
|
});
|
|
3777
3774
|
|
|
3778
3775
|
// src/commands/agents/index.ts
|
|
3779
|
-
var agentsCommand =
|
|
3776
|
+
var agentsCommand = defineCommand33({
|
|
3780
3777
|
meta: {
|
|
3781
3778
|
name: "agents",
|
|
3782
3779
|
description: "Manage owned agents (register, spawn, list, destroy, allow, sync, run, serve, code, cleanup-orphans)"
|
|
@@ -3796,22 +3793,22 @@ var agentsCommand = defineCommand32({
|
|
|
3796
3793
|
});
|
|
3797
3794
|
|
|
3798
3795
|
// src/commands/nest/index.ts
|
|
3799
|
-
import { defineCommand as
|
|
3796
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
3800
3797
|
|
|
3801
3798
|
// src/commands/nest/authorize.ts
|
|
3802
3799
|
import { execFileSync as execFileSync8 } from "child_process";
|
|
3803
|
-
import { existsSync as
|
|
3804
|
-
import { join as
|
|
3805
|
-
import { defineCommand as
|
|
3806
|
-
import
|
|
3800
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
3801
|
+
import { join as join10 } from "path";
|
|
3802
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
3803
|
+
import consola31 from "consola";
|
|
3807
3804
|
|
|
3808
3805
|
// src/commands/nest/enroll.ts
|
|
3809
|
-
import { hostname as hostname4, homedir as
|
|
3810
|
-
import { existsSync as
|
|
3811
|
-
import { join as
|
|
3812
|
-
import { defineCommand as
|
|
3813
|
-
import
|
|
3814
|
-
var NEST_DATA_DIR =
|
|
3806
|
+
import { hostname as hostname4, homedir as homedir10 } from "os";
|
|
3807
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, chmodSync } from "fs";
|
|
3808
|
+
import { join as join9 } from "path";
|
|
3809
|
+
import { defineCommand as defineCommand34 } from "citty";
|
|
3810
|
+
import consola30 from "consola";
|
|
3811
|
+
var NEST_DATA_DIR = join9(homedir10(), ".openape", "nest");
|
|
3815
3812
|
function nestAgentName() {
|
|
3816
3813
|
const raw = hostname4().toLowerCase();
|
|
3817
3814
|
const head = raw.split(".")[0] ?? raw;
|
|
@@ -3819,7 +3816,7 @@ function nestAgentName() {
|
|
|
3819
3816
|
const trimmed = safe.slice(0, 16);
|
|
3820
3817
|
return `nest-${trimmed || "host"}`;
|
|
3821
3818
|
}
|
|
3822
|
-
var enrollNestCommand =
|
|
3819
|
+
var enrollNestCommand = defineCommand34({
|
|
3823
3820
|
meta: {
|
|
3824
3821
|
name: "enroll",
|
|
3825
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."
|
|
@@ -3844,25 +3841,25 @@ var enrollNestCommand = defineCommand33({
|
|
|
3844
3841
|
throw new CliError("Run `apes login <email>` first \u2014 nest enroll attaches the new identity to your owner account.");
|
|
3845
3842
|
}
|
|
3846
3843
|
const name = args.name || nestAgentName();
|
|
3847
|
-
const authPath =
|
|
3848
|
-
if (
|
|
3844
|
+
const authPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
3845
|
+
if (existsSync13(authPath) && !args.force) {
|
|
3849
3846
|
throw new CliError(`Nest already enrolled at ${authPath}. Pass --force to re-enroll.`);
|
|
3850
3847
|
}
|
|
3851
|
-
const sshDir =
|
|
3852
|
-
const configDir =
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3848
|
+
const sshDir = join9(NEST_DATA_DIR, ".ssh");
|
|
3849
|
+
const configDir = join9(NEST_DATA_DIR, ".config", "apes");
|
|
3850
|
+
mkdirSync4(sshDir, { recursive: true });
|
|
3851
|
+
mkdirSync4(configDir, { recursive: true });
|
|
3852
|
+
consola30.start(`Generating keypair for ${name}\u2026`);
|
|
3856
3853
|
const { privatePem, publicSshLine } = generateKeyPairInMemory();
|
|
3857
|
-
|
|
3854
|
+
writeFileSync7(join9(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
|
|
3858
3855
|
`, { mode: 384 });
|
|
3859
|
-
|
|
3856
|
+
writeFileSync7(join9(sshDir, "id_ed25519.pub"), `${publicSshLine}
|
|
3860
3857
|
`, { mode: 420 });
|
|
3861
3858
|
chmodSync(sshDir, 448);
|
|
3862
|
-
|
|
3859
|
+
consola30.start(`Registering nest at ${idp}\u2026`);
|
|
3863
3860
|
const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
|
|
3864
|
-
|
|
3865
|
-
|
|
3861
|
+
consola30.success(`Registered as ${registration.email}`);
|
|
3862
|
+
consola30.start("Issuing nest access token\u2026");
|
|
3866
3863
|
const { token, expiresIn } = await issueAgentToken({
|
|
3867
3864
|
idp,
|
|
3868
3865
|
agentEmail: registration.email,
|
|
@@ -3873,16 +3870,16 @@ var enrollNestCommand = defineCommand33({
|
|
|
3873
3870
|
accessToken: token,
|
|
3874
3871
|
email: registration.email,
|
|
3875
3872
|
expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
|
|
3876
|
-
keyPath:
|
|
3873
|
+
keyPath: join9(sshDir, "id_ed25519"),
|
|
3877
3874
|
ownerEmail: ownerAuth.email
|
|
3878
3875
|
});
|
|
3879
|
-
|
|
3876
|
+
writeFileSync7(authPath, authJson, { mode: 384 });
|
|
3880
3877
|
chmodSync(configDir, 448);
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
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");
|
|
3886
3883
|
}
|
|
3887
3884
|
});
|
|
3888
3885
|
|
|
@@ -3929,7 +3926,7 @@ var DEFAULT_ALLOW_PATTERNS = [
|
|
|
3929
3926
|
"pm2 delete openape-bridge-*",
|
|
3930
3927
|
"pm2 jlist"
|
|
3931
3928
|
];
|
|
3932
|
-
var authorizeNestCommand =
|
|
3929
|
+
var authorizeNestCommand = defineCommand35({
|
|
3933
3930
|
meta: {
|
|
3934
3931
|
name: "authorize",
|
|
3935
3932
|
description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts (wraps `apes yolo set`)"
|
|
@@ -3945,14 +3942,14 @@ var authorizeNestCommand = defineCommand34({
|
|
|
3945
3942
|
}
|
|
3946
3943
|
},
|
|
3947
3944
|
async run({ args }) {
|
|
3948
|
-
const nestAuthPath =
|
|
3949
|
-
if (!
|
|
3945
|
+
const nestAuthPath = join10(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
3946
|
+
if (!existsSync14(nestAuthPath)) {
|
|
3950
3947
|
throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
|
|
3951
3948
|
}
|
|
3952
|
-
const nestAuth = JSON.parse(
|
|
3949
|
+
const nestAuth = JSON.parse(readFileSync12(nestAuthPath, "utf8"));
|
|
3953
3950
|
if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
|
|
3954
|
-
const
|
|
3955
|
-
|
|
3951
|
+
const allow2 = args.allow ?? DEFAULT_ALLOW_PATTERNS.join(",");
|
|
3952
|
+
consola31.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
|
|
3956
3953
|
const cmdArgs = [
|
|
3957
3954
|
"yolo",
|
|
3958
3955
|
"set",
|
|
@@ -3960,7 +3957,7 @@ var authorizeNestCommand = defineCommand34({
|
|
|
3960
3957
|
"--mode",
|
|
3961
3958
|
"allow-list",
|
|
3962
3959
|
"--allow",
|
|
3963
|
-
|
|
3960
|
+
allow2
|
|
3964
3961
|
];
|
|
3965
3962
|
if (typeof args["expires-in"] === "string" && args["expires-in"]) {
|
|
3966
3963
|
cmdArgs.push("--expires-in", args["expires-in"]);
|
|
@@ -3970,16 +3967,16 @@ var authorizeNestCommand = defineCommand34({
|
|
|
3970
3967
|
} catch (err) {
|
|
3971
3968
|
throw new CliError(err instanceof Error ? err.message : String(err));
|
|
3972
3969
|
}
|
|
3973
|
-
|
|
3974
|
-
|
|
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.");
|
|
3975
3972
|
}
|
|
3976
3973
|
});
|
|
3977
3974
|
|
|
3978
3975
|
// src/commands/nest/destroy.ts
|
|
3979
3976
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
3980
|
-
import { defineCommand as
|
|
3981
|
-
import
|
|
3982
|
-
var destroyNestCommand =
|
|
3977
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
3978
|
+
import consola32 from "consola";
|
|
3979
|
+
var destroyNestCommand = defineCommand36({
|
|
3983
3980
|
meta: {
|
|
3984
3981
|
name: "destroy",
|
|
3985
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."
|
|
@@ -3991,7 +3988,7 @@ var destroyNestCommand = defineCommand35({
|
|
|
3991
3988
|
const name = String(args.name);
|
|
3992
3989
|
try {
|
|
3993
3990
|
execFileSync9("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
|
|
3994
|
-
|
|
3991
|
+
consola32.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
|
|
3995
3992
|
} catch (err) {
|
|
3996
3993
|
const status = err.status ?? 1;
|
|
3997
3994
|
throw new CliExit(status);
|
|
@@ -4000,11 +3997,11 @@ var destroyNestCommand = defineCommand35({
|
|
|
4000
3997
|
});
|
|
4001
3998
|
|
|
4002
3999
|
// src/commands/nest/install.ts
|
|
4003
|
-
import { existsSync as
|
|
4004
|
-
import { homedir as
|
|
4005
|
-
import { dirname as
|
|
4006
|
-
import { defineCommand as
|
|
4007
|
-
import
|
|
4000
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
|
|
4001
|
+
import { homedir as homedir11 } from "os";
|
|
4002
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
4003
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
4004
|
+
import consola33 from "consola";
|
|
4008
4005
|
|
|
4009
4006
|
// src/commands/nest/apes-agents-adapter.ts
|
|
4010
4007
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4070,45 +4067,45 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
4070
4067
|
|
|
4071
4068
|
// src/commands/nest/install.ts
|
|
4072
4069
|
function installAdapter2() {
|
|
4073
|
-
const target =
|
|
4074
|
-
|
|
4070
|
+
const target = join11(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4071
|
+
mkdirSync5(dirname2(target), { recursive: true });
|
|
4075
4072
|
let existing = "";
|
|
4076
4073
|
try {
|
|
4077
|
-
existing =
|
|
4074
|
+
existing = readFileSync13(target, "utf8");
|
|
4078
4075
|
} catch {
|
|
4079
4076
|
}
|
|
4080
4077
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4081
|
-
|
|
4082
|
-
|
|
4078
|
+
writeFileSync8(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4079
|
+
consola33.success(`Wrote shapes adapter ${target}`);
|
|
4083
4080
|
return true;
|
|
4084
4081
|
}
|
|
4085
4082
|
function writeBridgeModelDefault(model) {
|
|
4086
|
-
for (const envDir of [
|
|
4087
|
-
const envFile =
|
|
4088
|
-
|
|
4083
|
+
for (const envDir of [join11(homedir11(), "litellm"), join11(NEST_DATA_DIR, "litellm")]) {
|
|
4084
|
+
const envFile = join11(envDir, ".env");
|
|
4085
|
+
mkdirSync5(envDir, { recursive: true });
|
|
4089
4086
|
let lines = [];
|
|
4090
|
-
if (
|
|
4091
|
-
lines =
|
|
4087
|
+
if (existsSync15(envFile)) {
|
|
4088
|
+
lines = readFileSync13(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
|
|
4092
4089
|
}
|
|
4093
4090
|
lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
|
|
4094
4091
|
while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
|
|
4095
|
-
|
|
4092
|
+
writeFileSync8(envFile, `${lines.join("\n")}
|
|
4096
4093
|
`, { mode: 384 });
|
|
4097
4094
|
}
|
|
4098
4095
|
}
|
|
4099
4096
|
function findBinary(name) {
|
|
4100
4097
|
for (const dir of [
|
|
4101
|
-
|
|
4098
|
+
join11(homedir11(), ".bun", "bin"),
|
|
4102
4099
|
"/opt/homebrew/bin",
|
|
4103
4100
|
"/usr/local/bin",
|
|
4104
4101
|
"/usr/bin"
|
|
4105
4102
|
]) {
|
|
4106
|
-
const p =
|
|
4107
|
-
if (
|
|
4103
|
+
const p = join11(dir, name);
|
|
4104
|
+
if (existsSync15(p)) return p;
|
|
4108
4105
|
}
|
|
4109
4106
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4110
4107
|
}
|
|
4111
|
-
var installNestCommand =
|
|
4108
|
+
var installNestCommand = defineCommand37({
|
|
4112
4109
|
meta: {
|
|
4113
4110
|
name: "install",
|
|
4114
4111
|
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
@@ -4124,23 +4121,23 @@ var installNestCommand = defineCommand36({
|
|
|
4124
4121
|
}
|
|
4125
4122
|
},
|
|
4126
4123
|
async run({ args }) {
|
|
4127
|
-
const homeDir =
|
|
4124
|
+
const homeDir = homedir11();
|
|
4128
4125
|
const port = Number(args.port ?? 9091);
|
|
4129
4126
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
4130
4127
|
throw new Error(`invalid port ${port}`);
|
|
4131
4128
|
}
|
|
4132
4129
|
const nestBin = findBinary("openape-nest");
|
|
4133
4130
|
const apesBin = findBinary("apes");
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4131
|
+
consola33.info(`Installing nest supervisor`);
|
|
4132
|
+
consola33.info(` nest binary: ${nestBin}`);
|
|
4133
|
+
consola33.info(` apes binary: ${apesBin}`);
|
|
4134
|
+
consola33.info(` HTTP port: ${port}`);
|
|
4138
4135
|
if (typeof args["bridge-model"] === "string" && args["bridge-model"]) {
|
|
4139
4136
|
writeBridgeModelDefault(args["bridge-model"]);
|
|
4140
|
-
|
|
4137
|
+
consola33.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
|
|
4141
4138
|
}
|
|
4142
4139
|
installAdapter2();
|
|
4143
|
-
|
|
4140
|
+
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
4144
4141
|
await getHostPlatform().installNestSupervisor({
|
|
4145
4142
|
nestBin,
|
|
4146
4143
|
apesBin,
|
|
@@ -4148,21 +4145,21 @@ var installNestCommand = defineCommand36({
|
|
|
4148
4145
|
nestHome: NEST_DATA_DIR,
|
|
4149
4146
|
port
|
|
4150
4147
|
});
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
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.");
|
|
4159
4156
|
}
|
|
4160
4157
|
});
|
|
4161
4158
|
|
|
4162
4159
|
// src/commands/nest/list.ts
|
|
4163
|
-
import { defineCommand as
|
|
4164
|
-
import
|
|
4165
|
-
var listNestCommand =
|
|
4160
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
4161
|
+
import consola34 from "consola";
|
|
4162
|
+
var listNestCommand = defineCommand38({
|
|
4166
4163
|
meta: {
|
|
4167
4164
|
name: "list",
|
|
4168
4165
|
description: "List agents registered with the local nest. Reads /var/openape/nest/agents.json directly."
|
|
@@ -4177,22 +4174,22 @@ var listNestCommand = defineCommand37({
|
|
|
4177
4174
|
return;
|
|
4178
4175
|
}
|
|
4179
4176
|
if (reg.agents.length === 0) {
|
|
4180
|
-
|
|
4177
|
+
consola34.info("(no agents registered with this nest)");
|
|
4181
4178
|
return;
|
|
4182
4179
|
}
|
|
4183
|
-
|
|
4180
|
+
consola34.info(`${reg.agents.length} agent(s) registered with this nest:`);
|
|
4184
4181
|
for (const a of reg.agents) {
|
|
4185
4182
|
const bridge = a.bridge ? " bridge=on" : "";
|
|
4186
|
-
|
|
4183
|
+
consola34.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
4187
4184
|
}
|
|
4188
4185
|
}
|
|
4189
4186
|
});
|
|
4190
4187
|
|
|
4191
4188
|
// src/commands/nest/spawn.ts
|
|
4192
4189
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4193
|
-
import { defineCommand as
|
|
4194
|
-
import
|
|
4195
|
-
var spawnNestCommand =
|
|
4190
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
4191
|
+
import consola35 from "consola";
|
|
4192
|
+
var spawnNestCommand = defineCommand39({
|
|
4196
4193
|
meta: {
|
|
4197
4194
|
name: "spawn",
|
|
4198
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."
|
|
@@ -4223,7 +4220,7 @@ var spawnNestCommand = defineCommand38({
|
|
|
4223
4220
|
if (typeof args["bridge-model"] === "string") apesArgs.push("--bridge-model", args["bridge-model"]);
|
|
4224
4221
|
try {
|
|
4225
4222
|
execFileSync10("apes", apesArgs, { stdio: "inherit" });
|
|
4226
|
-
|
|
4223
|
+
consola35.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
|
|
4227
4224
|
} catch (err) {
|
|
4228
4225
|
const status = err.status ?? 1;
|
|
4229
4226
|
throw new CliExit(status);
|
|
@@ -4232,22 +4229,22 @@ var spawnNestCommand = defineCommand38({
|
|
|
4232
4229
|
});
|
|
4233
4230
|
|
|
4234
4231
|
// src/commands/nest/uninstall.ts
|
|
4235
|
-
import { defineCommand as
|
|
4236
|
-
import
|
|
4237
|
-
var uninstallNestCommand =
|
|
4232
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
4233
|
+
import consola36 from "consola";
|
|
4234
|
+
var uninstallNestCommand = defineCommand40({
|
|
4238
4235
|
meta: {
|
|
4239
4236
|
name: "uninstall",
|
|
4240
4237
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4241
4238
|
},
|
|
4242
4239
|
async run() {
|
|
4243
4240
|
await getHostPlatform().uninstallNestSupervisor();
|
|
4244
|
-
|
|
4245
|
-
|
|
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.");
|
|
4246
4243
|
}
|
|
4247
4244
|
});
|
|
4248
4245
|
|
|
4249
4246
|
// src/commands/nest/index.ts
|
|
4250
|
-
var nestCommand =
|
|
4247
|
+
var nestCommand = defineCommand41({
|
|
4251
4248
|
meta: {
|
|
4252
4249
|
name: "nest",
|
|
4253
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."
|
|
@@ -4264,12 +4261,12 @@ var nestCommand = defineCommand40({
|
|
|
4264
4261
|
});
|
|
4265
4262
|
|
|
4266
4263
|
// src/commands/yolo/index.ts
|
|
4267
|
-
import { defineCommand as
|
|
4264
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
4268
4265
|
|
|
4269
4266
|
// src/commands/yolo/clear.ts
|
|
4270
|
-
import { defineCommand as
|
|
4271
|
-
import
|
|
4272
|
-
var yoloClearCommand =
|
|
4267
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
4268
|
+
import consola37 from "consola";
|
|
4269
|
+
var yoloClearCommand = defineCommand42({
|
|
4273
4270
|
meta: {
|
|
4274
4271
|
name: "clear",
|
|
4275
4272
|
description: "Remove the YOLO-policy from a DDISA agent (subsequent grants need human approval)"
|
|
@@ -4298,15 +4295,15 @@ var yoloClearCommand = defineCommand41({
|
|
|
4298
4295
|
const text = await res.text().catch(() => "");
|
|
4299
4296
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
4300
4297
|
}
|
|
4301
|
-
|
|
4298
|
+
consola37.success(`YOLO-policy cleared on ${email}`);
|
|
4302
4299
|
}
|
|
4303
4300
|
});
|
|
4304
4301
|
|
|
4305
4302
|
// src/commands/yolo/set.ts
|
|
4306
|
-
import { defineCommand as
|
|
4307
|
-
import
|
|
4303
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
4304
|
+
import consola38 from "consola";
|
|
4308
4305
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
4309
|
-
var yoloSetCommand =
|
|
4306
|
+
var yoloSetCommand = defineCommand43({
|
|
4310
4307
|
meta: {
|
|
4311
4308
|
name: "set",
|
|
4312
4309
|
description: "Write a YOLO-policy on a DDISA agent you own"
|
|
@@ -4354,12 +4351,12 @@ var yoloSetCommand = defineCommand42({
|
|
|
4354
4351
|
const denyPatterns = parseList(args.deny);
|
|
4355
4352
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
4356
4353
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
if (allowPatterns.length)
|
|
4360
|
-
if (denyPatterns.length)
|
|
4361
|
-
if (denyRiskThreshold)
|
|
4362
|
-
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()}`);
|
|
4363
4360
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
4364
4361
|
const res = await fetch(url, {
|
|
4365
4362
|
method: "PUT",
|
|
@@ -4379,7 +4376,7 @@ var yoloSetCommand = defineCommand42({
|
|
|
4379
4376
|
const text = await res.text().catch(() => "");
|
|
4380
4377
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
4381
4378
|
}
|
|
4382
|
-
|
|
4379
|
+
consola38.success(`YOLO-policy applied to ${email}`);
|
|
4383
4380
|
}
|
|
4384
4381
|
});
|
|
4385
4382
|
function parseList(s) {
|
|
@@ -4397,9 +4394,9 @@ function parseExpiresIn(s) {
|
|
|
4397
4394
|
}
|
|
4398
4395
|
|
|
4399
4396
|
// src/commands/yolo/show.ts
|
|
4400
|
-
import { defineCommand as
|
|
4401
|
-
import
|
|
4402
|
-
var yoloShowCommand =
|
|
4397
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
4398
|
+
import consola39 from "consola";
|
|
4399
|
+
var yoloShowCommand = defineCommand44({
|
|
4403
4400
|
meta: {
|
|
4404
4401
|
name: "show",
|
|
4405
4402
|
description: "Print the YOLO-policy currently set on a DDISA agent"
|
|
@@ -4436,17 +4433,17 @@ var yoloShowCommand = defineCommand43({
|
|
|
4436
4433
|
console.log(JSON.stringify(policy, null, 2));
|
|
4437
4434
|
return;
|
|
4438
4435
|
}
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
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)"}`);
|
|
4445
4442
|
}
|
|
4446
4443
|
});
|
|
4447
4444
|
|
|
4448
4445
|
// src/commands/yolo/index.ts
|
|
4449
|
-
var yoloCommand =
|
|
4446
|
+
var yoloCommand = defineCommand45({
|
|
4450
4447
|
meta: {
|
|
4451
4448
|
name: "yolo",
|
|
4452
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)."
|
|
@@ -4459,15 +4456,15 @@ var yoloCommand = defineCommand44({
|
|
|
4459
4456
|
});
|
|
4460
4457
|
|
|
4461
4458
|
// src/commands/adapter/index.ts
|
|
4462
|
-
import { defineCommand as
|
|
4463
|
-
import
|
|
4464
|
-
var adapterCommand =
|
|
4459
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
4460
|
+
import consola40 from "consola";
|
|
4461
|
+
var adapterCommand = defineCommand46({
|
|
4465
4462
|
meta: {
|
|
4466
4463
|
name: "adapter",
|
|
4467
4464
|
description: "Manage CLI adapters"
|
|
4468
4465
|
},
|
|
4469
4466
|
subCommands: {
|
|
4470
|
-
list:
|
|
4467
|
+
list: defineCommand46({
|
|
4471
4468
|
meta: {
|
|
4472
4469
|
name: "list",
|
|
4473
4470
|
description: "List available adapters"
|
|
@@ -4498,7 +4495,7 @@ var adapterCommand = defineCommand45({
|
|
|
4498
4495
|
`);
|
|
4499
4496
|
return;
|
|
4500
4497
|
}
|
|
4501
|
-
|
|
4498
|
+
consola40.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4502
4499
|
for (const a of index2.adapters) {
|
|
4503
4500
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4504
4501
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4520,7 +4517,7 @@ var adapterCommand = defineCommand45({
|
|
|
4520
4517
|
return;
|
|
4521
4518
|
}
|
|
4522
4519
|
if (local.length === 0) {
|
|
4523
|
-
|
|
4520
|
+
consola40.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4524
4521
|
return;
|
|
4525
4522
|
}
|
|
4526
4523
|
for (const a of local) {
|
|
@@ -4528,7 +4525,7 @@ var adapterCommand = defineCommand45({
|
|
|
4528
4525
|
}
|
|
4529
4526
|
}
|
|
4530
4527
|
}),
|
|
4531
|
-
install:
|
|
4528
|
+
install: defineCommand46({
|
|
4532
4529
|
meta: {
|
|
4533
4530
|
name: "install",
|
|
4534
4531
|
description: "Install an adapter from the registry"
|
|
@@ -4557,24 +4554,24 @@ var adapterCommand = defineCommand45({
|
|
|
4557
4554
|
for (const id of ids) {
|
|
4558
4555
|
const entry = findAdapter(index, id);
|
|
4559
4556
|
if (!entry) {
|
|
4560
|
-
|
|
4557
|
+
consola40.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4561
4558
|
continue;
|
|
4562
4559
|
}
|
|
4563
4560
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4564
4561
|
if (conflicts.length > 0) {
|
|
4565
4562
|
for (const c of conflicts) {
|
|
4566
|
-
|
|
4567
|
-
|
|
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}`);
|
|
4568
4565
|
}
|
|
4569
4566
|
}
|
|
4570
4567
|
const result = await installAdapter(entry, { local });
|
|
4571
4568
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4572
|
-
|
|
4573
|
-
|
|
4569
|
+
consola40.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
4570
|
+
consola40.info(`Digest: ${result.digest}`);
|
|
4574
4571
|
}
|
|
4575
4572
|
}
|
|
4576
4573
|
}),
|
|
4577
|
-
remove:
|
|
4574
|
+
remove: defineCommand46({
|
|
4578
4575
|
meta: {
|
|
4579
4576
|
name: "remove",
|
|
4580
4577
|
description: "Remove an installed adapter"
|
|
@@ -4597,9 +4594,9 @@ var adapterCommand = defineCommand45({
|
|
|
4597
4594
|
let failed = false;
|
|
4598
4595
|
for (const id of ids) {
|
|
4599
4596
|
if (removeAdapter(id, local)) {
|
|
4600
|
-
|
|
4597
|
+
consola40.success(`Removed adapter: ${id}`);
|
|
4601
4598
|
} else {
|
|
4602
|
-
|
|
4599
|
+
consola40.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4603
4600
|
failed = true;
|
|
4604
4601
|
}
|
|
4605
4602
|
}
|
|
@@ -4607,7 +4604,7 @@ var adapterCommand = defineCommand45({
|
|
|
4607
4604
|
throw new CliError("Some adapters could not be removed");
|
|
4608
4605
|
}
|
|
4609
4606
|
}),
|
|
4610
|
-
info:
|
|
4607
|
+
info: defineCommand46({
|
|
4611
4608
|
meta: {
|
|
4612
4609
|
name: "info",
|
|
4613
4610
|
description: "Show detailed adapter information"
|
|
@@ -4649,7 +4646,7 @@ var adapterCommand = defineCommand45({
|
|
|
4649
4646
|
}
|
|
4650
4647
|
}
|
|
4651
4648
|
}),
|
|
4652
|
-
search:
|
|
4649
|
+
search: defineCommand46({
|
|
4653
4650
|
meta: {
|
|
4654
4651
|
name: "search",
|
|
4655
4652
|
description: "Search adapters in the registry"
|
|
@@ -4681,7 +4678,7 @@ var adapterCommand = defineCommand45({
|
|
|
4681
4678
|
return;
|
|
4682
4679
|
}
|
|
4683
4680
|
if (results.length === 0) {
|
|
4684
|
-
|
|
4681
|
+
consola40.info(`No adapters matching "${query}"`);
|
|
4685
4682
|
return;
|
|
4686
4683
|
}
|
|
4687
4684
|
for (const a of results) {
|
|
@@ -4690,7 +4687,7 @@ var adapterCommand = defineCommand45({
|
|
|
4690
4687
|
}
|
|
4691
4688
|
}
|
|
4692
4689
|
}),
|
|
4693
|
-
update:
|
|
4690
|
+
update: defineCommand46({
|
|
4694
4691
|
meta: {
|
|
4695
4692
|
name: "update",
|
|
4696
4693
|
description: "Update installed adapters"
|
|
@@ -4716,33 +4713,33 @@ var adapterCommand = defineCommand45({
|
|
|
4716
4713
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4717
4714
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4718
4715
|
if (targets.length === 0) {
|
|
4719
|
-
|
|
4716
|
+
consola40.info("No adapters installed to update.");
|
|
4720
4717
|
return;
|
|
4721
4718
|
}
|
|
4722
4719
|
for (const id of targets) {
|
|
4723
4720
|
const entry = findAdapter(index, id);
|
|
4724
4721
|
if (!entry) {
|
|
4725
|
-
|
|
4722
|
+
consola40.warn(`${id}: not found in registry, skipping`);
|
|
4726
4723
|
continue;
|
|
4727
4724
|
}
|
|
4728
4725
|
const localDigest = getInstalledDigest(id, false);
|
|
4729
4726
|
if (localDigest === entry.digest) {
|
|
4730
|
-
|
|
4727
|
+
consola40.info(`${id}: already up to date`);
|
|
4731
4728
|
continue;
|
|
4732
4729
|
}
|
|
4733
4730
|
if (localDigest && !args.yes) {
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
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");
|
|
4738
4735
|
continue;
|
|
4739
4736
|
}
|
|
4740
4737
|
const result = await installAdapter(entry);
|
|
4741
|
-
|
|
4738
|
+
consola40.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4742
4739
|
}
|
|
4743
4740
|
}
|
|
4744
4741
|
}),
|
|
4745
|
-
verify:
|
|
4742
|
+
verify: defineCommand46({
|
|
4746
4743
|
meta: {
|
|
4747
4744
|
name: "verify",
|
|
4748
4745
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4775,7 +4772,7 @@ var adapterCommand = defineCommand45({
|
|
|
4775
4772
|
if (!localDigest)
|
|
4776
4773
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4777
4774
|
if (localDigest === entry.digest) {
|
|
4778
|
-
|
|
4775
|
+
consola40.success(`${id}: digest matches registry`);
|
|
4779
4776
|
} else {
|
|
4780
4777
|
console.log(` Local: ${localDigest}`);
|
|
4781
4778
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4790,8 +4787,8 @@ var adapterCommand = defineCommand45({
|
|
|
4790
4787
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4791
4788
|
import { hostname as hostname5 } from "os";
|
|
4792
4789
|
import { basename } from "path";
|
|
4793
|
-
import { defineCommand as
|
|
4794
|
-
import
|
|
4790
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
4791
|
+
import consola41 from "consola";
|
|
4795
4792
|
function resolveRunAsTarget(runAs) {
|
|
4796
4793
|
if (!runAs) return runAs;
|
|
4797
4794
|
if (!AGENT_NAME_REGEX.test(runAs)) return runAs;
|
|
@@ -4847,7 +4844,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4847
4844
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4848
4845
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4849
4846
|
if (mode === "human") {
|
|
4850
|
-
|
|
4847
|
+
consola41.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4851
4848
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4852
4849
|
console.log(` Check status: ${statusCmd}`);
|
|
4853
4850
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4857,7 +4854,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4857
4854
|
return;
|
|
4858
4855
|
}
|
|
4859
4856
|
const maxMin = getPollMaxMinutes();
|
|
4860
|
-
|
|
4857
|
+
consola41.success(`Grant ${grant.id} created (pending approval)`);
|
|
4861
4858
|
console.log(` Approve: ${approveUrl}`);
|
|
4862
4859
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4863
4860
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4879,7 +4876,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4879
4876
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4880
4877
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4881
4878
|
}
|
|
4882
|
-
var runCommand =
|
|
4879
|
+
var runCommand = defineCommand47({
|
|
4883
4880
|
meta: {
|
|
4884
4881
|
name: "run",
|
|
4885
4882
|
description: "Execute a grant-secured command"
|
|
@@ -4985,7 +4982,7 @@ async function runShellMode(command, args) {
|
|
|
4985
4982
|
}
|
|
4986
4983
|
} catch {
|
|
4987
4984
|
}
|
|
4988
|
-
|
|
4985
|
+
consola41.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
4989
4986
|
const grant = await apiFetch(grantsUrl, {
|
|
4990
4987
|
method: "POST",
|
|
4991
4988
|
body: {
|
|
@@ -5005,8 +5002,8 @@ async function runShellMode(command, args) {
|
|
|
5005
5002
|
host: targetHost
|
|
5006
5003
|
});
|
|
5007
5004
|
if (shouldWaitForGrant(args)) {
|
|
5008
|
-
|
|
5009
|
-
|
|
5005
|
+
consola41.info(`Grant requested: ${grant.id}`);
|
|
5006
|
+
consola41.info("Waiting for approval...");
|
|
5010
5007
|
const maxWait = 3e5;
|
|
5011
5008
|
const interval = 3e3;
|
|
5012
5009
|
const start = Date.now();
|
|
@@ -5037,13 +5034,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5037
5034
|
try {
|
|
5038
5035
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
5039
5036
|
} catch (err) {
|
|
5040
|
-
|
|
5037
|
+
consola41.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
5041
5038
|
return false;
|
|
5042
5039
|
}
|
|
5043
5040
|
try {
|
|
5044
5041
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5045
5042
|
if (existingGrantId) {
|
|
5046
|
-
|
|
5043
|
+
consola41.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
5047
5044
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5048
5045
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5049
5046
|
return true;
|
|
@@ -5051,7 +5048,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5051
5048
|
} catch {
|
|
5052
5049
|
}
|
|
5053
5050
|
const approval = args.approval ?? "once";
|
|
5054
|
-
|
|
5051
|
+
consola41.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
5055
5052
|
const grant = await createShapesGrant(resolved, {
|
|
5056
5053
|
idp,
|
|
5057
5054
|
approval,
|
|
@@ -5059,8 +5056,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5059
5056
|
});
|
|
5060
5057
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5061
5058
|
const n = grant.similar_grants.similar_grants.length;
|
|
5062
|
-
|
|
5063
|
-
|
|
5059
|
+
consola41.info("");
|
|
5060
|
+
consola41.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
5064
5061
|
}
|
|
5065
5062
|
notifyGrantPending({
|
|
5066
5063
|
grantId: grant.id,
|
|
@@ -5070,8 +5067,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5070
5067
|
host: args.host || hostname5()
|
|
5071
5068
|
});
|
|
5072
5069
|
if (shouldWaitForGrant(args)) {
|
|
5073
|
-
|
|
5074
|
-
|
|
5070
|
+
consola41.info(`Grant requested: ${grant.id}`);
|
|
5071
|
+
consola41.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5075
5072
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5076
5073
|
if (status !== "approved")
|
|
5077
5074
|
throw new CliError(`Grant ${status}`);
|
|
@@ -5145,7 +5142,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5145
5142
|
try {
|
|
5146
5143
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5147
5144
|
if (existingGrantId) {
|
|
5148
|
-
|
|
5145
|
+
consola41.info(`Reusing existing grant: ${existingGrantId}`);
|
|
5149
5146
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5150
5147
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5151
5148
|
return;
|
|
@@ -5159,17 +5156,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5159
5156
|
});
|
|
5160
5157
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5161
5158
|
const n = grant.similar_grants.similar_grants.length;
|
|
5162
|
-
|
|
5163
|
-
|
|
5159
|
+
consola41.info("");
|
|
5160
|
+
consola41.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
5164
5161
|
if (grant.similar_grants.widened_details?.length) {
|
|
5165
5162
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
5166
|
-
|
|
5163
|
+
consola41.info(` Broader scope: ${wider}`);
|
|
5167
5164
|
}
|
|
5168
|
-
|
|
5165
|
+
consola41.info("");
|
|
5169
5166
|
}
|
|
5170
5167
|
if (shouldWaitForGrant(args)) {
|
|
5171
|
-
|
|
5172
|
-
|
|
5168
|
+
consola41.info(`Grant requested: ${grant.id}`);
|
|
5169
|
+
consola41.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5173
5170
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5174
5171
|
if (status !== "approved")
|
|
5175
5172
|
throw new Error(`Grant ${status}`);
|
|
@@ -5202,7 +5199,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5202
5199
|
const { authz_jwt: authz_jwt2 } = await apiFetch(`${grantsUrl}/${reusableId}/token`, { method: "POST" });
|
|
5203
5200
|
return executeWithGrantToken({ audience, command, args, token: authz_jwt2 });
|
|
5204
5201
|
}
|
|
5205
|
-
|
|
5202
|
+
consola41.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5206
5203
|
const grant = await apiFetch(grantsUrl, {
|
|
5207
5204
|
method: "POST",
|
|
5208
5205
|
body: {
|
|
@@ -5219,9 +5216,9 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5219
5216
|
printPendingGrantInfo(grant, idp);
|
|
5220
5217
|
throw new CliExit(getAsyncExitCode());
|
|
5221
5218
|
}
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
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...");
|
|
5225
5222
|
const maxWait = 15 * 60 * 1e3;
|
|
5226
5223
|
const interval = 3e3;
|
|
5227
5224
|
const start = Date.now();
|
|
@@ -5229,7 +5226,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5229
5226
|
while (Date.now() - start < maxWait) {
|
|
5230
5227
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5231
5228
|
if (status.status === "approved") {
|
|
5232
|
-
|
|
5229
|
+
consola41.success("Grant approved!");
|
|
5233
5230
|
approved = true;
|
|
5234
5231
|
break;
|
|
5235
5232
|
}
|
|
@@ -5244,7 +5241,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5244
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.`
|
|
5245
5242
|
);
|
|
5246
5243
|
}
|
|
5247
|
-
|
|
5244
|
+
consola41.info("Fetching grant token...");
|
|
5248
5245
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5249
5246
|
method: "POST"
|
|
5250
5247
|
});
|
|
@@ -5253,7 +5250,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5253
5250
|
function executeWithGrantToken(opts) {
|
|
5254
5251
|
const { audience, command, args, token } = opts;
|
|
5255
5252
|
if (audience === "escapes") {
|
|
5256
|
-
|
|
5253
|
+
consola41.info(`Executing: ${command.join(" ")}`);
|
|
5257
5254
|
try {
|
|
5258
5255
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5259
5256
|
execFileSync11(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
|
|
@@ -5292,11 +5289,11 @@ async function findReusableAudienceGrant(opts) {
|
|
|
5292
5289
|
|
|
5293
5290
|
// src/commands/proxy.ts
|
|
5294
5291
|
import { spawn as spawn2 } from "child_process";
|
|
5295
|
-
import { existsSync as
|
|
5296
|
-
import { homedir as
|
|
5297
|
-
import { join as
|
|
5298
|
-
import { defineCommand as
|
|
5299
|
-
import
|
|
5292
|
+
import { existsSync as existsSync17 } from "fs";
|
|
5293
|
+
import { homedir as homedir12 } from "os";
|
|
5294
|
+
import { join as join14 } from "path";
|
|
5295
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
5296
|
+
import consola42 from "consola";
|
|
5300
5297
|
|
|
5301
5298
|
// src/proxy/config.ts
|
|
5302
5299
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5330,10 +5327,10 @@ note = "VPC-internal hostname suffix"
|
|
|
5330
5327
|
|
|
5331
5328
|
// src/proxy/local-proxy.ts
|
|
5332
5329
|
import { spawn } from "child_process";
|
|
5333
|
-
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as
|
|
5330
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
5334
5331
|
import { createRequire } from "module";
|
|
5335
5332
|
import { tmpdir as tmpdir2 } from "os";
|
|
5336
|
-
import { dirname as
|
|
5333
|
+
import { dirname as dirname3, join as join12, resolve as resolve4 } from "path";
|
|
5337
5334
|
var require2 = createRequire(import.meta.url);
|
|
5338
5335
|
function findProxyBin() {
|
|
5339
5336
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -5342,12 +5339,12 @@ function findProxyBin() {
|
|
|
5342
5339
|
if (!binRel) {
|
|
5343
5340
|
throw new Error("@openape/proxy is missing the openape-proxy bin entry");
|
|
5344
5341
|
}
|
|
5345
|
-
return resolve4(
|
|
5342
|
+
return resolve4(dirname3(pkgPath), binRel);
|
|
5346
5343
|
}
|
|
5347
5344
|
async function startEphemeralProxy(configToml) {
|
|
5348
|
-
const tmpDir = mkdtempSync2(
|
|
5349
|
-
const configPath =
|
|
5350
|
-
|
|
5345
|
+
const tmpDir = mkdtempSync2(join12(tmpdir2(), "openape-proxy-"));
|
|
5346
|
+
const configPath = join12(tmpDir, "config.toml");
|
|
5347
|
+
writeFileSync9(configPath, configToml, { mode: 384 });
|
|
5351
5348
|
const binPath = findProxyBin();
|
|
5352
5349
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
5353
5350
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5429,9 +5426,9 @@ function waitForListenLine(child) {
|
|
|
5429
5426
|
}
|
|
5430
5427
|
|
|
5431
5428
|
// src/proxy/trust-bundle.ts
|
|
5432
|
-
import { existsSync as
|
|
5429
|
+
import { existsSync as existsSync16, mkdtempSync as mkdtempSync3, readFileSync as readFileSync14, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
|
|
5433
5430
|
import { tmpdir as tmpdir3 } from "os";
|
|
5434
|
-
import { join as
|
|
5431
|
+
import { join as join13 } from "path";
|
|
5435
5432
|
var CANDIDATES = [
|
|
5436
5433
|
"/etc/ssl/cert.pem",
|
|
5437
5434
|
// macOS
|
|
@@ -5444,18 +5441,18 @@ var CANDIDATES = [
|
|
|
5444
5441
|
];
|
|
5445
5442
|
function detectSystemCaPath() {
|
|
5446
5443
|
for (const p of CANDIDATES) {
|
|
5447
|
-
if (
|
|
5444
|
+
if (existsSync16(p)) return p;
|
|
5448
5445
|
}
|
|
5449
5446
|
throw new Error(
|
|
5450
5447
|
`Could not locate a system CA bundle. Tried: ${CANDIDATES.join(", ")}. Set NODE_EXTRA_CA_CERTS yourself or pass --allow-no-system-ca.`
|
|
5451
5448
|
);
|
|
5452
5449
|
}
|
|
5453
5450
|
function buildTrustBundle(opts) {
|
|
5454
|
-
const dir = mkdtempSync3(
|
|
5455
|
-
const path2 =
|
|
5456
|
-
const sys =
|
|
5457
|
-
const local =
|
|
5458
|
-
|
|
5451
|
+
const dir = mkdtempSync3(join13(tmpdir3(), "openape-trust-"));
|
|
5452
|
+
const path2 = join13(dir, "bundle.pem");
|
|
5453
|
+
const sys = readFileSync14(opts.systemCaPath, "utf-8");
|
|
5454
|
+
const local = readFileSync14(opts.localCaPath, "utf-8");
|
|
5455
|
+
writeFileSync10(path2, `${sys.trimEnd()}
|
|
5459
5456
|
${local.trimEnd()}
|
|
5460
5457
|
`, { mode: 384 });
|
|
5461
5458
|
return {
|
|
@@ -5484,10 +5481,10 @@ function resolveProxyConfigOptions() {
|
|
|
5484
5481
|
77
|
|
5485
5482
|
);
|
|
5486
5483
|
}
|
|
5487
|
-
|
|
5484
|
+
consola42.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5488
5485
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5489
5486
|
}
|
|
5490
|
-
var proxyCommand =
|
|
5487
|
+
var proxyCommand = defineCommand48({
|
|
5491
5488
|
meta: {
|
|
5492
5489
|
name: "proxy",
|
|
5493
5490
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -5511,9 +5508,9 @@ var proxyCommand = defineCommand47({
|
|
|
5511
5508
|
let close = null;
|
|
5512
5509
|
if (reuseHostPort) {
|
|
5513
5510
|
proxyUrl = `http://${reuseHostPort}`;
|
|
5514
|
-
|
|
5515
|
-
const localCaPath =
|
|
5516
|
-
if (!
|
|
5511
|
+
consola42.info(`[apes proxy] using long-running daemon at ${proxyUrl}`);
|
|
5512
|
+
const localCaPath = join14(homedir12(), ".openape", "proxy", "ca.crt");
|
|
5513
|
+
if (!existsSync17(localCaPath)) {
|
|
5517
5514
|
throw new CliError(
|
|
5518
5515
|
`OPENAPE_PROXY is set but no local CA found at ${localCaPath}. Start the daemon (sudo -u <agent> apes proxy --global < secrets.toml) first.`
|
|
5519
5516
|
);
|
|
@@ -5522,15 +5519,15 @@ var proxyCommand = defineCommand47({
|
|
|
5522
5519
|
systemCaPath: detectSystemCaPath(),
|
|
5523
5520
|
localCaPath
|
|
5524
5521
|
});
|
|
5525
|
-
|
|
5522
|
+
consola42.debug(`[apes proxy] trust bundle: ${bundle.path}`);
|
|
5526
5523
|
} else if (reuseUrl) {
|
|
5527
5524
|
proxyUrl = reuseUrl;
|
|
5528
|
-
|
|
5525
|
+
consola42.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5529
5526
|
} else {
|
|
5530
5527
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5531
5528
|
proxyUrl = ephemeral.url;
|
|
5532
5529
|
close = ephemeral.close;
|
|
5533
|
-
|
|
5530
|
+
consola42.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5534
5531
|
}
|
|
5535
5532
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5536
5533
|
const childEnv = {
|
|
@@ -5571,7 +5568,7 @@ var proxyCommand = defineCommand47({
|
|
|
5571
5568
|
else resolveExit(code ?? 0);
|
|
5572
5569
|
});
|
|
5573
5570
|
child.once("error", (err) => {
|
|
5574
|
-
|
|
5571
|
+
consola42.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5575
5572
|
resolveExit(127);
|
|
5576
5573
|
});
|
|
5577
5574
|
});
|
|
@@ -5588,8 +5585,8 @@ function signalNumber(signal) {
|
|
|
5588
5585
|
}
|
|
5589
5586
|
|
|
5590
5587
|
// src/commands/explain.ts
|
|
5591
|
-
import { defineCommand as
|
|
5592
|
-
var explainCommand =
|
|
5588
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
5589
|
+
var explainCommand = defineCommand49({
|
|
5593
5590
|
meta: {
|
|
5594
5591
|
name: "explain",
|
|
5595
5592
|
description: "Show what permission a command would need"
|
|
@@ -5627,9 +5624,9 @@ var explainCommand = defineCommand48({
|
|
|
5627
5624
|
});
|
|
5628
5625
|
|
|
5629
5626
|
// src/commands/config/get.ts
|
|
5630
|
-
import { defineCommand as
|
|
5631
|
-
import
|
|
5632
|
-
var configGetCommand =
|
|
5627
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
5628
|
+
import consola43 from "consola";
|
|
5629
|
+
var configGetCommand = defineCommand50({
|
|
5633
5630
|
meta: {
|
|
5634
5631
|
name: "get",
|
|
5635
5632
|
description: "Get a configuration value"
|
|
@@ -5649,7 +5646,7 @@ var configGetCommand = defineCommand49({
|
|
|
5649
5646
|
if (idp)
|
|
5650
5647
|
console.log(idp);
|
|
5651
5648
|
else
|
|
5652
|
-
|
|
5649
|
+
consola43.info("No IdP configured.");
|
|
5653
5650
|
break;
|
|
5654
5651
|
}
|
|
5655
5652
|
case "email": {
|
|
@@ -5657,7 +5654,7 @@ var configGetCommand = defineCommand49({
|
|
|
5657
5654
|
if (auth?.email)
|
|
5658
5655
|
console.log(auth.email);
|
|
5659
5656
|
else
|
|
5660
|
-
|
|
5657
|
+
consola43.info("Not logged in.");
|
|
5661
5658
|
break;
|
|
5662
5659
|
}
|
|
5663
5660
|
default: {
|
|
@@ -5670,7 +5667,7 @@ var configGetCommand = defineCommand49({
|
|
|
5670
5667
|
if (sectionObj && field in sectionObj) {
|
|
5671
5668
|
console.log(sectionObj[field]);
|
|
5672
5669
|
} else {
|
|
5673
|
-
|
|
5670
|
+
consola43.info(`Key "${key}" not set.`);
|
|
5674
5671
|
}
|
|
5675
5672
|
} else {
|
|
5676
5673
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5681,9 +5678,9 @@ var configGetCommand = defineCommand49({
|
|
|
5681
5678
|
});
|
|
5682
5679
|
|
|
5683
5680
|
// src/commands/config/set.ts
|
|
5684
|
-
import { defineCommand as
|
|
5685
|
-
import
|
|
5686
|
-
var configSetCommand =
|
|
5681
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
5682
|
+
import consola44 from "consola";
|
|
5683
|
+
var configSetCommand = defineCommand51({
|
|
5687
5684
|
meta: {
|
|
5688
5685
|
name: "set",
|
|
5689
5686
|
description: "Set a configuration value"
|
|
@@ -5719,12 +5716,12 @@ var configSetCommand = defineCommand50({
|
|
|
5719
5716
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5720
5717
|
}
|
|
5721
5718
|
saveConfig(config);
|
|
5722
|
-
|
|
5719
|
+
consola44.success(`Set ${key} = ${value}`);
|
|
5723
5720
|
}
|
|
5724
5721
|
});
|
|
5725
5722
|
|
|
5726
5723
|
// src/commands/fetch/index.ts
|
|
5727
|
-
import { defineCommand as
|
|
5724
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
5728
5725
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5729
5726
|
const token = getAuthToken();
|
|
5730
5727
|
if (!token) {
|
|
@@ -5760,13 +5757,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5760
5757
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5761
5758
|
}
|
|
5762
5759
|
}
|
|
5763
|
-
var fetchCommand =
|
|
5760
|
+
var fetchCommand = defineCommand52({
|
|
5764
5761
|
meta: {
|
|
5765
5762
|
name: "fetch",
|
|
5766
5763
|
description: "Make authenticated HTTP requests"
|
|
5767
5764
|
},
|
|
5768
5765
|
subCommands: {
|
|
5769
|
-
get:
|
|
5766
|
+
get: defineCommand52({
|
|
5770
5767
|
meta: {
|
|
5771
5768
|
name: "get",
|
|
5772
5769
|
description: "GET request with auth token"
|
|
@@ -5792,7 +5789,7 @@ var fetchCommand = defineCommand51({
|
|
|
5792
5789
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5793
5790
|
}
|
|
5794
5791
|
}),
|
|
5795
|
-
post:
|
|
5792
|
+
post: defineCommand52({
|
|
5796
5793
|
meta: {
|
|
5797
5794
|
name: "post",
|
|
5798
5795
|
description: "POST request with auth token"
|
|
@@ -5831,8 +5828,8 @@ var fetchCommand = defineCommand51({
|
|
|
5831
5828
|
});
|
|
5832
5829
|
|
|
5833
5830
|
// src/commands/mcp/index.ts
|
|
5834
|
-
import { defineCommand as
|
|
5835
|
-
var mcpCommand =
|
|
5831
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
5832
|
+
var mcpCommand = defineCommand53({
|
|
5836
5833
|
meta: {
|
|
5837
5834
|
name: "mcp",
|
|
5838
5835
|
description: "Start MCP server for AI agents"
|
|
@@ -5855,25 +5852,25 @@ var mcpCommand = defineCommand52({
|
|
|
5855
5852
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5856
5853
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5857
5854
|
}
|
|
5858
|
-
const { startMcpServer } = await import("./server-
|
|
5855
|
+
const { startMcpServer } = await import("./server-OSX5LE4W.js");
|
|
5859
5856
|
await startMcpServer(transport, port);
|
|
5860
5857
|
}
|
|
5861
5858
|
});
|
|
5862
5859
|
|
|
5863
5860
|
// src/commands/init/index.ts
|
|
5864
|
-
import { existsSync as
|
|
5861
|
+
import { existsSync as existsSync18, copyFileSync, writeFileSync as writeFileSync11 } from "fs";
|
|
5865
5862
|
import { randomBytes } from "crypto";
|
|
5866
5863
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
5867
|
-
import { join as
|
|
5868
|
-
import { defineCommand as
|
|
5869
|
-
import
|
|
5864
|
+
import { join as join15 } from "path";
|
|
5865
|
+
import { defineCommand as defineCommand54 } from "citty";
|
|
5866
|
+
import consola45 from "consola";
|
|
5870
5867
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5871
5868
|
async function downloadTemplate(repo, targetDir) {
|
|
5872
5869
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
5873
5870
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
5874
5871
|
}
|
|
5875
5872
|
function installDeps(dir) {
|
|
5876
|
-
const hasLockFile = (name) =>
|
|
5873
|
+
const hasLockFile = (name) => existsSync18(join15(dir, name));
|
|
5877
5874
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
5878
5875
|
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5879
5876
|
} else if (hasLockFile("bun.lockb")) {
|
|
@@ -5883,20 +5880,20 @@ function installDeps(dir) {
|
|
|
5883
5880
|
}
|
|
5884
5881
|
}
|
|
5885
5882
|
async function promptChoice(message, choices) {
|
|
5886
|
-
const result = await
|
|
5883
|
+
const result = await consola45.prompt(message, { type: "select", options: choices });
|
|
5887
5884
|
if (typeof result === "symbol") {
|
|
5888
5885
|
throw new CliExit(0);
|
|
5889
5886
|
}
|
|
5890
5887
|
return result;
|
|
5891
5888
|
}
|
|
5892
5889
|
async function promptText(message, defaultValue) {
|
|
5893
|
-
const result = await
|
|
5890
|
+
const result = await consola45.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5894
5891
|
if (typeof result === "symbol") {
|
|
5895
5892
|
throw new CliExit(0);
|
|
5896
5893
|
}
|
|
5897
5894
|
return result || defaultValue || "";
|
|
5898
5895
|
}
|
|
5899
|
-
var initCommand =
|
|
5896
|
+
var initCommand = defineCommand54({
|
|
5900
5897
|
meta: {
|
|
5901
5898
|
name: "init",
|
|
5902
5899
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5938,23 +5935,23 @@ var initCommand = defineCommand53({
|
|
|
5938
5935
|
});
|
|
5939
5936
|
async function initSP(targetDir) {
|
|
5940
5937
|
const dir = targetDir || "my-app";
|
|
5941
|
-
if (
|
|
5938
|
+
if (existsSync18(join15(dir, "package.json"))) {
|
|
5942
5939
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5943
5940
|
}
|
|
5944
|
-
|
|
5941
|
+
consola45.start("Scaffolding SP starter...");
|
|
5945
5942
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5946
|
-
|
|
5947
|
-
|
|
5943
|
+
consola45.success("Scaffolded from openape-sp-starter");
|
|
5944
|
+
consola45.start("Installing dependencies...");
|
|
5948
5945
|
installDeps(dir);
|
|
5949
|
-
|
|
5950
|
-
const envExample =
|
|
5951
|
-
const envFile =
|
|
5952
|
-
if (
|
|
5946
|
+
consola45.success("Dependencies installed");
|
|
5947
|
+
const envExample = join15(dir, ".env.example");
|
|
5948
|
+
const envFile = join15(dir, ".env");
|
|
5949
|
+
if (existsSync18(envExample) && !existsSync18(envFile)) {
|
|
5953
5950
|
copyFileSync(envExample, envFile);
|
|
5954
|
-
|
|
5951
|
+
consola45.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5955
5952
|
}
|
|
5956
5953
|
console.log("");
|
|
5957
|
-
|
|
5954
|
+
consola45.box([
|
|
5958
5955
|
`cd ${dir}`,
|
|
5959
5956
|
"npm run dev",
|
|
5960
5957
|
"",
|
|
@@ -5963,7 +5960,7 @@ async function initSP(targetDir) {
|
|
|
5963
5960
|
}
|
|
5964
5961
|
async function initIdP(targetDir) {
|
|
5965
5962
|
const dir = targetDir || "my-idp";
|
|
5966
|
-
if (
|
|
5963
|
+
if (existsSync18(join15(dir, "package.json"))) {
|
|
5967
5964
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5968
5965
|
}
|
|
5969
5966
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -5973,15 +5970,15 @@ async function initIdP(targetDir) {
|
|
|
5973
5970
|
"s3 (S3-compatible)"
|
|
5974
5971
|
]);
|
|
5975
5972
|
const adminEmail = await promptText("Admin email");
|
|
5976
|
-
|
|
5973
|
+
consola45.start("Scaffolding IdP starter...");
|
|
5977
5974
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5978
|
-
|
|
5979
|
-
|
|
5975
|
+
consola45.success("Scaffolded from openape-idp-starter");
|
|
5976
|
+
consola45.start("Installing dependencies...");
|
|
5980
5977
|
installDeps(dir);
|
|
5981
|
-
|
|
5978
|
+
consola45.success("Dependencies installed");
|
|
5982
5979
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5983
5980
|
const managementToken = randomBytes(32).toString("hex");
|
|
5984
|
-
|
|
5981
|
+
consola45.success("Secrets generated");
|
|
5985
5982
|
const isLocalhost = domain === "localhost";
|
|
5986
5983
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5987
5984
|
const envContent = [
|
|
@@ -5995,11 +5992,11 @@ async function initIdP(targetDir) {
|
|
|
5995
5992
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
5996
5993
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
5997
5994
|
].join("\n");
|
|
5998
|
-
|
|
5995
|
+
writeFileSync11(join15(dir, ".env"), `${envContent}
|
|
5999
5996
|
`, { mode: 384 });
|
|
6000
|
-
|
|
5997
|
+
consola45.success(".env created");
|
|
6001
5998
|
console.log("");
|
|
6002
|
-
|
|
5999
|
+
consola45.box([
|
|
6003
6000
|
`cd ${dir}`,
|
|
6004
6001
|
"npm run dev",
|
|
6005
6002
|
"",
|
|
@@ -6016,11 +6013,11 @@ async function initIdP(targetDir) {
|
|
|
6016
6013
|
|
|
6017
6014
|
// src/commands/enroll.ts
|
|
6018
6015
|
import { Buffer as Buffer4 } from "buffer";
|
|
6019
|
-
import { existsSync as
|
|
6016
|
+
import { existsSync as existsSync19, readFileSync as readFileSync15 } from "fs";
|
|
6020
6017
|
import { execFile as execFile2 } from "child_process";
|
|
6021
6018
|
import { sign as sign2 } from "crypto";
|
|
6022
|
-
import { defineCommand as
|
|
6023
|
-
import
|
|
6019
|
+
import { defineCommand as defineCommand55 } from "citty";
|
|
6020
|
+
import consola46 from "consola";
|
|
6024
6021
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
6025
6022
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
6026
6023
|
var POLL_INTERVAL = 3e3;
|
|
@@ -6032,7 +6029,7 @@ function openBrowser2(url) {
|
|
|
6032
6029
|
}
|
|
6033
6030
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
6034
6031
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6035
|
-
const keyContent =
|
|
6032
|
+
const keyContent = readFileSync15(resolvedKey, "utf-8");
|
|
6036
6033
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
6037
6034
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
6038
6035
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -6063,7 +6060,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
6063
6060
|
}
|
|
6064
6061
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
6065
6062
|
}
|
|
6066
|
-
var enrollCommand =
|
|
6063
|
+
var enrollCommand = defineCommand55({
|
|
6067
6064
|
meta: {
|
|
6068
6065
|
name: "enroll",
|
|
6069
6066
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -6083,38 +6080,38 @@ var enrollCommand = defineCommand54({
|
|
|
6083
6080
|
}
|
|
6084
6081
|
},
|
|
6085
6082
|
async run({ args }) {
|
|
6086
|
-
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) => {
|
|
6087
6084
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6088
6085
|
return r;
|
|
6089
6086
|
}) || DEFAULT_IDP_URL2;
|
|
6090
|
-
const agentName = args.name || await
|
|
6087
|
+
const agentName = args.name || await consola46.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
6091
6088
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6092
6089
|
return r;
|
|
6093
6090
|
});
|
|
6094
6091
|
if (!agentName) {
|
|
6095
6092
|
throw new CliError("Agent name is required.");
|
|
6096
6093
|
}
|
|
6097
|
-
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) => {
|
|
6098
6095
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6099
6096
|
return r;
|
|
6100
6097
|
}) || DEFAULT_KEY_PATH;
|
|
6101
6098
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6102
6099
|
let publicKey;
|
|
6103
|
-
if (
|
|
6100
|
+
if (existsSync19(resolvedKey)) {
|
|
6104
6101
|
publicKey = readPublicKey(resolvedKey);
|
|
6105
|
-
|
|
6102
|
+
consola46.success(`Using existing key ${keyPath}`);
|
|
6106
6103
|
} else {
|
|
6107
|
-
|
|
6104
|
+
consola46.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
6108
6105
|
publicKey = generateAndSaveKey(keyPath);
|
|
6109
|
-
|
|
6106
|
+
consola46.success(`Key pair generated at ${keyPath}`);
|
|
6110
6107
|
}
|
|
6111
6108
|
const encodedKey = encodeURIComponent(publicKey);
|
|
6112
6109
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
6113
|
-
|
|
6114
|
-
|
|
6110
|
+
consola46.info("Opening browser for enrollment...");
|
|
6111
|
+
consola46.info(`\u2192 ${idp}/enroll`);
|
|
6115
6112
|
openBrowser2(enrollUrl);
|
|
6116
6113
|
console.log("");
|
|
6117
|
-
const agentEmail = await
|
|
6114
|
+
const agentEmail = await consola46.prompt(
|
|
6118
6115
|
"Agent email (shown in browser after enrollment)",
|
|
6119
6116
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
6120
6117
|
).then((r) => {
|
|
@@ -6124,7 +6121,7 @@ var enrollCommand = defineCommand54({
|
|
|
6124
6121
|
if (!agentEmail) {
|
|
6125
6122
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
6126
6123
|
}
|
|
6127
|
-
|
|
6124
|
+
consola46.start("Verifying enrollment...");
|
|
6128
6125
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
6129
6126
|
saveAuth({
|
|
6130
6127
|
idp,
|
|
@@ -6136,18 +6133,18 @@ var enrollCommand = defineCommand54({
|
|
|
6136
6133
|
config.defaults = { ...config.defaults, idp };
|
|
6137
6134
|
config.agent = { key: keyPath, email: agentEmail };
|
|
6138
6135
|
saveConfig(config);
|
|
6139
|
-
|
|
6140
|
-
|
|
6136
|
+
consola46.success(`Agent enrolled as ${agentEmail}`);
|
|
6137
|
+
consola46.success("Config saved to ~/.config/apes/");
|
|
6141
6138
|
console.log("");
|
|
6142
|
-
|
|
6139
|
+
consola46.info("Verify with: apes whoami");
|
|
6143
6140
|
}
|
|
6144
6141
|
});
|
|
6145
6142
|
|
|
6146
6143
|
// src/commands/register-user.ts
|
|
6147
|
-
import { existsSync as
|
|
6148
|
-
import { defineCommand as
|
|
6149
|
-
import
|
|
6150
|
-
var registerUserCommand =
|
|
6144
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16 } from "fs";
|
|
6145
|
+
import { defineCommand as defineCommand56 } from "citty";
|
|
6146
|
+
import consola47 from "consola";
|
|
6147
|
+
var registerUserCommand = defineCommand56({
|
|
6151
6148
|
meta: {
|
|
6152
6149
|
name: "register-user",
|
|
6153
6150
|
description: "Register a sub-user with SSH key"
|
|
@@ -6183,8 +6180,8 @@ var registerUserCommand = defineCommand55({
|
|
|
6183
6180
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
6184
6181
|
}
|
|
6185
6182
|
let publicKey = args.key;
|
|
6186
|
-
if (
|
|
6187
|
-
publicKey =
|
|
6183
|
+
if (existsSync20(args.key)) {
|
|
6184
|
+
publicKey = readFileSync16(args.key, "utf-8").trim();
|
|
6188
6185
|
}
|
|
6189
6186
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
6190
6187
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -6202,18 +6199,18 @@ var registerUserCommand = defineCommand55({
|
|
|
6202
6199
|
...userType ? { type: userType } : {}
|
|
6203
6200
|
}
|
|
6204
6201
|
});
|
|
6205
|
-
|
|
6202
|
+
consola47.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
6206
6203
|
}
|
|
6207
6204
|
});
|
|
6208
6205
|
|
|
6209
6206
|
// src/commands/utils/index.ts
|
|
6210
|
-
import { defineCommand as
|
|
6207
|
+
import { defineCommand as defineCommand58 } from "citty";
|
|
6211
6208
|
|
|
6212
6209
|
// src/commands/utils/dig.ts
|
|
6213
|
-
import { defineCommand as
|
|
6214
|
-
import
|
|
6210
|
+
import { defineCommand as defineCommand57 } from "citty";
|
|
6211
|
+
import consola48 from "consola";
|
|
6215
6212
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
6216
|
-
var digCommand =
|
|
6213
|
+
var digCommand = defineCommand57({
|
|
6217
6214
|
meta: {
|
|
6218
6215
|
name: "dig",
|
|
6219
6216
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -6286,12 +6283,12 @@ var digCommand = defineCommand56({
|
|
|
6286
6283
|
console.log(` domain: ${domain}`);
|
|
6287
6284
|
console.log("");
|
|
6288
6285
|
if (!result.ddisa.found) {
|
|
6289
|
-
|
|
6286
|
+
consola48.warn(`No DDISA record at _ddisa.${domain}`);
|
|
6290
6287
|
if (result.hint) console.log(`
|
|
6291
6288
|
${result.hint}`);
|
|
6292
6289
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6293
6290
|
}
|
|
6294
|
-
|
|
6291
|
+
consola48.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
6295
6292
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
6296
6293
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
6297
6294
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -6301,13 +6298,13 @@ ${result.hint}`);
|
|
|
6301
6298
|
return;
|
|
6302
6299
|
}
|
|
6303
6300
|
if (result.idpDiscovery.ok) {
|
|
6304
|
-
|
|
6301
|
+
consola48.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
6305
6302
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
6306
6303
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
6307
6304
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
6308
6305
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
6309
6306
|
} else {
|
|
6310
|
-
|
|
6307
|
+
consola48.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
6311
6308
|
if (result.hint) console.log(`
|
|
6312
6309
|
${result.hint}`);
|
|
6313
6310
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -6316,7 +6313,7 @@ ${result.hint}`);
|
|
|
6316
6313
|
});
|
|
6317
6314
|
|
|
6318
6315
|
// src/commands/utils/index.ts
|
|
6319
|
-
var utilsCommand =
|
|
6316
|
+
var utilsCommand = defineCommand58({
|
|
6320
6317
|
meta: {
|
|
6321
6318
|
name: "utils",
|
|
6322
6319
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -6327,12 +6324,12 @@ var utilsCommand = defineCommand57({
|
|
|
6327
6324
|
});
|
|
6328
6325
|
|
|
6329
6326
|
// src/commands/sessions/index.ts
|
|
6330
|
-
import { defineCommand as
|
|
6327
|
+
import { defineCommand as defineCommand61 } from "citty";
|
|
6331
6328
|
|
|
6332
6329
|
// src/commands/sessions/list.ts
|
|
6333
|
-
import { defineCommand as
|
|
6334
|
-
import
|
|
6335
|
-
var sessionsListCommand =
|
|
6330
|
+
import { defineCommand as defineCommand59 } from "citty";
|
|
6331
|
+
import consola49 from "consola";
|
|
6332
|
+
var sessionsListCommand = defineCommand59({
|
|
6336
6333
|
meta: {
|
|
6337
6334
|
name: "list",
|
|
6338
6335
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6350,7 +6347,7 @@ var sessionsListCommand = defineCommand58({
|
|
|
6350
6347
|
return;
|
|
6351
6348
|
}
|
|
6352
6349
|
if (result.data.length === 0) {
|
|
6353
|
-
|
|
6350
|
+
consola49.info("No active sessions.");
|
|
6354
6351
|
return;
|
|
6355
6352
|
}
|
|
6356
6353
|
for (const f of result.data) {
|
|
@@ -6362,9 +6359,9 @@ var sessionsListCommand = defineCommand58({
|
|
|
6362
6359
|
});
|
|
6363
6360
|
|
|
6364
6361
|
// src/commands/sessions/remove.ts
|
|
6365
|
-
import { defineCommand as
|
|
6366
|
-
import
|
|
6367
|
-
var sessionsRemoveCommand =
|
|
6362
|
+
import { defineCommand as defineCommand60 } from "citty";
|
|
6363
|
+
import consola50 from "consola";
|
|
6364
|
+
var sessionsRemoveCommand = defineCommand60({
|
|
6368
6365
|
meta: {
|
|
6369
6366
|
name: "remove",
|
|
6370
6367
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6380,12 +6377,12 @@ var sessionsRemoveCommand = defineCommand59({
|
|
|
6380
6377
|
const id = String(args.familyId).trim();
|
|
6381
6378
|
if (!id) throw new CliError("familyId required");
|
|
6382
6379
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6383
|
-
|
|
6380
|
+
consola50.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6384
6381
|
}
|
|
6385
6382
|
});
|
|
6386
6383
|
|
|
6387
6384
|
// src/commands/sessions/index.ts
|
|
6388
|
-
var sessionsCommand =
|
|
6385
|
+
var sessionsCommand = defineCommand61({
|
|
6389
6386
|
meta: {
|
|
6390
6387
|
name: "sessions",
|
|
6391
6388
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6397,10 +6394,10 @@ var sessionsCommand = defineCommand60({
|
|
|
6397
6394
|
});
|
|
6398
6395
|
|
|
6399
6396
|
// src/commands/dns-check.ts
|
|
6400
|
-
import { defineCommand as
|
|
6401
|
-
import
|
|
6397
|
+
import { defineCommand as defineCommand62 } from "citty";
|
|
6398
|
+
import consola51 from "consola";
|
|
6402
6399
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6403
|
-
var dnsCheckCommand =
|
|
6400
|
+
var dnsCheckCommand = defineCommand62({
|
|
6404
6401
|
meta: {
|
|
6405
6402
|
name: "dns-check",
|
|
6406
6403
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6414,7 +6411,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6414
6411
|
},
|
|
6415
6412
|
async run({ args }) {
|
|
6416
6413
|
const domain = args.domain;
|
|
6417
|
-
|
|
6414
|
+
consola51.start(`Checking _ddisa.${domain}...`);
|
|
6418
6415
|
try {
|
|
6419
6416
|
const result = await resolveDDISA3(domain);
|
|
6420
6417
|
if (!result) {
|
|
@@ -6423,7 +6420,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6423
6420
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
6424
6421
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6425
6422
|
}
|
|
6426
|
-
|
|
6423
|
+
consola51.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
6427
6424
|
console.log("");
|
|
6428
6425
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
6429
6426
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -6432,14 +6429,14 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6432
6429
|
if (result.priority !== void 0)
|
|
6433
6430
|
console.log(` Priority: ${result.priority}`);
|
|
6434
6431
|
console.log("");
|
|
6435
|
-
|
|
6432
|
+
consola51.start(`Verifying IdP at ${result.idp}...`);
|
|
6436
6433
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
6437
6434
|
if (!discoResp.ok) {
|
|
6438
|
-
|
|
6435
|
+
consola51.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
6439
6436
|
return;
|
|
6440
6437
|
}
|
|
6441
6438
|
const disco = await discoResp.json();
|
|
6442
|
-
|
|
6439
|
+
consola51.success(`IdP is reachable`);
|
|
6443
6440
|
console.log(` Issuer: ${disco.issuer}`);
|
|
6444
6441
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
6445
6442
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -6457,7 +6454,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6457
6454
|
// src/commands/health.ts
|
|
6458
6455
|
import { exec } from "child_process";
|
|
6459
6456
|
import { promisify } from "util";
|
|
6460
|
-
import { defineCommand as
|
|
6457
|
+
import { defineCommand as defineCommand63 } from "citty";
|
|
6461
6458
|
var execAsync = promisify(exec);
|
|
6462
6459
|
async function resolveApeShellPath() {
|
|
6463
6460
|
try {
|
|
@@ -6493,7 +6490,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6493
6490
|
}
|
|
6494
6491
|
}
|
|
6495
6492
|
async function runHealth(args) {
|
|
6496
|
-
const version = true ? "1.31.
|
|
6493
|
+
const version = true ? "1.31.2" : "0.0.0";
|
|
6497
6494
|
const auth = loadAuth();
|
|
6498
6495
|
if (!auth) {
|
|
6499
6496
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6556,7 +6553,7 @@ async function runHealth(args) {
|
|
|
6556
6553
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
6557
6554
|
}
|
|
6558
6555
|
}
|
|
6559
|
-
var healthCommand =
|
|
6556
|
+
var healthCommand = defineCommand63({
|
|
6560
6557
|
meta: {
|
|
6561
6558
|
name: "health",
|
|
6562
6559
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -6574,8 +6571,8 @@ var healthCommand = defineCommand62({
|
|
|
6574
6571
|
});
|
|
6575
6572
|
|
|
6576
6573
|
// src/commands/workflows.ts
|
|
6577
|
-
import { defineCommand as
|
|
6578
|
-
import
|
|
6574
|
+
import { defineCommand as defineCommand64 } from "citty";
|
|
6575
|
+
import consola52 from "consola";
|
|
6579
6576
|
|
|
6580
6577
|
// src/guides/index.ts
|
|
6581
6578
|
var guides = [
|
|
@@ -6625,7 +6622,7 @@ var guides = [
|
|
|
6625
6622
|
];
|
|
6626
6623
|
|
|
6627
6624
|
// src/commands/workflows.ts
|
|
6628
|
-
var workflowsCommand =
|
|
6625
|
+
var workflowsCommand = defineCommand64({
|
|
6629
6626
|
meta: {
|
|
6630
6627
|
name: "workflows",
|
|
6631
6628
|
description: "Discover workflow guides"
|
|
@@ -6646,7 +6643,7 @@ var workflowsCommand = defineCommand63({
|
|
|
6646
6643
|
if (args.id) {
|
|
6647
6644
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6648
6645
|
if (!guide) {
|
|
6649
|
-
|
|
6646
|
+
consola52.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6650
6647
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6651
6648
|
}
|
|
6652
6649
|
if (args.json) {
|
|
@@ -6686,26 +6683,26 @@ var workflowsCommand = defineCommand63({
|
|
|
6686
6683
|
});
|
|
6687
6684
|
|
|
6688
6685
|
// src/version-check.ts
|
|
6689
|
-
import { existsSync as
|
|
6690
|
-
import { homedir as
|
|
6691
|
-
import { join as
|
|
6692
|
-
import
|
|
6686
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync12 } from "fs";
|
|
6687
|
+
import { homedir as homedir13 } from "os";
|
|
6688
|
+
import { join as join16 } from "path";
|
|
6689
|
+
import consola53 from "consola";
|
|
6693
6690
|
var PACKAGE_NAME = "@openape/apes";
|
|
6694
6691
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6695
|
-
var CACHE_FILE =
|
|
6692
|
+
var CACHE_FILE = join16(homedir13(), ".config", "apes", ".version-check.json");
|
|
6696
6693
|
function readCache() {
|
|
6697
|
-
if (!
|
|
6694
|
+
if (!existsSync21(CACHE_FILE)) return null;
|
|
6698
6695
|
try {
|
|
6699
|
-
return JSON.parse(
|
|
6696
|
+
return JSON.parse(readFileSync17(CACHE_FILE, "utf-8"));
|
|
6700
6697
|
} catch {
|
|
6701
6698
|
return null;
|
|
6702
6699
|
}
|
|
6703
6700
|
}
|
|
6704
6701
|
function writeCache(entry) {
|
|
6705
6702
|
try {
|
|
6706
|
-
const dir =
|
|
6707
|
-
if (!
|
|
6708
|
-
|
|
6703
|
+
const dir = join16(homedir13(), ".config", "apes");
|
|
6704
|
+
if (!existsSync21(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
6705
|
+
writeFileSync12(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
6709
6706
|
} catch {
|
|
6710
6707
|
}
|
|
6711
6708
|
}
|
|
@@ -6734,7 +6731,7 @@ async function fetchLatestVersion() {
|
|
|
6734
6731
|
}
|
|
6735
6732
|
function warnIfBehind(currentVersion, latest) {
|
|
6736
6733
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6737
|
-
|
|
6734
|
+
consola53.warn(
|
|
6738
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.)`
|
|
6739
6736
|
);
|
|
6740
6737
|
}
|
|
@@ -6766,10 +6763,10 @@ if (shellRewrite) {
|
|
|
6766
6763
|
if (shellRewrite.action === "rewrite") {
|
|
6767
6764
|
process.argv = shellRewrite.argv;
|
|
6768
6765
|
} else if (shellRewrite.action === "version") {
|
|
6769
|
-
console.log(`ape-shell ${"1.31.
|
|
6766
|
+
console.log(`ape-shell ${"1.31.2"} (OpenApe DDISA shell wrapper)`);
|
|
6770
6767
|
process.exit(0);
|
|
6771
6768
|
} else if (shellRewrite.action === "help") {
|
|
6772
|
-
console.log(`ape-shell ${"1.31.
|
|
6769
|
+
console.log(`ape-shell ${"1.31.2"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6773
6770
|
console.log("");
|
|
6774
6771
|
console.log("Usage:");
|
|
6775
6772
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6793,7 +6790,7 @@ if (shellRewrite) {
|
|
|
6793
6790
|
}
|
|
6794
6791
|
}
|
|
6795
6792
|
var debug = process.argv.includes("--debug");
|
|
6796
|
-
var grantsCommand =
|
|
6793
|
+
var grantsCommand = defineCommand65({
|
|
6797
6794
|
meta: {
|
|
6798
6795
|
name: "grants",
|
|
6799
6796
|
description: "Grant management"
|
|
@@ -6811,10 +6808,11 @@ var grantsCommand = defineCommand64({
|
|
|
6811
6808
|
token: tokenCommand,
|
|
6812
6809
|
delegate: delegateCommand,
|
|
6813
6810
|
delegations: delegationsCommand,
|
|
6814
|
-
"delegation-revoke": delegationRevokeCommand
|
|
6811
|
+
"delegation-revoke": delegationRevokeCommand,
|
|
6812
|
+
llm: llmCommand
|
|
6815
6813
|
}
|
|
6816
6814
|
});
|
|
6817
|
-
var configCommand =
|
|
6815
|
+
var configCommand = defineCommand65({
|
|
6818
6816
|
meta: {
|
|
6819
6817
|
name: "config",
|
|
6820
6818
|
description: "Configuration management"
|
|
@@ -6824,10 +6822,10 @@ var configCommand = defineCommand64({
|
|
|
6824
6822
|
set: configSetCommand
|
|
6825
6823
|
}
|
|
6826
6824
|
});
|
|
6827
|
-
var main =
|
|
6825
|
+
var main = defineCommand65({
|
|
6828
6826
|
meta: {
|
|
6829
6827
|
name: "apes",
|
|
6830
|
-
version: "1.31.
|
|
6828
|
+
version: "1.31.2",
|
|
6831
6829
|
description: "Unified CLI for OpenApe"
|
|
6832
6830
|
},
|
|
6833
6831
|
subCommands: {
|
|
@@ -6885,20 +6883,20 @@ async function maybeRefreshAuth() {
|
|
|
6885
6883
|
}
|
|
6886
6884
|
}
|
|
6887
6885
|
await maybeRefreshAuth();
|
|
6888
|
-
await maybeWarnStaleVersion("1.31.
|
|
6886
|
+
await maybeWarnStaleVersion("1.31.2").catch(() => {
|
|
6889
6887
|
});
|
|
6890
6888
|
runMain(main).catch((err) => {
|
|
6891
6889
|
if (err instanceof CliExit) {
|
|
6892
6890
|
process.exit(err.exitCode);
|
|
6893
6891
|
}
|
|
6894
6892
|
if (err instanceof CliError) {
|
|
6895
|
-
|
|
6893
|
+
consola54.error(err.message);
|
|
6896
6894
|
process.exit(err.exitCode);
|
|
6897
6895
|
}
|
|
6898
6896
|
if (debug) {
|
|
6899
|
-
|
|
6897
|
+
consola54.error(err);
|
|
6900
6898
|
} else {
|
|
6901
|
-
|
|
6899
|
+
consola54.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6902
6900
|
}
|
|
6903
6901
|
process.exit(1);
|
|
6904
6902
|
});
|