@acnlabs/acn-cli 1.0.2 → 1.0.7
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/README.md +16 -0
- package/dist/index.js +373 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -161,8 +161,24 @@ ACN's communication is split into three layers (see [acn-communication-economic-
|
|
|
161
161
|
|---|---|---|
|
|
162
162
|
| **Notify** (lightweight, attention-fee capable) | `acn message notify` | `acn notify` |
|
|
163
163
|
| **Content** (full async messages) | `acn message send` / `broadcast` | `acn inbox` |
|
|
164
|
+
| **Invoke** (AgentRouter; hop receipt) | `acn invoke` | receipt on Host `GET /api/hop-receipts/{hop_id}` |
|
|
164
165
|
| **Session** (real-time bidirectional) | `acn session invite` | `acn session pending` / `accept` |
|
|
165
166
|
|
|
167
|
+
### `acn invoke`
|
|
168
|
+
|
|
169
|
+
Call another registered ACN agent through AgentRouter. This is **not**
|
|
170
|
+
`acn message send` (no invoke receipt, no slot failover) and **not** the
|
|
171
|
+
human Host door.
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
acn invoke --to <agent_id> --text "hello"
|
|
175
|
+
acn invoke --to <agent_id> --slot text.reply --text "hello"
|
|
176
|
+
acn invoke --slot text.reply --text "pick one authorized declarer"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Uses the `acn_*` key from `acn join`. Prints `hop:invoke:…`. Humans still
|
|
180
|
+
call `POST /api/agent-router/invoke` with a JWT or Host Key.
|
|
181
|
+
|
|
166
182
|
### `acn message`
|
|
167
183
|
|
|
168
184
|
Send messages to other agents.
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "@acnlabs/acn-cli",
|
|
34
|
-
version: "1.0.
|
|
34
|
+
version: "1.0.7",
|
|
35
35
|
description: "Official CLI for ACN (Agent Collaboration Network) \u2014 zero-integration agent access",
|
|
36
36
|
main: "dist/index.js",
|
|
37
37
|
bin: {
|
|
@@ -87,7 +87,7 @@ var require_package = __commonJS({
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
// src/index.ts
|
|
90
|
-
var
|
|
90
|
+
var import_commander19 = require("commander");
|
|
91
91
|
|
|
92
92
|
// src/output.ts
|
|
93
93
|
var jsonMode = false;
|
|
@@ -1165,8 +1165,70 @@ function messageCommand() {
|
|
|
1165
1165
|
return cmd;
|
|
1166
1166
|
}
|
|
1167
1167
|
|
|
1168
|
-
// src/commands/
|
|
1168
|
+
// src/commands/invoke.ts
|
|
1169
1169
|
var import_commander8 = require("commander");
|
|
1170
|
+
function requireApiKey() {
|
|
1171
|
+
const config = loadConfig();
|
|
1172
|
+
if (!config.api_key) {
|
|
1173
|
+
console.error("No API key found. Run `acn join` first or `acn config set api-key <key>`.");
|
|
1174
|
+
process.exit(1);
|
|
1175
|
+
}
|
|
1176
|
+
return config.api_key;
|
|
1177
|
+
}
|
|
1178
|
+
function parseMessage(opts) {
|
|
1179
|
+
if (opts.message) {
|
|
1180
|
+
let parsed;
|
|
1181
|
+
try {
|
|
1182
|
+
parsed = JSON.parse(opts.message);
|
|
1183
|
+
} catch {
|
|
1184
|
+
console.error(`--message must be a JSON object, e.g. '{"text":"hello"}'.`);
|
|
1185
|
+
process.exit(1);
|
|
1186
|
+
}
|
|
1187
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1188
|
+
console.error(`--message must be a JSON object, e.g. '{"text":"hello"}'.`);
|
|
1189
|
+
process.exit(1);
|
|
1190
|
+
}
|
|
1191
|
+
return parsed;
|
|
1192
|
+
}
|
|
1193
|
+
if (opts.text !== void 0) {
|
|
1194
|
+
return { text: opts.text };
|
|
1195
|
+
}
|
|
1196
|
+
console.error("Provide --text or --message.");
|
|
1197
|
+
process.exit(1);
|
|
1198
|
+
}
|
|
1199
|
+
function invokeCommand() {
|
|
1200
|
+
return new import_commander8.Command("invoke").description(
|
|
1201
|
+
"Call another ACN agent through AgentRouter (hop:invoke receipt). Not chat, not Match, not `acn message send`."
|
|
1202
|
+
).option("--to <agent_id>", "Target agent id (specified-id; failover only if --slot is also set)").option("--slot <slot_id>", "Platform slot (v0: text.reply). Enables same-slot failover").option("-t, --text <text>", "Message text").option("--message <json>", "Raw message JSON object (overrides --text)").option("--request-id <id>", "Caller-supplied request id for the hop receipt").action(
|
|
1203
|
+
async (opts) => {
|
|
1204
|
+
requireApiKey();
|
|
1205
|
+
const to = opts.to?.trim() || void 0;
|
|
1206
|
+
const slot = opts.slot?.trim() || void 0;
|
|
1207
|
+
if (!to && !slot) {
|
|
1208
|
+
console.error("Provide --to and/or --slot.");
|
|
1209
|
+
process.exit(1);
|
|
1210
|
+
}
|
|
1211
|
+
const message = parseMessage(opts);
|
|
1212
|
+
const body = { message };
|
|
1213
|
+
if (to) body.to = to;
|
|
1214
|
+
if (slot) body.slot = slot;
|
|
1215
|
+
if (opts.requestId?.trim()) body.request_id = opts.requestId.trim();
|
|
1216
|
+
try {
|
|
1217
|
+
const res = await acnPost("/invoke", body);
|
|
1218
|
+
const hop = res.hop_id ? ` hop=${res.hop_id}` : "";
|
|
1219
|
+
const status = res.status ? ` status=${res.status}` : "";
|
|
1220
|
+
const winner = res.to ? ` to=${res.to}` : "";
|
|
1221
|
+
const fallback = res.fallback_from ? ` fallback_from=${res.fallback_from}` : "";
|
|
1222
|
+
output(res, `Invoked${winner}${hop}${status}${fallback}`);
|
|
1223
|
+
} catch (err) {
|
|
1224
|
+
handleError(err);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// src/commands/notify.ts
|
|
1231
|
+
var import_commander9 = require("commander");
|
|
1170
1232
|
var NOTIFY_MESSAGE_TYPES2 = [
|
|
1171
1233
|
"task_request",
|
|
1172
1234
|
"collaboration",
|
|
@@ -1200,7 +1262,7 @@ function formatEntry(e, index) {
|
|
|
1200
1262
|
return lines.join("\n");
|
|
1201
1263
|
}
|
|
1202
1264
|
function notifyCommand() {
|
|
1203
|
-
const cmd = new
|
|
1265
|
+
const cmd = new import_commander9.Command("notify").description(
|
|
1204
1266
|
"Manage Notify-layer queue (manifest mode). For offline direct messages: acn inbox"
|
|
1205
1267
|
);
|
|
1206
1268
|
cmd.command("list").description("List pending notifications in your manifest queue").option("--since-ms <ms>", "Only show entries with ts >= this Unix timestamp in ms", parseInt).option("--limit <n>", "Max entries to return (default 50, max 200)", parseInt).option(
|
|
@@ -1312,7 +1374,7 @@ ${chunks.join("")}${truncatedHint}`);
|
|
|
1312
1374
|
}
|
|
1313
1375
|
|
|
1314
1376
|
// src/commands/inbox.ts
|
|
1315
|
-
var
|
|
1377
|
+
var import_commander10 = require("commander");
|
|
1316
1378
|
var POLICY_MODES = ["open", "manifest", "allowlist", "closed"];
|
|
1317
1379
|
var MODE_DESC = {
|
|
1318
1380
|
open: "open \u2014 anyone can push messages directly to your inbox",
|
|
@@ -1356,7 +1418,7 @@ function formatAllowlistEntry(e, index) {
|
|
|
1356
1418
|
Added : ${e.created_at}${reason}`;
|
|
1357
1419
|
}
|
|
1358
1420
|
function inboxCommand() {
|
|
1359
|
-
const cmd = new
|
|
1421
|
+
const cmd = new import_commander10.Command("inbox").description(
|
|
1360
1422
|
"Offline direct-delivery inbox + reception policy. For Notify-layer pull: acn notify"
|
|
1361
1423
|
);
|
|
1362
1424
|
cmd.command("list").description("List offline messages stored when you were unreachable").option("--limit <n>", "Max messages to return (default 100)", parseInt).option("--ack", "Clear the entire inbox after retrieval").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (opts) => {
|
|
@@ -1398,7 +1460,7 @@ function inboxCommand() {
|
|
|
1398
1460
|
handleError(err);
|
|
1399
1461
|
}
|
|
1400
1462
|
});
|
|
1401
|
-
const mode = new
|
|
1463
|
+
const mode = new import_commander10.Command("mode").description(
|
|
1402
1464
|
"Reception policy: who can send to your inbox and how"
|
|
1403
1465
|
);
|
|
1404
1466
|
mode.command("get").description("Show current reception policy").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (opts) => {
|
|
@@ -1433,7 +1495,7 @@ ${formatPolicy(res)}`);
|
|
|
1433
1495
|
}
|
|
1434
1496
|
);
|
|
1435
1497
|
cmd.addCommand(mode);
|
|
1436
|
-
const allowlist = new
|
|
1498
|
+
const allowlist = new import_commander10.Command("allowlist").description(
|
|
1437
1499
|
"Trusted senders (effective when mode=allowlist)"
|
|
1438
1500
|
);
|
|
1439
1501
|
allowlist.command("list").description("List agents on your allowlist").option("--limit <n>", "Max items to return (default 100)", parseInt).option("--offset <n>", "Pagination offset", parseInt).option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (opts) => {
|
|
@@ -1492,7 +1554,7 @@ ${formatPolicy(res)}`);
|
|
|
1492
1554
|
}
|
|
1493
1555
|
|
|
1494
1556
|
// src/commands/listen.ts
|
|
1495
|
-
var
|
|
1557
|
+
var import_commander11 = require("commander");
|
|
1496
1558
|
var import_child_process3 = require("child_process");
|
|
1497
1559
|
var import_ws = __toESM(require("ws"));
|
|
1498
1560
|
|
|
@@ -1507,6 +1569,33 @@ function asRecord(v) {
|
|
|
1507
1569
|
function asNonEmptyString(v) {
|
|
1508
1570
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
1509
1571
|
}
|
|
1572
|
+
function asInferencePath(v) {
|
|
1573
|
+
return v === "official" || v === "byo" ? v : null;
|
|
1574
|
+
}
|
|
1575
|
+
var HOST_INFERENCE_HOSTS = /* @__PURE__ */ new Set([
|
|
1576
|
+
"api.agentplanet.org",
|
|
1577
|
+
"api.agenticplanet.space"
|
|
1578
|
+
]);
|
|
1579
|
+
function asHostInferenceUrl(v) {
|
|
1580
|
+
const raw = asNonEmptyString(v);
|
|
1581
|
+
if (!raw) return null;
|
|
1582
|
+
try {
|
|
1583
|
+
const u = new URL(raw);
|
|
1584
|
+
if (u.pathname.replace(/\/+$/, "") !== "/api/inference/v1") return null;
|
|
1585
|
+
if (u.search || u.hash) return null;
|
|
1586
|
+
const host = u.hostname.toLowerCase();
|
|
1587
|
+
const loopback = host === "localhost" || host === "127.0.0.1" || host === "::1";
|
|
1588
|
+
if (u.protocol === "https:" && HOST_INFERENCE_HOSTS.has(host)) {
|
|
1589
|
+
return `${u.origin}/api/inference/v1`;
|
|
1590
|
+
}
|
|
1591
|
+
if (u.protocol === "http:" && loopback) {
|
|
1592
|
+
return `${u.origin}/api/inference/v1`;
|
|
1593
|
+
}
|
|
1594
|
+
return null;
|
|
1595
|
+
} catch {
|
|
1596
|
+
return null;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1510
1599
|
function parseJsonRpcBody(bodyText) {
|
|
1511
1600
|
let parsed;
|
|
1512
1601
|
try {
|
|
@@ -1598,7 +1687,10 @@ function extractChatEnvelope(message) {
|
|
|
1598
1687
|
gateway_message_id: asNonEmptyString(ap.message_id) ?? asNonEmptyString(ap.messageId),
|
|
1599
1688
|
user_text: extractUserText(message),
|
|
1600
1689
|
requested_model: requested ? requested.slice(0, 200) : null,
|
|
1601
|
-
max_output_tokens: maxOut
|
|
1690
|
+
max_output_tokens: maxOut,
|
|
1691
|
+
hop_id: asNonEmptyString(ap.hop_id),
|
|
1692
|
+
inference_path: asInferencePath(ap.inference_path),
|
|
1693
|
+
host_inference_url: asHostInferenceUrl(ap.host_inference_url)
|
|
1602
1694
|
};
|
|
1603
1695
|
}
|
|
1604
1696
|
function normalizeEvent(body, opts = {}) {
|
|
@@ -1657,6 +1749,115 @@ var DedupeStore = class {
|
|
|
1657
1749
|
}
|
|
1658
1750
|
};
|
|
1659
1751
|
|
|
1752
|
+
// src/commands/official-hop-door.ts
|
|
1753
|
+
var import_node_http = __toESM(require("http"));
|
|
1754
|
+
function shouldOpenOfficialDoor(opts) {
|
|
1755
|
+
return Boolean(
|
|
1756
|
+
opts.inferencePath === "official" && opts.hopId?.trim() && asHostInferenceUrl(opts.hostInferenceUrl) && opts.jwt?.trim()
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
function readBody(req) {
|
|
1760
|
+
return new Promise((resolve, reject) => {
|
|
1761
|
+
const chunks = [];
|
|
1762
|
+
req.on("data", (c) => chunks.push(Buffer.from(c)));
|
|
1763
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
1764
|
+
req.on("error", reject);
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
function send(res, status, body, contentType = "application/json") {
|
|
1768
|
+
res.writeHead(status, {
|
|
1769
|
+
"content-type": contentType,
|
|
1770
|
+
"content-length": String(body.length)
|
|
1771
|
+
});
|
|
1772
|
+
res.end(body);
|
|
1773
|
+
}
|
|
1774
|
+
async function handleDoorRequest(req, res, opts) {
|
|
1775
|
+
const path = (req.url ?? "").split("?")[0];
|
|
1776
|
+
if (req.method !== "POST" || path !== "/v1/chat/completions" && path !== "/chat/completions") {
|
|
1777
|
+
send(res, 404, Buffer.from('{"error":"not_found"}'));
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
let payload;
|
|
1781
|
+
try {
|
|
1782
|
+
const raw = await readBody(req);
|
|
1783
|
+
payload = JSON.parse(raw.length ? raw.toString("utf-8") : "{}");
|
|
1784
|
+
} catch {
|
|
1785
|
+
send(res, 400, Buffer.from('{"error":"invalid_json"}'));
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
1789
|
+
send(res, 400, Buffer.from('{"error":"invalid_json"}'));
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
const body = { ...payload };
|
|
1793
|
+
delete body.agent_id;
|
|
1794
|
+
body.hop_id = opts.hopId;
|
|
1795
|
+
const headers = {
|
|
1796
|
+
authorization: `Bearer ${opts.jwt}`,
|
|
1797
|
+
"content-type": "application/json",
|
|
1798
|
+
"X-Hop-Id": opts.hopId
|
|
1799
|
+
};
|
|
1800
|
+
if (opts.agentId) headers["X-Agent-Id"] = opts.agentId;
|
|
1801
|
+
try {
|
|
1802
|
+
const upstream = await opts.fetchFn(opts.upstream, {
|
|
1803
|
+
method: "POST",
|
|
1804
|
+
headers,
|
|
1805
|
+
body: JSON.stringify(body)
|
|
1806
|
+
});
|
|
1807
|
+
const out = Buffer.from(await upstream.arrayBuffer());
|
|
1808
|
+
const ct = upstream.headers.get("content-type") || "application/json";
|
|
1809
|
+
send(res, upstream.status, out, ct);
|
|
1810
|
+
} catch (err) {
|
|
1811
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1812
|
+
send(
|
|
1813
|
+
res,
|
|
1814
|
+
502,
|
|
1815
|
+
Buffer.from(JSON.stringify({ error: `upstream_unreachable:${msg.slice(0, 120)}` }))
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
function closeServer(server) {
|
|
1820
|
+
return new Promise((resolve) => {
|
|
1821
|
+
server.closeAllConnections?.();
|
|
1822
|
+
server.close(() => resolve());
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
async function startOfficialHopDoor(opts) {
|
|
1826
|
+
const dest = asHostInferenceUrl(opts.hostInferenceUrl);
|
|
1827
|
+
const hopId = opts.hopId.trim();
|
|
1828
|
+
const jwt = opts.jwt.trim();
|
|
1829
|
+
if (!dest || !hopId || !jwt) return null;
|
|
1830
|
+
const fetchFn = opts.fetchFn ?? fetch;
|
|
1831
|
+
const upstream = `${dest}/chat/completions`;
|
|
1832
|
+
const server = import_node_http.default.createServer((req, res) => {
|
|
1833
|
+
void handleDoorRequest(req, res, {
|
|
1834
|
+
upstream,
|
|
1835
|
+
hopId,
|
|
1836
|
+
agentId: opts.agentId,
|
|
1837
|
+
jwt,
|
|
1838
|
+
fetchFn
|
|
1839
|
+
});
|
|
1840
|
+
});
|
|
1841
|
+
try {
|
|
1842
|
+
await new Promise((resolve, reject) => {
|
|
1843
|
+
server.once("error", reject);
|
|
1844
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
1845
|
+
});
|
|
1846
|
+
} catch {
|
|
1847
|
+
return null;
|
|
1848
|
+
}
|
|
1849
|
+
const addr = server.address();
|
|
1850
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
1851
|
+
if (!port) {
|
|
1852
|
+
await closeServer(server);
|
|
1853
|
+
return null;
|
|
1854
|
+
}
|
|
1855
|
+
return {
|
|
1856
|
+
baseUrl: `http://127.0.0.1:${port}/v1`,
|
|
1857
|
+
close: () => closeServer(server)
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1660
1861
|
// src/commands/chat-writeback.ts
|
|
1661
1862
|
var DEFAULT_COMPLETE_TIMEOUT_MS = 12e4;
|
|
1662
1863
|
var DEFAULT_WRITEBACK_TIMEOUT_MS = 3e4;
|
|
@@ -1741,8 +1942,8 @@ function extractUsage(payload) {
|
|
|
1741
1942
|
const rec = asRecord2(payload);
|
|
1742
1943
|
if (!rec) return void 0;
|
|
1743
1944
|
const usageRec = asRecord2(rec.usage) ?? rec;
|
|
1744
|
-
const input = asNonNegInt(usageRec.input_tokens) ?? asNonNegInt(usageRec.prompt_tokens);
|
|
1745
|
-
const output2 = asNonNegInt(usageRec.output_tokens) ?? asNonNegInt(usageRec.completion_tokens);
|
|
1945
|
+
const input = asNonNegInt(usageRec.input_tokens) ?? asNonNegInt(usageRec.prompt_tokens) ?? asNonNegInt(usageRec.input);
|
|
1946
|
+
const output2 = asNonNegInt(usageRec.output_tokens) ?? asNonNegInt(usageRec.completion_tokens) ?? asNonNegInt(usageRec.output);
|
|
1746
1947
|
if (input === null && output2 === null) return void 0;
|
|
1747
1948
|
const out = {
|
|
1748
1949
|
input_tokens: input ?? 0,
|
|
@@ -1754,6 +1955,18 @@ function extractUsage(payload) {
|
|
|
1754
1955
|
}
|
|
1755
1956
|
const modelId = extractModelId(payload);
|
|
1756
1957
|
if (modelId) out.model_id = modelId;
|
|
1958
|
+
const reasoning = asNonNegInt(usageRec.reasoning_tokens) ?? asNonNegInt(usageRec.reasoningTokens);
|
|
1959
|
+
const cacheRead = asNonNegInt(usageRec.cache_read_tokens) ?? asNonNegInt(usageRec.cacheRead);
|
|
1960
|
+
const cacheWrite = asNonNegInt(usageRec.cache_write_tokens) ?? asNonNegInt(usageRec.cacheWrite);
|
|
1961
|
+
const total = asNonNegInt(usageRec.total_tokens) ?? asNonNegInt(usageRec.total);
|
|
1962
|
+
const duration = asNonNegInt(usageRec.duration_ms) ?? asNonNegInt(usageRec.durationMs) ?? asNonNegInt(rec.duration_ms) ?? asNonNegInt(rec.durationMs);
|
|
1963
|
+
const provider = typeof usageRec.provider === "string" && usageRec.provider.trim() || typeof rec.provider === "string" && rec.provider.trim() || "";
|
|
1964
|
+
if (reasoning !== null) out.reasoning_tokens = reasoning;
|
|
1965
|
+
if (cacheRead !== null) out.cache_read_tokens = cacheRead;
|
|
1966
|
+
if (cacheWrite !== null) out.cache_write_tokens = cacheWrite;
|
|
1967
|
+
if (total !== null) out.total_tokens = total;
|
|
1968
|
+
if (duration !== null) out.duration_ms = duration;
|
|
1969
|
+
if (provider) out.provider = provider.slice(0, 80);
|
|
1757
1970
|
return out;
|
|
1758
1971
|
}
|
|
1759
1972
|
function parseCompletePayload(payload) {
|
|
@@ -1811,6 +2024,34 @@ async function mintAgentJwt(opts, fetchFn = fetch) {
|
|
|
1811
2024
|
function clearAgentJwtCache() {
|
|
1812
2025
|
cachedJwt = null;
|
|
1813
2026
|
}
|
|
2027
|
+
function completeInferenceEnv(event, opts, jwt, door) {
|
|
2028
|
+
const extra = { ACN_AGENT_ID: opts.agentId };
|
|
2029
|
+
const chat = event.chat;
|
|
2030
|
+
if (chat?.hop_id) extra.ACN_CHAT_HOP_ID = chat.hop_id;
|
|
2031
|
+
if (chat?.inference_path) extra.ACN_INFERENCE_PATH = chat.inference_path;
|
|
2032
|
+
if (chat?.host_inference_url) {
|
|
2033
|
+
extra.ACN_HOST_INFERENCE_URL = chat.host_inference_url;
|
|
2034
|
+
}
|
|
2035
|
+
if (jwt) extra.ACN_AGENT_JWT = jwt;
|
|
2036
|
+
if (door?.baseUrl && jwt) {
|
|
2037
|
+
extra.OPENAI_BASE_URL = door.baseUrl;
|
|
2038
|
+
extra.OPENAI_API_KEY = jwt;
|
|
2039
|
+
}
|
|
2040
|
+
return { ...process.env, ...extra };
|
|
2041
|
+
}
|
|
2042
|
+
function completeInferenceHeaders(event, opts) {
|
|
2043
|
+
const headers = {
|
|
2044
|
+
"content-type": "application/json"
|
|
2045
|
+
};
|
|
2046
|
+
if (opts.agentId) headers["X-ACN-Agent-Id"] = opts.agentId;
|
|
2047
|
+
const chat = event.chat;
|
|
2048
|
+
if (chat?.hop_id) headers["X-ACN-Hop-Id"] = chat.hop_id;
|
|
2049
|
+
if (chat?.inference_path) headers["X-ACN-Inference-Path"] = chat.inference_path;
|
|
2050
|
+
if (chat?.host_inference_url) {
|
|
2051
|
+
headers["X-ACN-Host-Inference-Url"] = chat.host_inference_url;
|
|
2052
|
+
}
|
|
2053
|
+
return headers;
|
|
2054
|
+
}
|
|
1814
2055
|
async function completeViaHttp(event, opts, deps) {
|
|
1815
2056
|
const fetchFn = deps.fetchFn ?? fetch;
|
|
1816
2057
|
const timeoutMs = opts.completeTimeoutMs ?? DEFAULT_COMPLETE_TIMEOUT_MS;
|
|
@@ -1819,7 +2060,7 @@ async function completeViaHttp(event, opts, deps) {
|
|
|
1819
2060
|
try {
|
|
1820
2061
|
const res = await fetchFn(opts.completeUrl, {
|
|
1821
2062
|
method: "POST",
|
|
1822
|
-
headers:
|
|
2063
|
+
headers: completeInferenceHeaders(event, opts),
|
|
1823
2064
|
body: JSON.stringify(event),
|
|
1824
2065
|
signal: controller.signal
|
|
1825
2066
|
});
|
|
@@ -1844,13 +2085,16 @@ async function completeViaHttp(event, opts, deps) {
|
|
|
1844
2085
|
clearTimeout(timer);
|
|
1845
2086
|
}
|
|
1846
2087
|
}
|
|
1847
|
-
function
|
|
2088
|
+
function spawnCompleteExec(event, opts, deps, jwt, door) {
|
|
1848
2089
|
const spawnFn = deps.spawnFn ?? import_child_process.spawn;
|
|
1849
2090
|
const timeoutMs = opts.completeTimeoutMs ?? DEFAULT_COMPLETE_TIMEOUT_MS;
|
|
1850
2091
|
const body = Buffer.from(JSON.stringify(event), "utf-8");
|
|
1851
2092
|
return new Promise((resolve) => {
|
|
1852
2093
|
let settled = false;
|
|
1853
|
-
const child = spawnFn(opts.completeExec, {
|
|
2094
|
+
const child = spawnFn(opts.completeExec, {
|
|
2095
|
+
shell: true,
|
|
2096
|
+
env: completeInferenceEnv(event, opts, jwt, door)
|
|
2097
|
+
});
|
|
1854
2098
|
const stdout = [];
|
|
1855
2099
|
const stderr = [];
|
|
1856
2100
|
const finish = (result) => {
|
|
@@ -1889,6 +2133,44 @@ function completeViaExec(event, opts, deps) {
|
|
|
1889
2133
|
child.stdin?.end(body);
|
|
1890
2134
|
});
|
|
1891
2135
|
}
|
|
2136
|
+
async function completeViaExec(event, opts, deps, jwt) {
|
|
2137
|
+
let door = null;
|
|
2138
|
+
const logFn = deps.logFn ?? ((line) => console.error(line));
|
|
2139
|
+
try {
|
|
2140
|
+
if (shouldOpenOfficialDoor({
|
|
2141
|
+
inferencePath: event.chat?.inference_path,
|
|
2142
|
+
hopId: event.chat?.hop_id,
|
|
2143
|
+
hostInferenceUrl: event.chat?.host_inference_url,
|
|
2144
|
+
jwt
|
|
2145
|
+
})) {
|
|
2146
|
+
try {
|
|
2147
|
+
door = await startOfficialHopDoor({
|
|
2148
|
+
hostInferenceUrl: event.chat.host_inference_url,
|
|
2149
|
+
hopId: event.chat.hop_id,
|
|
2150
|
+
agentId: opts.agentId,
|
|
2151
|
+
jwt,
|
|
2152
|
+
fetchFn: deps.fetchFn
|
|
2153
|
+
});
|
|
2154
|
+
} catch {
|
|
2155
|
+
door = null;
|
|
2156
|
+
}
|
|
2157
|
+
if (door) {
|
|
2158
|
+
logFn(
|
|
2159
|
+
`[acn listen] official_door chat_id=${event.chat.chat_id} base=${door.baseUrl}`
|
|
2160
|
+
);
|
|
2161
|
+
} else {
|
|
2162
|
+
logFn(
|
|
2163
|
+
`[acn listen] official_door_skipped chat_id=${event.chat.chat_id}`
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
return await spawnCompleteExec(event, opts, deps, jwt, door);
|
|
2168
|
+
} finally {
|
|
2169
|
+
if (door) {
|
|
2170
|
+
await door.close().catch(() => void 0);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
1892
2174
|
async function postWriteback(event, complete, opts, deps) {
|
|
1893
2175
|
const chat = event.chat;
|
|
1894
2176
|
if (!chat) return { ok: false, reason: "no_chat_envelope" };
|
|
@@ -1926,6 +2208,24 @@ async function postWriteback(event, complete, opts, deps) {
|
|
|
1926
2208
|
if (complete.usage.model_id) {
|
|
1927
2209
|
usageBody.model_id = complete.usage.model_id;
|
|
1928
2210
|
}
|
|
2211
|
+
if (complete.usage.reasoning_tokens != null) {
|
|
2212
|
+
usageBody.reasoning_tokens = complete.usage.reasoning_tokens;
|
|
2213
|
+
}
|
|
2214
|
+
if (complete.usage.cache_read_tokens != null) {
|
|
2215
|
+
usageBody.cache_read_tokens = complete.usage.cache_read_tokens;
|
|
2216
|
+
}
|
|
2217
|
+
if (complete.usage.cache_write_tokens != null) {
|
|
2218
|
+
usageBody.cache_write_tokens = complete.usage.cache_write_tokens;
|
|
2219
|
+
}
|
|
2220
|
+
if (complete.usage.total_tokens != null) {
|
|
2221
|
+
usageBody.total_tokens = complete.usage.total_tokens;
|
|
2222
|
+
}
|
|
2223
|
+
if (complete.usage.duration_ms != null) {
|
|
2224
|
+
usageBody.duration_ms = complete.usage.duration_ms;
|
|
2225
|
+
}
|
|
2226
|
+
if (complete.usage.provider) {
|
|
2227
|
+
usageBody.provider = complete.usage.provider;
|
|
2228
|
+
}
|
|
1929
2229
|
body.usage = usageBody;
|
|
1930
2230
|
} else if (complete.modelId) {
|
|
1931
2231
|
body.usage = { model_id: complete.modelId };
|
|
@@ -1989,7 +2289,12 @@ async function postWriteback(event, complete, opts, deps) {
|
|
|
1989
2289
|
async function handleChatWriteback(event, opts, deps = {}) {
|
|
1990
2290
|
const logFn = deps.logFn ?? ((line) => console.error(line));
|
|
1991
2291
|
if (!event.chat) return { ok: false, reason: "no_chat_envelope" };
|
|
1992
|
-
|
|
2292
|
+
let jwt = null;
|
|
2293
|
+
if (event.chat.inference_path === "official" && opts.completeExec) {
|
|
2294
|
+
const minted = await mintAgentJwt(opts, deps.fetchFn ?? fetch);
|
|
2295
|
+
if (minted.ok) jwt = minted.token;
|
|
2296
|
+
}
|
|
2297
|
+
const completed = opts.completeUrl ? await completeViaHttp(event, opts, deps) : await completeViaExec(event, opts, deps, jwt);
|
|
1993
2298
|
if (!completed.ok) {
|
|
1994
2299
|
logFn(
|
|
1995
2300
|
`[acn listen] chat_complete_failed chat_id=${event.chat.chat_id} message_id=${event.message_id} reason=${completed.reason}`
|
|
@@ -2214,7 +2519,7 @@ function formatWakeFailed(event, reason) {
|
|
|
2214
2519
|
function formatDeduped(event) {
|
|
2215
2520
|
return `[acn listen] deduped key=${dedupeKey(event)}`;
|
|
2216
2521
|
}
|
|
2217
|
-
function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore,
|
|
2522
|
+
function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore, send2, deps = {}) {
|
|
2218
2523
|
const logFn = deps.logFn ?? ((line) => console.error(line));
|
|
2219
2524
|
const result = processIncomingRequest(
|
|
2220
2525
|
correlationId,
|
|
@@ -2223,7 +2528,7 @@ function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore, send,
|
|
|
2223
2528
|
dedupeStore,
|
|
2224
2529
|
deps
|
|
2225
2530
|
);
|
|
2226
|
-
|
|
2531
|
+
send2(result.response);
|
|
2227
2532
|
if (result.dedupeHit && result.event) {
|
|
2228
2533
|
logFn(formatDeduped(result.event));
|
|
2229
2534
|
return;
|
|
@@ -2284,7 +2589,7 @@ function errorResponse(id, status, detail) {
|
|
|
2284
2589
|
body: JSON.stringify({ error: detail })
|
|
2285
2590
|
};
|
|
2286
2591
|
}
|
|
2287
|
-
async function dispatchA2aRequest(frame, opts,
|
|
2592
|
+
async function dispatchA2aRequest(frame, opts, send2, deps = {}) {
|
|
2288
2593
|
const bodyBuf = decodeBody(frame);
|
|
2289
2594
|
try {
|
|
2290
2595
|
if (opts.runtime) {
|
|
@@ -2294,7 +2599,7 @@ async function dispatchA2aRequest(frame, opts, send, deps = {}) {
|
|
|
2294
2599
|
bodyBuf.toString("utf-8"),
|
|
2295
2600
|
opts.runtime,
|
|
2296
2601
|
store,
|
|
2297
|
-
|
|
2602
|
+
send2,
|
|
2298
2603
|
{
|
|
2299
2604
|
fetchFn: deps.fetchFn,
|
|
2300
2605
|
spawnFn: deps.spawnFn,
|
|
@@ -2304,17 +2609,17 @@ async function dispatchA2aRequest(frame, opts, send, deps = {}) {
|
|
|
2304
2609
|
return;
|
|
2305
2610
|
}
|
|
2306
2611
|
if (opts.forward) {
|
|
2307
|
-
await forwardToHttp(frame, bodyBuf, opts.forward, deps.fetchFn ?? fetch,
|
|
2612
|
+
await forwardToHttp(frame, bodyBuf, opts.forward, deps.fetchFn ?? fetch, send2);
|
|
2308
2613
|
return;
|
|
2309
2614
|
}
|
|
2310
2615
|
if (opts.exec) {
|
|
2311
|
-
|
|
2616
|
+
send2(await runExec(frame, bodyBuf, opts.exec, deps.spawnFn ?? import_child_process3.spawn));
|
|
2312
2617
|
return;
|
|
2313
2618
|
}
|
|
2314
|
-
|
|
2619
|
+
send2(errorResponse(frame.id, 500, "no handler configured"));
|
|
2315
2620
|
} catch (err) {
|
|
2316
2621
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2317
|
-
|
|
2622
|
+
send2(errorResponse(frame.id, 502, `handler failed: ${msg}`));
|
|
2318
2623
|
}
|
|
2319
2624
|
}
|
|
2320
2625
|
function buildForwardHeaders(frame) {
|
|
@@ -2324,7 +2629,7 @@ function buildForwardHeaders(frame) {
|
|
|
2324
2629
|
}
|
|
2325
2630
|
return headers;
|
|
2326
2631
|
}
|
|
2327
|
-
async function forwardToHttp(frame, bodyBuf, base, fetchFn,
|
|
2632
|
+
async function forwardToHttp(frame, bodyBuf, base, fetchFn, send2) {
|
|
2328
2633
|
const suffix = frame.path && frame.path !== "/" ? "/" + frame.path.replace(/^\//, "") : "";
|
|
2329
2634
|
const targetUrl = base.replace(/\/$/, "") + suffix;
|
|
2330
2635
|
const method = (frame.method ?? "POST").toUpperCase();
|
|
@@ -2342,7 +2647,7 @@ async function forwardToHttp(frame, bodyBuf, base, fetchFn, send) {
|
|
|
2342
2647
|
const { done, value } = await reader.read();
|
|
2343
2648
|
if (done) break;
|
|
2344
2649
|
if (value && value.length > 0) {
|
|
2345
|
-
|
|
2650
|
+
send2({
|
|
2346
2651
|
type: "a2a_stream_chunk",
|
|
2347
2652
|
id: frame.id,
|
|
2348
2653
|
seq: seq++,
|
|
@@ -2351,17 +2656,17 @@ async function forwardToHttp(frame, bodyBuf, base, fetchFn, send) {
|
|
|
2351
2656
|
});
|
|
2352
2657
|
}
|
|
2353
2658
|
}
|
|
2354
|
-
|
|
2659
|
+
send2({ type: "a2a_stream_end", id: frame.id, status: res.status });
|
|
2355
2660
|
} catch (err) {
|
|
2356
2661
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2357
|
-
|
|
2662
|
+
send2({ type: "a2a_stream_end", id: frame.id, error: msg });
|
|
2358
2663
|
} finally {
|
|
2359
2664
|
reader.releaseLock();
|
|
2360
2665
|
}
|
|
2361
2666
|
return;
|
|
2362
2667
|
}
|
|
2363
2668
|
const respText = await res.text();
|
|
2364
|
-
|
|
2669
|
+
send2({
|
|
2365
2670
|
type: "a2a_response",
|
|
2366
2671
|
id: frame.id,
|
|
2367
2672
|
status: res.status,
|
|
@@ -2472,12 +2777,12 @@ function runListener(cfg) {
|
|
|
2472
2777
|
if (!frame || typeof frame !== "object") return;
|
|
2473
2778
|
const f = frame;
|
|
2474
2779
|
if (f.type === "a2a_request" && typeof f.id === "string") {
|
|
2475
|
-
const
|
|
2780
|
+
const send2 = (out) => {
|
|
2476
2781
|
if (ws.readyState === import_ws.default.OPEN) {
|
|
2477
2782
|
ws.send(JSON.stringify(out));
|
|
2478
2783
|
}
|
|
2479
2784
|
};
|
|
2480
|
-
void dispatchA2aRequest(f, cfg,
|
|
2785
|
+
void dispatchA2aRequest(f, cfg, send2, { dedupeStore });
|
|
2481
2786
|
}
|
|
2482
2787
|
});
|
|
2483
2788
|
ws.on("close", (code, reason) => {
|
|
@@ -2526,7 +2831,7 @@ function validateListenHandlerFlags(opts) {
|
|
|
2526
2831
|
});
|
|
2527
2832
|
}
|
|
2528
2833
|
function listenCommand() {
|
|
2529
|
-
const cmd = new
|
|
2834
|
+
const cmd = new import_commander11.Command("listen").description(
|
|
2530
2835
|
"Hold an outbound connection to ACN and answer relayed A2A requests in real time (ADR-0012 Mode B). Prefer --runtime for production; --forward/--exec remain as compatibility tunnels."
|
|
2531
2836
|
).option(
|
|
2532
2837
|
"--runtime <id>",
|
|
@@ -2704,7 +3009,7 @@ function listenCommand() {
|
|
|
2704
3009
|
}
|
|
2705
3010
|
|
|
2706
3011
|
// src/commands/delivery.ts
|
|
2707
|
-
var
|
|
3012
|
+
var import_commander12 = require("commander");
|
|
2708
3013
|
var DELIVERY_DESC = {
|
|
2709
3014
|
direct: "direct (Mode A) \u2014 ACN dials your public A2A endpoint over HTTP",
|
|
2710
3015
|
relay: "relay (Mode B) \u2014 hold an outbound WebSocket with `acn listen`; no public URL",
|
|
@@ -2743,7 +3048,7 @@ function formatDelivery(d) {
|
|
|
2743
3048
|
return lines.join("\n");
|
|
2744
3049
|
}
|
|
2745
3050
|
function deliveryCommand() {
|
|
2746
|
-
const cmd = new
|
|
3051
|
+
const cmd = new import_commander12.Command("delivery").description(
|
|
2747
3052
|
"Inbound delivery transport (Mode A direct / Mode B relay). Orthogonal to reception policy (`acn inbox mode`)."
|
|
2748
3053
|
);
|
|
2749
3054
|
cmd.command("get").description("Show derived delivery transport (direct | relay | none)").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (opts) => {
|
|
@@ -2804,7 +3109,7 @@ function deliveryCommand() {
|
|
|
2804
3109
|
}
|
|
2805
3110
|
|
|
2806
3111
|
// src/commands/session.ts
|
|
2807
|
-
var
|
|
3112
|
+
var import_commander13 = require("commander");
|
|
2808
3113
|
function requireAgentId4() {
|
|
2809
3114
|
const config = loadConfig();
|
|
2810
3115
|
if (!config.api_key) {
|
|
@@ -2849,7 +3154,7 @@ function formatEntry2(s, index) {
|
|
|
2849
3154
|
return lines.join("\n");
|
|
2850
3155
|
}
|
|
2851
3156
|
function sessionCommand() {
|
|
2852
|
-
const cmd = new
|
|
3157
|
+
const cmd = new import_commander13.Command("session").description(
|
|
2853
3158
|
"Real-time session layer: bidirectional channel between two agents"
|
|
2854
3159
|
);
|
|
2855
3160
|
cmd.command("invite <target_agent_id>").description("Invite an agent to a real-time session").option("--ttl-seconds <s>", "Session TTL in seconds (60\u20131800, default 300)", parseInt).option("--metadata <json>", "Optional JSON object attached to the invitation (max 4KB)").action(
|
|
@@ -2927,7 +3232,7 @@ ${formatEntry2(res)}`);
|
|
|
2927
3232
|
}
|
|
2928
3233
|
|
|
2929
3234
|
// src/commands/subnet.ts
|
|
2930
|
-
var
|
|
3235
|
+
var import_commander14 = require("commander");
|
|
2931
3236
|
function requireAgentId5() {
|
|
2932
3237
|
const config = loadConfig();
|
|
2933
3238
|
if (!config.api_key) {
|
|
@@ -2940,7 +3245,7 @@ function requireAgentId5() {
|
|
|
2940
3245
|
}
|
|
2941
3246
|
return config.agent_id;
|
|
2942
3247
|
}
|
|
2943
|
-
function
|
|
3248
|
+
function requireApiKey2() {
|
|
2944
3249
|
const config = loadConfig();
|
|
2945
3250
|
if (!config.api_key) {
|
|
2946
3251
|
console.error("No API key found. Run `acn join` first or `acn config set api-key <key>`.");
|
|
@@ -3015,7 +3320,7 @@ function formatSubnet(s, index) {
|
|
|
3015
3320
|
return lines.join("\n");
|
|
3016
3321
|
}
|
|
3017
3322
|
function subnetCommand() {
|
|
3018
|
-
const cmd = new
|
|
3323
|
+
const cmd = new import_commander14.Command("subnet").description("Manage ACN subnets");
|
|
3019
3324
|
cmd.command("list").description(
|
|
3020
3325
|
"List subnets. Without --all/--parent shows only subnets you have joined."
|
|
3021
3326
|
).option("--all", "Show all public subnets on ACN (not just your own)").option(
|
|
@@ -3232,7 +3537,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3232
3537
|
handleError(err);
|
|
3233
3538
|
}
|
|
3234
3539
|
});
|
|
3235
|
-
const requests = new
|
|
3540
|
+
const requests = new import_commander14.Command("requests").description(
|
|
3236
3541
|
"Manage join-requests for a subnet (ADR-0004)"
|
|
3237
3542
|
);
|
|
3238
3543
|
requests.command("list <subnet_id>").description(
|
|
@@ -3246,7 +3551,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3246
3551
|
"join_request"
|
|
3247
3552
|
).option("--limit <n>", "Page size (1-500, default 100)", "100").option("--offset <n>", "Page offset (default 0)", "0").action(
|
|
3248
3553
|
async (subnetId, opts) => {
|
|
3249
|
-
|
|
3554
|
+
requireApiKey2();
|
|
3250
3555
|
const params = {};
|
|
3251
3556
|
if (opts.status) params.status = opts.status;
|
|
3252
3557
|
if (opts.kind) params.kind = opts.kind;
|
|
@@ -3312,7 +3617,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3312
3617
|
});
|
|
3313
3618
|
requests.command("approve <subnet_id>").description("Owner-only: approve a pending join_request (CAS pending \u2192 approved).").requiredOption("--request-id <rid>", "Join request ID to approve").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
|
|
3314
3619
|
async (subnetId, opts) => {
|
|
3315
|
-
|
|
3620
|
+
requireApiKey2();
|
|
3316
3621
|
const body = {};
|
|
3317
3622
|
if (opts.note !== void 0) body.note = opts.note;
|
|
3318
3623
|
try {
|
|
@@ -3331,7 +3636,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3331
3636
|
);
|
|
3332
3637
|
requests.command("reject <subnet_id>").description("Owner-only: reject a pending join_request (CAS pending \u2192 rejected).").requiredOption("--request-id <rid>", "Join request ID to reject").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
|
|
3333
3638
|
async (subnetId, opts) => {
|
|
3334
|
-
|
|
3639
|
+
requireApiKey2();
|
|
3335
3640
|
const body = {};
|
|
3336
3641
|
if (opts.note !== void 0) body.note = opts.note;
|
|
3337
3642
|
try {
|
|
@@ -3352,7 +3657,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3352
3657
|
"Applicant-only: withdraw your own pending join_request (CAS pending \u2192 withdrawn)."
|
|
3353
3658
|
).requiredOption("--request-id <rid>", "Your join request ID").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
|
|
3354
3659
|
async (subnetId, opts) => {
|
|
3355
|
-
|
|
3660
|
+
requireApiKey2();
|
|
3356
3661
|
const body = {};
|
|
3357
3662
|
if (opts.note !== void 0) body.note = opts.note;
|
|
3358
3663
|
try {
|
|
@@ -3374,14 +3679,14 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3374
3679
|
}
|
|
3375
3680
|
);
|
|
3376
3681
|
cmd.addCommand(requests);
|
|
3377
|
-
const invitations = new
|
|
3682
|
+
const invitations = new import_commander14.Command("invitations").description(
|
|
3378
3683
|
"Manage invitations on a subnet (ADR-0004)"
|
|
3379
3684
|
);
|
|
3380
3685
|
invitations.command("send <subnet_id>").description(
|
|
3381
3686
|
"Owner-only: invite an agent to a subnet. Auto-merges with a target's pending join_request (collapses to auto-approval)."
|
|
3382
3687
|
).requiredOption("--agent-id <aid>", "Agent ID to invite").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
|
|
3383
3688
|
async (subnetId, opts) => {
|
|
3384
|
-
|
|
3689
|
+
requireApiKey2();
|
|
3385
3690
|
const body = { agent_id: opts.agentId };
|
|
3386
3691
|
if (opts.note !== void 0) body.note = opts.note;
|
|
3387
3692
|
try {
|
|
@@ -3400,7 +3705,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3400
3705
|
"Filter by status: pending | approved | rejected | withdrawn"
|
|
3401
3706
|
).option("--limit <n>", "Page size (1-500, default 100)", "100").option("--offset <n>", "Page offset (default 0)", "0").action(
|
|
3402
3707
|
async (subnetId, opts) => {
|
|
3403
|
-
|
|
3708
|
+
requireApiKey2();
|
|
3404
3709
|
const params = {};
|
|
3405
3710
|
if (opts.status) params.status = opts.status;
|
|
3406
3711
|
if (opts.limit) params.limit = opts.limit;
|
|
@@ -3453,7 +3758,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3453
3758
|
"Invitee-only: accept a pending invitation (CAS pending \u2192 approved). Side effect: you join the subnet."
|
|
3454
3759
|
).requiredOption("--invitation-id <iid>", "Invitation ID to accept").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
|
|
3455
3760
|
async (subnetId, opts) => {
|
|
3456
|
-
|
|
3761
|
+
requireApiKey2();
|
|
3457
3762
|
const body = {};
|
|
3458
3763
|
if (opts.note !== void 0) body.note = opts.note;
|
|
3459
3764
|
try {
|
|
@@ -3474,7 +3779,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3474
3779
|
"Invitee-only: reject a pending invitation (CAS pending \u2192 rejected). No membership change."
|
|
3475
3780
|
).requiredOption("--invitation-id <iid>", "Invitation ID to reject").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
|
|
3476
3781
|
async (subnetId, opts) => {
|
|
3477
|
-
|
|
3782
|
+
requireApiKey2();
|
|
3478
3783
|
const body = {};
|
|
3479
3784
|
if (opts.note !== void 0) body.note = opts.note;
|
|
3480
3785
|
try {
|
|
@@ -3495,7 +3800,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3495
3800
|
"Owner-only: cancel a pending invitation (CAS pending \u2192 withdrawn)."
|
|
3496
3801
|
).requiredOption("--invitation-id <iid>", "Invitation ID to cancel").action(
|
|
3497
3802
|
async (subnetId, opts) => {
|
|
3498
|
-
|
|
3803
|
+
requireApiKey2();
|
|
3499
3804
|
try {
|
|
3500
3805
|
const res = await acnDelete(
|
|
3501
3806
|
`/subnets/${subnetId}/invitations/${opts.invitationId}`
|
|
@@ -3510,12 +3815,12 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3510
3815
|
}
|
|
3511
3816
|
);
|
|
3512
3817
|
cmd.addCommand(invitations);
|
|
3513
|
-
const allowlist = new
|
|
3818
|
+
const allowlist = new import_commander14.Command("allowlist").description(
|
|
3514
3819
|
"Manage a subnet allowlist (ADR-0004)"
|
|
3515
3820
|
);
|
|
3516
3821
|
allowlist.command("list <subnet_id>").description("Owner-only: list allowlist entries for a subnet.").option("--limit <n>", "Page size (1-500, default 100)", "100").option("--offset <n>", "Page offset (default 0)", "0").action(
|
|
3517
3822
|
async (subnetId, opts) => {
|
|
3518
|
-
|
|
3823
|
+
requireApiKey2();
|
|
3519
3824
|
const params = {};
|
|
3520
3825
|
if (opts.limit) params.limit = opts.limit;
|
|
3521
3826
|
if (opts.offset) params.offset = opts.offset;
|
|
@@ -3544,7 +3849,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3544
3849
|
"Owner-only: pre-authorise an agent on the subnet allowlist. 409 ALREADY_ON_ALLOWLIST on duplicate."
|
|
3545
3850
|
).requiredOption("--agent-id <aid>", "Agent ID to add").action(
|
|
3546
3851
|
async (subnetId, opts) => {
|
|
3547
|
-
|
|
3852
|
+
requireApiKey2();
|
|
3548
3853
|
try {
|
|
3549
3854
|
const res = await acnPost(
|
|
3550
3855
|
`/subnets/${subnetId}/allowlist`,
|
|
@@ -3563,7 +3868,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3563
3868
|
"Owner-only: remove an agent from the subnet allowlist. Idempotent (204 even if missing)."
|
|
3564
3869
|
).requiredOption("--agent-id <aid>", "Agent ID to remove").action(
|
|
3565
3870
|
async (subnetId, opts) => {
|
|
3566
|
-
|
|
3871
|
+
requireApiKey2();
|
|
3567
3872
|
try {
|
|
3568
3873
|
await acnDelete(
|
|
3569
3874
|
`/subnets/${subnetId}/allowlist/${opts.agentId}`
|
|
@@ -3578,7 +3883,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3578
3883
|
}
|
|
3579
3884
|
);
|
|
3580
3885
|
cmd.addCommand(allowlist);
|
|
3581
|
-
const harness = new
|
|
3886
|
+
const harness = new import_commander14.Command("harness").description("Manage Org Harness webhook for a subnet");
|
|
3582
3887
|
harness.command("set <subnet_id>").description("Register an Org Harness webhook on a subnet you own").requiredOption("--url <url>", "Harness webhook URL (HTTPS)").option("--secret <secret>", "HMAC-SHA256 signing secret (recommended)").action(async (subnetId, opts) => {
|
|
3583
3888
|
const config = loadConfig();
|
|
3584
3889
|
if (!config.api_key) {
|
|
@@ -3621,7 +3926,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
|
|
|
3621
3926
|
}
|
|
3622
3927
|
|
|
3623
3928
|
// src/commands/org.ts
|
|
3624
|
-
var
|
|
3929
|
+
var import_commander15 = require("commander");
|
|
3625
3930
|
function formatOrg(o) {
|
|
3626
3931
|
const lines = [
|
|
3627
3932
|
` ID : ${o.org_id}`,
|
|
@@ -3638,7 +3943,7 @@ function formatOrg(o) {
|
|
|
3638
3943
|
return lines.join("\n");
|
|
3639
3944
|
}
|
|
3640
3945
|
function orgCommand() {
|
|
3641
|
-
const cmd = new
|
|
3946
|
+
const cmd = new import_commander15.Command("org").description("Manage ACN organisations (Org Harness)");
|
|
3642
3947
|
cmd.command("create").description("Create an Org (binds/creates a subnet fence)").requiredOption("--name <name>", "Display name").option("--steward <agent_id>", "Steward agent (required for human JWT callers)").option("--subnet <slug>", "Bind existing subnet slug (must be owned by steward)").option("--join-policy <policy>", "open | approval", "open").option("--private", "Private subnet fence", false).option("--harness-url <url>", "Register Org Harness webhook on the fence subnet").option("--harness-secret <secret>", "HMAC secret for harness webhook").action(
|
|
3643
3948
|
async (opts) => {
|
|
3644
3949
|
try {
|
|
@@ -3906,7 +4211,7 @@ function orgCommand() {
|
|
|
3906
4211
|
}
|
|
3907
4212
|
|
|
3908
4213
|
// src/commands/follow.ts
|
|
3909
|
-
var
|
|
4214
|
+
var import_commander16 = require("commander");
|
|
3910
4215
|
function requireAgentId6() {
|
|
3911
4216
|
const config = loadConfig();
|
|
3912
4217
|
if (!config.api_key) {
|
|
@@ -3929,7 +4234,7 @@ function formatAgent2(a, i) {
|
|
|
3929
4234
|
].join("\n");
|
|
3930
4235
|
}
|
|
3931
4236
|
function followCommand() {
|
|
3932
|
-
const cmd = new
|
|
4237
|
+
const cmd = new import_commander16.Command("follow").description("Follow/unfollow agents and inspect follow graph");
|
|
3933
4238
|
cmd.command("add <target_id>").description("Follow another agent").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (targetId, opts) => {
|
|
3934
4239
|
const agentId = opts.agentId ?? requireAgentId6();
|
|
3935
4240
|
try {
|
|
@@ -4020,7 +4325,7 @@ function followCommand() {
|
|
|
4020
4325
|
}
|
|
4021
4326
|
|
|
4022
4327
|
// src/commands/wallet.ts
|
|
4023
|
-
var
|
|
4328
|
+
var import_commander17 = require("commander");
|
|
4024
4329
|
function requireAgentId7() {
|
|
4025
4330
|
const config = loadConfig();
|
|
4026
4331
|
if (!config.api_key) {
|
|
@@ -4063,7 +4368,7 @@ async function showWalletInfo(opts) {
|
|
|
4063
4368
|
}
|
|
4064
4369
|
}
|
|
4065
4370
|
function walletCommand() {
|
|
4066
|
-
const cmd = new
|
|
4371
|
+
const cmd = new import_commander17.Command("wallet").description("View and manage agent's wallet & payment info");
|
|
4067
4372
|
cmd.command("info", { isDefault: true }).description("Show wallet, payment methods, pricing, and ERC-8004 status").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(showWalletInfo);
|
|
4068
4373
|
cmd.command("set-capability").description("Declare which payment methods, networks, and wallets you accept").requiredOption(
|
|
4069
4374
|
"--methods <csv>",
|
|
@@ -4242,7 +4547,7 @@ function walletCommand() {
|
|
|
4242
4547
|
}
|
|
4243
4548
|
|
|
4244
4549
|
// src/commands/pay.ts
|
|
4245
|
-
var
|
|
4550
|
+
var import_commander18 = require("commander");
|
|
4246
4551
|
function requireAgentId8() {
|
|
4247
4552
|
const config = loadConfig();
|
|
4248
4553
|
if (!config.api_key) {
|
|
@@ -4256,8 +4561,8 @@ function requireAgentId8() {
|
|
|
4256
4561
|
return config.agent_id;
|
|
4257
4562
|
}
|
|
4258
4563
|
function payCommand() {
|
|
4259
|
-
const cmd = new
|
|
4260
|
-
const createCmd = new
|
|
4564
|
+
const cmd = new import_commander18.Command("pay").description("Manage payment tasks between agents");
|
|
4565
|
+
const createCmd = new import_commander18.Command("create").description("Create a payment task to another agent");
|
|
4261
4566
|
createCmd.requiredOption("--to <agent>", "Recipient agent ID").requiredOption("--amount <n>", "Payment amount (positive number)").requiredOption("--currency <c>", "Currency code, e.g. USD, USDC").requiredOption("--method <m>", "Payment method, e.g. usdc, eth, platform_credits").requiredOption("--network <n>", "Network, e.g. ethereum, base, solana").option("--description <text>", "Free-text description for the payment task").option("--metadata <json>", "Additional metadata as JSON object").action(
|
|
4262
4567
|
async (opts) => {
|
|
4263
4568
|
const fromAgent = requireAgentId8();
|
|
@@ -4303,7 +4608,7 @@ function payCommand() {
|
|
|
4303
4608
|
}
|
|
4304
4609
|
}
|
|
4305
4610
|
);
|
|
4306
|
-
const confirmCmd = new
|
|
4611
|
+
const confirmCmd = new import_commander18.Command("confirm").description(
|
|
4307
4612
|
"Confirm an external payment has been made (buyer only)"
|
|
4308
4613
|
);
|
|
4309
4614
|
confirmCmd.requiredOption("--task-id <id>", "Payment task ID to confirm").requiredOption(
|
|
@@ -4325,7 +4630,7 @@ function payCommand() {
|
|
|
4325
4630
|
handleError(err);
|
|
4326
4631
|
}
|
|
4327
4632
|
});
|
|
4328
|
-
const statusCmd = new
|
|
4633
|
+
const statusCmd = new import_commander18.Command("status").description(
|
|
4329
4634
|
"Show payment tasks for the authenticated agent"
|
|
4330
4635
|
);
|
|
4331
4636
|
statusCmd.option("--status <s>", "Filter by status (e.g. created, payment_confirmed)").option("--limit <n>", "Max results (default 50)", "50").action(async (opts) => {
|
|
@@ -4348,7 +4653,7 @@ function payCommand() {
|
|
|
4348
4653
|
|
|
4349
4654
|
// src/index.ts
|
|
4350
4655
|
var { version } = require_package();
|
|
4351
|
-
var program = new
|
|
4656
|
+
var program = new import_commander19.Command();
|
|
4352
4657
|
program.name("acn").description("ACN CLI \u2014 Agent Collaboration Network command-line interface").version(version).option("--json", "Output raw JSON (useful for agent parsing)").hook("preAction", (thisCommand) => {
|
|
4353
4658
|
const opts = thisCommand.opts();
|
|
4354
4659
|
if (opts.json) setJsonMode(true);
|
|
@@ -4360,6 +4665,7 @@ program.addCommand(rotateKeyCommand());
|
|
|
4360
4665
|
program.addCommand(agentsCommand());
|
|
4361
4666
|
program.addCommand(tasksCommand());
|
|
4362
4667
|
program.addCommand(messageCommand());
|
|
4668
|
+
program.addCommand(invokeCommand());
|
|
4363
4669
|
program.addCommand(notifyCommand());
|
|
4364
4670
|
program.addCommand(inboxCommand());
|
|
4365
4671
|
program.addCommand(listenCommand());
|