@acnlabs/acn-cli 1.0.3 → 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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +341 -65
  3. 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.3",
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 import_commander18 = require("commander");
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/notify.ts
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 import_commander8.Command("notify").description(
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 import_commander9 = require("commander");
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 import_commander9.Command("inbox").description(
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 import_commander9.Command("mode").description(
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 import_commander9.Command("allowlist").description(
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 import_commander10 = require("commander");
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;
@@ -1823,6 +2024,34 @@ async function mintAgentJwt(opts, fetchFn = fetch) {
1823
2024
  function clearAgentJwtCache() {
1824
2025
  cachedJwt = null;
1825
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
+ }
1826
2055
  async function completeViaHttp(event, opts, deps) {
1827
2056
  const fetchFn = deps.fetchFn ?? fetch;
1828
2057
  const timeoutMs = opts.completeTimeoutMs ?? DEFAULT_COMPLETE_TIMEOUT_MS;
@@ -1831,7 +2060,7 @@ async function completeViaHttp(event, opts, deps) {
1831
2060
  try {
1832
2061
  const res = await fetchFn(opts.completeUrl, {
1833
2062
  method: "POST",
1834
- headers: { "content-type": "application/json" },
2063
+ headers: completeInferenceHeaders(event, opts),
1835
2064
  body: JSON.stringify(event),
1836
2065
  signal: controller.signal
1837
2066
  });
@@ -1856,13 +2085,16 @@ async function completeViaHttp(event, opts, deps) {
1856
2085
  clearTimeout(timer);
1857
2086
  }
1858
2087
  }
1859
- function completeViaExec(event, opts, deps) {
2088
+ function spawnCompleteExec(event, opts, deps, jwt, door) {
1860
2089
  const spawnFn = deps.spawnFn ?? import_child_process.spawn;
1861
2090
  const timeoutMs = opts.completeTimeoutMs ?? DEFAULT_COMPLETE_TIMEOUT_MS;
1862
2091
  const body = Buffer.from(JSON.stringify(event), "utf-8");
1863
2092
  return new Promise((resolve) => {
1864
2093
  let settled = false;
1865
- const child = spawnFn(opts.completeExec, { shell: true });
2094
+ const child = spawnFn(opts.completeExec, {
2095
+ shell: true,
2096
+ env: completeInferenceEnv(event, opts, jwt, door)
2097
+ });
1866
2098
  const stdout = [];
1867
2099
  const stderr = [];
1868
2100
  const finish = (result) => {
@@ -1901,6 +2133,44 @@ function completeViaExec(event, opts, deps) {
1901
2133
  child.stdin?.end(body);
1902
2134
  });
1903
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
+ }
1904
2174
  async function postWriteback(event, complete, opts, deps) {
1905
2175
  const chat = event.chat;
1906
2176
  if (!chat) return { ok: false, reason: "no_chat_envelope" };
@@ -2019,7 +2289,12 @@ async function postWriteback(event, complete, opts, deps) {
2019
2289
  async function handleChatWriteback(event, opts, deps = {}) {
2020
2290
  const logFn = deps.logFn ?? ((line) => console.error(line));
2021
2291
  if (!event.chat) return { ok: false, reason: "no_chat_envelope" };
2022
- const completed = opts.completeUrl ? await completeViaHttp(event, opts, deps) : await completeViaExec(event, opts, deps);
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);
2023
2298
  if (!completed.ok) {
2024
2299
  logFn(
2025
2300
  `[acn listen] chat_complete_failed chat_id=${event.chat.chat_id} message_id=${event.message_id} reason=${completed.reason}`
@@ -2244,7 +2519,7 @@ function formatWakeFailed(event, reason) {
2244
2519
  function formatDeduped(event) {
2245
2520
  return `[acn listen] deduped key=${dedupeKey(event)}`;
2246
2521
  }
2247
- function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore, send, deps = {}) {
2522
+ function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore, send2, deps = {}) {
2248
2523
  const logFn = deps.logFn ?? ((line) => console.error(line));
2249
2524
  const result = processIncomingRequest(
2250
2525
  correlationId,
@@ -2253,7 +2528,7 @@ function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore, send,
2253
2528
  dedupeStore,
2254
2529
  deps
2255
2530
  );
2256
- send(result.response);
2531
+ send2(result.response);
2257
2532
  if (result.dedupeHit && result.event) {
2258
2533
  logFn(formatDeduped(result.event));
2259
2534
  return;
@@ -2314,7 +2589,7 @@ function errorResponse(id, status, detail) {
2314
2589
  body: JSON.stringify({ error: detail })
2315
2590
  };
2316
2591
  }
2317
- async function dispatchA2aRequest(frame, opts, send, deps = {}) {
2592
+ async function dispatchA2aRequest(frame, opts, send2, deps = {}) {
2318
2593
  const bodyBuf = decodeBody(frame);
2319
2594
  try {
2320
2595
  if (opts.runtime) {
@@ -2324,7 +2599,7 @@ async function dispatchA2aRequest(frame, opts, send, deps = {}) {
2324
2599
  bodyBuf.toString("utf-8"),
2325
2600
  opts.runtime,
2326
2601
  store,
2327
- send,
2602
+ send2,
2328
2603
  {
2329
2604
  fetchFn: deps.fetchFn,
2330
2605
  spawnFn: deps.spawnFn,
@@ -2334,17 +2609,17 @@ async function dispatchA2aRequest(frame, opts, send, deps = {}) {
2334
2609
  return;
2335
2610
  }
2336
2611
  if (opts.forward) {
2337
- await forwardToHttp(frame, bodyBuf, opts.forward, deps.fetchFn ?? fetch, send);
2612
+ await forwardToHttp(frame, bodyBuf, opts.forward, deps.fetchFn ?? fetch, send2);
2338
2613
  return;
2339
2614
  }
2340
2615
  if (opts.exec) {
2341
- send(await runExec(frame, bodyBuf, opts.exec, deps.spawnFn ?? import_child_process3.spawn));
2616
+ send2(await runExec(frame, bodyBuf, opts.exec, deps.spawnFn ?? import_child_process3.spawn));
2342
2617
  return;
2343
2618
  }
2344
- send(errorResponse(frame.id, 500, "no handler configured"));
2619
+ send2(errorResponse(frame.id, 500, "no handler configured"));
2345
2620
  } catch (err) {
2346
2621
  const msg = err instanceof Error ? err.message : String(err);
2347
- send(errorResponse(frame.id, 502, `handler failed: ${msg}`));
2622
+ send2(errorResponse(frame.id, 502, `handler failed: ${msg}`));
2348
2623
  }
2349
2624
  }
2350
2625
  function buildForwardHeaders(frame) {
@@ -2354,7 +2629,7 @@ function buildForwardHeaders(frame) {
2354
2629
  }
2355
2630
  return headers;
2356
2631
  }
2357
- async function forwardToHttp(frame, bodyBuf, base, fetchFn, send) {
2632
+ async function forwardToHttp(frame, bodyBuf, base, fetchFn, send2) {
2358
2633
  const suffix = frame.path && frame.path !== "/" ? "/" + frame.path.replace(/^\//, "") : "";
2359
2634
  const targetUrl = base.replace(/\/$/, "") + suffix;
2360
2635
  const method = (frame.method ?? "POST").toUpperCase();
@@ -2372,7 +2647,7 @@ async function forwardToHttp(frame, bodyBuf, base, fetchFn, send) {
2372
2647
  const { done, value } = await reader.read();
2373
2648
  if (done) break;
2374
2649
  if (value && value.length > 0) {
2375
- send({
2650
+ send2({
2376
2651
  type: "a2a_stream_chunk",
2377
2652
  id: frame.id,
2378
2653
  seq: seq++,
@@ -2381,17 +2656,17 @@ async function forwardToHttp(frame, bodyBuf, base, fetchFn, send) {
2381
2656
  });
2382
2657
  }
2383
2658
  }
2384
- send({ type: "a2a_stream_end", id: frame.id, status: res.status });
2659
+ send2({ type: "a2a_stream_end", id: frame.id, status: res.status });
2385
2660
  } catch (err) {
2386
2661
  const msg = err instanceof Error ? err.message : String(err);
2387
- send({ type: "a2a_stream_end", id: frame.id, error: msg });
2662
+ send2({ type: "a2a_stream_end", id: frame.id, error: msg });
2388
2663
  } finally {
2389
2664
  reader.releaseLock();
2390
2665
  }
2391
2666
  return;
2392
2667
  }
2393
2668
  const respText = await res.text();
2394
- send({
2669
+ send2({
2395
2670
  type: "a2a_response",
2396
2671
  id: frame.id,
2397
2672
  status: res.status,
@@ -2502,12 +2777,12 @@ function runListener(cfg) {
2502
2777
  if (!frame || typeof frame !== "object") return;
2503
2778
  const f = frame;
2504
2779
  if (f.type === "a2a_request" && typeof f.id === "string") {
2505
- const send = (out) => {
2780
+ const send2 = (out) => {
2506
2781
  if (ws.readyState === import_ws.default.OPEN) {
2507
2782
  ws.send(JSON.stringify(out));
2508
2783
  }
2509
2784
  };
2510
- void dispatchA2aRequest(f, cfg, send, { dedupeStore });
2785
+ void dispatchA2aRequest(f, cfg, send2, { dedupeStore });
2511
2786
  }
2512
2787
  });
2513
2788
  ws.on("close", (code, reason) => {
@@ -2556,7 +2831,7 @@ function validateListenHandlerFlags(opts) {
2556
2831
  });
2557
2832
  }
2558
2833
  function listenCommand() {
2559
- const cmd = new import_commander10.Command("listen").description(
2834
+ const cmd = new import_commander11.Command("listen").description(
2560
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."
2561
2836
  ).option(
2562
2837
  "--runtime <id>",
@@ -2734,7 +3009,7 @@ function listenCommand() {
2734
3009
  }
2735
3010
 
2736
3011
  // src/commands/delivery.ts
2737
- var import_commander11 = require("commander");
3012
+ var import_commander12 = require("commander");
2738
3013
  var DELIVERY_DESC = {
2739
3014
  direct: "direct (Mode A) \u2014 ACN dials your public A2A endpoint over HTTP",
2740
3015
  relay: "relay (Mode B) \u2014 hold an outbound WebSocket with `acn listen`; no public URL",
@@ -2773,7 +3048,7 @@ function formatDelivery(d) {
2773
3048
  return lines.join("\n");
2774
3049
  }
2775
3050
  function deliveryCommand() {
2776
- const cmd = new import_commander11.Command("delivery").description(
3051
+ const cmd = new import_commander12.Command("delivery").description(
2777
3052
  "Inbound delivery transport (Mode A direct / Mode B relay). Orthogonal to reception policy (`acn inbox mode`)."
2778
3053
  );
2779
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) => {
@@ -2834,7 +3109,7 @@ function deliveryCommand() {
2834
3109
  }
2835
3110
 
2836
3111
  // src/commands/session.ts
2837
- var import_commander12 = require("commander");
3112
+ var import_commander13 = require("commander");
2838
3113
  function requireAgentId4() {
2839
3114
  const config = loadConfig();
2840
3115
  if (!config.api_key) {
@@ -2879,7 +3154,7 @@ function formatEntry2(s, index) {
2879
3154
  return lines.join("\n");
2880
3155
  }
2881
3156
  function sessionCommand() {
2882
- const cmd = new import_commander12.Command("session").description(
3157
+ const cmd = new import_commander13.Command("session").description(
2883
3158
  "Real-time session layer: bidirectional channel between two agents"
2884
3159
  );
2885
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(
@@ -2957,7 +3232,7 @@ ${formatEntry2(res)}`);
2957
3232
  }
2958
3233
 
2959
3234
  // src/commands/subnet.ts
2960
- var import_commander13 = require("commander");
3235
+ var import_commander14 = require("commander");
2961
3236
  function requireAgentId5() {
2962
3237
  const config = loadConfig();
2963
3238
  if (!config.api_key) {
@@ -2970,7 +3245,7 @@ function requireAgentId5() {
2970
3245
  }
2971
3246
  return config.agent_id;
2972
3247
  }
2973
- function requireApiKey() {
3248
+ function requireApiKey2() {
2974
3249
  const config = loadConfig();
2975
3250
  if (!config.api_key) {
2976
3251
  console.error("No API key found. Run `acn join` first or `acn config set api-key <key>`.");
@@ -3045,7 +3320,7 @@ function formatSubnet(s, index) {
3045
3320
  return lines.join("\n");
3046
3321
  }
3047
3322
  function subnetCommand() {
3048
- const cmd = new import_commander13.Command("subnet").description("Manage ACN subnets");
3323
+ const cmd = new import_commander14.Command("subnet").description("Manage ACN subnets");
3049
3324
  cmd.command("list").description(
3050
3325
  "List subnets. Without --all/--parent shows only subnets you have joined."
3051
3326
  ).option("--all", "Show all public subnets on ACN (not just your own)").option(
@@ -3262,7 +3537,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3262
3537
  handleError(err);
3263
3538
  }
3264
3539
  });
3265
- const requests = new import_commander13.Command("requests").description(
3540
+ const requests = new import_commander14.Command("requests").description(
3266
3541
  "Manage join-requests for a subnet (ADR-0004)"
3267
3542
  );
3268
3543
  requests.command("list <subnet_id>").description(
@@ -3276,7 +3551,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3276
3551
  "join_request"
3277
3552
  ).option("--limit <n>", "Page size (1-500, default 100)", "100").option("--offset <n>", "Page offset (default 0)", "0").action(
3278
3553
  async (subnetId, opts) => {
3279
- requireApiKey();
3554
+ requireApiKey2();
3280
3555
  const params = {};
3281
3556
  if (opts.status) params.status = opts.status;
3282
3557
  if (opts.kind) params.kind = opts.kind;
@@ -3342,7 +3617,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3342
3617
  });
3343
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(
3344
3619
  async (subnetId, opts) => {
3345
- requireApiKey();
3620
+ requireApiKey2();
3346
3621
  const body = {};
3347
3622
  if (opts.note !== void 0) body.note = opts.note;
3348
3623
  try {
@@ -3361,7 +3636,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3361
3636
  );
3362
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(
3363
3638
  async (subnetId, opts) => {
3364
- requireApiKey();
3639
+ requireApiKey2();
3365
3640
  const body = {};
3366
3641
  if (opts.note !== void 0) body.note = opts.note;
3367
3642
  try {
@@ -3382,7 +3657,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3382
3657
  "Applicant-only: withdraw your own pending join_request (CAS pending \u2192 withdrawn)."
3383
3658
  ).requiredOption("--request-id <rid>", "Your join request ID").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
3384
3659
  async (subnetId, opts) => {
3385
- requireApiKey();
3660
+ requireApiKey2();
3386
3661
  const body = {};
3387
3662
  if (opts.note !== void 0) body.note = opts.note;
3388
3663
  try {
@@ -3404,14 +3679,14 @@ ${JSON.stringify(res.agents, null, 2)}`);
3404
3679
  }
3405
3680
  );
3406
3681
  cmd.addCommand(requests);
3407
- const invitations = new import_commander13.Command("invitations").description(
3682
+ const invitations = new import_commander14.Command("invitations").description(
3408
3683
  "Manage invitations on a subnet (ADR-0004)"
3409
3684
  );
3410
3685
  invitations.command("send <subnet_id>").description(
3411
3686
  "Owner-only: invite an agent to a subnet. Auto-merges with a target's pending join_request (collapses to auto-approval)."
3412
3687
  ).requiredOption("--agent-id <aid>", "Agent ID to invite").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
3413
3688
  async (subnetId, opts) => {
3414
- requireApiKey();
3689
+ requireApiKey2();
3415
3690
  const body = { agent_id: opts.agentId };
3416
3691
  if (opts.note !== void 0) body.note = opts.note;
3417
3692
  try {
@@ -3430,7 +3705,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3430
3705
  "Filter by status: pending | approved | rejected | withdrawn"
3431
3706
  ).option("--limit <n>", "Page size (1-500, default 100)", "100").option("--offset <n>", "Page offset (default 0)", "0").action(
3432
3707
  async (subnetId, opts) => {
3433
- requireApiKey();
3708
+ requireApiKey2();
3434
3709
  const params = {};
3435
3710
  if (opts.status) params.status = opts.status;
3436
3711
  if (opts.limit) params.limit = opts.limit;
@@ -3483,7 +3758,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3483
3758
  "Invitee-only: accept a pending invitation (CAS pending \u2192 approved). Side effect: you join the subnet."
3484
3759
  ).requiredOption("--invitation-id <iid>", "Invitation ID to accept").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
3485
3760
  async (subnetId, opts) => {
3486
- requireApiKey();
3761
+ requireApiKey2();
3487
3762
  const body = {};
3488
3763
  if (opts.note !== void 0) body.note = opts.note;
3489
3764
  try {
@@ -3504,7 +3779,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3504
3779
  "Invitee-only: reject a pending invitation (CAS pending \u2192 rejected). No membership change."
3505
3780
  ).requiredOption("--invitation-id <iid>", "Invitation ID to reject").option("--note <text>", "Optional audit note (\u2264500 chars)").action(
3506
3781
  async (subnetId, opts) => {
3507
- requireApiKey();
3782
+ requireApiKey2();
3508
3783
  const body = {};
3509
3784
  if (opts.note !== void 0) body.note = opts.note;
3510
3785
  try {
@@ -3525,7 +3800,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3525
3800
  "Owner-only: cancel a pending invitation (CAS pending \u2192 withdrawn)."
3526
3801
  ).requiredOption("--invitation-id <iid>", "Invitation ID to cancel").action(
3527
3802
  async (subnetId, opts) => {
3528
- requireApiKey();
3803
+ requireApiKey2();
3529
3804
  try {
3530
3805
  const res = await acnDelete(
3531
3806
  `/subnets/${subnetId}/invitations/${opts.invitationId}`
@@ -3540,12 +3815,12 @@ ${JSON.stringify(res.agents, null, 2)}`);
3540
3815
  }
3541
3816
  );
3542
3817
  cmd.addCommand(invitations);
3543
- const allowlist = new import_commander13.Command("allowlist").description(
3818
+ const allowlist = new import_commander14.Command("allowlist").description(
3544
3819
  "Manage a subnet allowlist (ADR-0004)"
3545
3820
  );
3546
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(
3547
3822
  async (subnetId, opts) => {
3548
- requireApiKey();
3823
+ requireApiKey2();
3549
3824
  const params = {};
3550
3825
  if (opts.limit) params.limit = opts.limit;
3551
3826
  if (opts.offset) params.offset = opts.offset;
@@ -3574,7 +3849,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3574
3849
  "Owner-only: pre-authorise an agent on the subnet allowlist. 409 ALREADY_ON_ALLOWLIST on duplicate."
3575
3850
  ).requiredOption("--agent-id <aid>", "Agent ID to add").action(
3576
3851
  async (subnetId, opts) => {
3577
- requireApiKey();
3852
+ requireApiKey2();
3578
3853
  try {
3579
3854
  const res = await acnPost(
3580
3855
  `/subnets/${subnetId}/allowlist`,
@@ -3593,7 +3868,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3593
3868
  "Owner-only: remove an agent from the subnet allowlist. Idempotent (204 even if missing)."
3594
3869
  ).requiredOption("--agent-id <aid>", "Agent ID to remove").action(
3595
3870
  async (subnetId, opts) => {
3596
- requireApiKey();
3871
+ requireApiKey2();
3597
3872
  try {
3598
3873
  await acnDelete(
3599
3874
  `/subnets/${subnetId}/allowlist/${opts.agentId}`
@@ -3608,7 +3883,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3608
3883
  }
3609
3884
  );
3610
3885
  cmd.addCommand(allowlist);
3611
- const harness = new import_commander13.Command("harness").description("Manage Org Harness webhook for a subnet");
3886
+ const harness = new import_commander14.Command("harness").description("Manage Org Harness webhook for a subnet");
3612
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) => {
3613
3888
  const config = loadConfig();
3614
3889
  if (!config.api_key) {
@@ -3651,7 +3926,7 @@ ${JSON.stringify(res.agents, null, 2)}`);
3651
3926
  }
3652
3927
 
3653
3928
  // src/commands/org.ts
3654
- var import_commander14 = require("commander");
3929
+ var import_commander15 = require("commander");
3655
3930
  function formatOrg(o) {
3656
3931
  const lines = [
3657
3932
  ` ID : ${o.org_id}`,
@@ -3668,7 +3943,7 @@ function formatOrg(o) {
3668
3943
  return lines.join("\n");
3669
3944
  }
3670
3945
  function orgCommand() {
3671
- const cmd = new import_commander14.Command("org").description("Manage ACN organisations (Org Harness)");
3946
+ const cmd = new import_commander15.Command("org").description("Manage ACN organisations (Org Harness)");
3672
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(
3673
3948
  async (opts) => {
3674
3949
  try {
@@ -3936,7 +4211,7 @@ function orgCommand() {
3936
4211
  }
3937
4212
 
3938
4213
  // src/commands/follow.ts
3939
- var import_commander15 = require("commander");
4214
+ var import_commander16 = require("commander");
3940
4215
  function requireAgentId6() {
3941
4216
  const config = loadConfig();
3942
4217
  if (!config.api_key) {
@@ -3959,7 +4234,7 @@ function formatAgent2(a, i) {
3959
4234
  ].join("\n");
3960
4235
  }
3961
4236
  function followCommand() {
3962
- const cmd = new import_commander15.Command("follow").description("Follow/unfollow agents and inspect follow graph");
4237
+ const cmd = new import_commander16.Command("follow").description("Follow/unfollow agents and inspect follow graph");
3963
4238
  cmd.command("add <target_id>").description("Follow another agent").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (targetId, opts) => {
3964
4239
  const agentId = opts.agentId ?? requireAgentId6();
3965
4240
  try {
@@ -4050,7 +4325,7 @@ function followCommand() {
4050
4325
  }
4051
4326
 
4052
4327
  // src/commands/wallet.ts
4053
- var import_commander16 = require("commander");
4328
+ var import_commander17 = require("commander");
4054
4329
  function requireAgentId7() {
4055
4330
  const config = loadConfig();
4056
4331
  if (!config.api_key) {
@@ -4093,7 +4368,7 @@ async function showWalletInfo(opts) {
4093
4368
  }
4094
4369
  }
4095
4370
  function walletCommand() {
4096
- const cmd = new import_commander16.Command("wallet").description("View and manage agent's wallet & payment info");
4371
+ const cmd = new import_commander17.Command("wallet").description("View and manage agent's wallet & payment info");
4097
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);
4098
4373
  cmd.command("set-capability").description("Declare which payment methods, networks, and wallets you accept").requiredOption(
4099
4374
  "--methods <csv>",
@@ -4272,7 +4547,7 @@ function walletCommand() {
4272
4547
  }
4273
4548
 
4274
4549
  // src/commands/pay.ts
4275
- var import_commander17 = require("commander");
4550
+ var import_commander18 = require("commander");
4276
4551
  function requireAgentId8() {
4277
4552
  const config = loadConfig();
4278
4553
  if (!config.api_key) {
@@ -4286,8 +4561,8 @@ function requireAgentId8() {
4286
4561
  return config.agent_id;
4287
4562
  }
4288
4563
  function payCommand() {
4289
- const cmd = new import_commander17.Command("pay").description("Manage payment tasks between agents");
4290
- const createCmd = new import_commander17.Command("create").description("Create a payment task to another agent");
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");
4291
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(
4292
4567
  async (opts) => {
4293
4568
  const fromAgent = requireAgentId8();
@@ -4333,7 +4608,7 @@ function payCommand() {
4333
4608
  }
4334
4609
  }
4335
4610
  );
4336
- const confirmCmd = new import_commander17.Command("confirm").description(
4611
+ const confirmCmd = new import_commander18.Command("confirm").description(
4337
4612
  "Confirm an external payment has been made (buyer only)"
4338
4613
  );
4339
4614
  confirmCmd.requiredOption("--task-id <id>", "Payment task ID to confirm").requiredOption(
@@ -4355,7 +4630,7 @@ function payCommand() {
4355
4630
  handleError(err);
4356
4631
  }
4357
4632
  });
4358
- const statusCmd = new import_commander17.Command("status").description(
4633
+ const statusCmd = new import_commander18.Command("status").description(
4359
4634
  "Show payment tasks for the authenticated agent"
4360
4635
  );
4361
4636
  statusCmd.option("--status <s>", "Filter by status (e.g. created, payment_confirmed)").option("--limit <n>", "Max results (default 50)", "50").action(async (opts) => {
@@ -4378,7 +4653,7 @@ function payCommand() {
4378
4653
 
4379
4654
  // src/index.ts
4380
4655
  var { version } = require_package();
4381
- var program = new import_commander18.Command();
4656
+ var program = new import_commander19.Command();
4382
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) => {
4383
4658
  const opts = thisCommand.opts();
4384
4659
  if (opts.json) setJsonMode(true);
@@ -4390,6 +4665,7 @@ program.addCommand(rotateKeyCommand());
4390
4665
  program.addCommand(agentsCommand());
4391
4666
  program.addCommand(tasksCommand());
4392
4667
  program.addCommand(messageCommand());
4668
+ program.addCommand(invokeCommand());
4393
4669
  program.addCommand(notifyCommand());
4394
4670
  program.addCommand(inboxCommand());
4395
4671
  program.addCommand(listenCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acnlabs/acn-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.7",
4
4
  "description": "Official CLI for ACN (Agent Collaboration Network) \u2014 zero-integration agent access",
5
5
  "main": "dist/index.js",
6
6
  "bin": {