@acnlabs/acn-cli 1.0.8 → 1.0.9
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 +2 -1
- package/dist/index.js +121 -157
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,7 +112,8 @@ acn listen --runtime http \
|
|
|
112
112
|
|
|
113
113
|
`--chat-token` is deprecated/ignored. Task / Org wakes still use `--wake-url` /
|
|
114
114
|
`--wake-exec`. Chat envelopes skip wake and use the complete → writeback path
|
|
115
|
-
instead.
|
|
115
|
+
instead. CLI **1.0.9+** completes official hops via Host; `--chat-complete-*`
|
|
116
|
+
is BYO only and may be omitted for official-only.
|
|
116
117
|
|
|
117
118
|
**Coverage:** only traffic that arrives over the Mode B relay. Open Task Pool
|
|
118
119
|
rows that were never pushed as A2A still need `acn tasks list` / reconcile.
|
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.9",
|
|
35
35
|
description: "Official CLI for ACN (Agent Collaboration Network) \u2014 zero-integration agent access",
|
|
36
36
|
main: "dist/index.js",
|
|
37
37
|
bin: {
|
|
@@ -1756,112 +1756,11 @@ var DedupeStore = class {
|
|
|
1756
1756
|
|
|
1757
1757
|
// src/commands/official-hop-door.ts
|
|
1758
1758
|
var import_node_http = __toESM(require("http"));
|
|
1759
|
-
function
|
|
1759
|
+
function canCompleteOfficialHop(opts) {
|
|
1760
1760
|
return Boolean(
|
|
1761
1761
|
opts.inferencePath === "official" && opts.hopId?.trim() && asHostInferenceUrl(opts.hostInferenceUrl) && opts.jwt?.trim()
|
|
1762
1762
|
);
|
|
1763
1763
|
}
|
|
1764
|
-
function readBody(req) {
|
|
1765
|
-
return new Promise((resolve, reject) => {
|
|
1766
|
-
const chunks = [];
|
|
1767
|
-
req.on("data", (c) => chunks.push(Buffer.from(c)));
|
|
1768
|
-
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
1769
|
-
req.on("error", reject);
|
|
1770
|
-
});
|
|
1771
|
-
}
|
|
1772
|
-
function send(res, status, body, contentType = "application/json") {
|
|
1773
|
-
res.writeHead(status, {
|
|
1774
|
-
"content-type": contentType,
|
|
1775
|
-
"content-length": String(body.length)
|
|
1776
|
-
});
|
|
1777
|
-
res.end(body);
|
|
1778
|
-
}
|
|
1779
|
-
async function handleDoorRequest(req, res, opts) {
|
|
1780
|
-
const path = (req.url ?? "").split("?")[0];
|
|
1781
|
-
if (req.method !== "POST" || path !== "/v1/chat/completions" && path !== "/chat/completions") {
|
|
1782
|
-
send(res, 404, Buffer.from('{"error":"not_found"}'));
|
|
1783
|
-
return;
|
|
1784
|
-
}
|
|
1785
|
-
let payload;
|
|
1786
|
-
try {
|
|
1787
|
-
const raw = await readBody(req);
|
|
1788
|
-
payload = JSON.parse(raw.length ? raw.toString("utf-8") : "{}");
|
|
1789
|
-
} catch {
|
|
1790
|
-
send(res, 400, Buffer.from('{"error":"invalid_json"}'));
|
|
1791
|
-
return;
|
|
1792
|
-
}
|
|
1793
|
-
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
1794
|
-
send(res, 400, Buffer.from('{"error":"invalid_json"}'));
|
|
1795
|
-
return;
|
|
1796
|
-
}
|
|
1797
|
-
const body = { ...payload };
|
|
1798
|
-
delete body.agent_id;
|
|
1799
|
-
body.hop_id = opts.hopId;
|
|
1800
|
-
const headers = {
|
|
1801
|
-
authorization: `Bearer ${opts.jwt}`,
|
|
1802
|
-
"content-type": "application/json",
|
|
1803
|
-
"X-Hop-Id": opts.hopId
|
|
1804
|
-
};
|
|
1805
|
-
if (opts.agentId) headers["X-Agent-Id"] = opts.agentId;
|
|
1806
|
-
try {
|
|
1807
|
-
const upstream = await opts.fetchFn(opts.upstream, {
|
|
1808
|
-
method: "POST",
|
|
1809
|
-
headers,
|
|
1810
|
-
body: JSON.stringify(body)
|
|
1811
|
-
});
|
|
1812
|
-
const out = Buffer.from(await upstream.arrayBuffer());
|
|
1813
|
-
const ct = upstream.headers.get("content-type") || "application/json";
|
|
1814
|
-
send(res, upstream.status, out, ct);
|
|
1815
|
-
} catch (err) {
|
|
1816
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1817
|
-
send(
|
|
1818
|
-
res,
|
|
1819
|
-
502,
|
|
1820
|
-
Buffer.from(JSON.stringify({ error: `upstream_unreachable:${msg.slice(0, 120)}` }))
|
|
1821
|
-
);
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
function closeServer(server) {
|
|
1825
|
-
return new Promise((resolve) => {
|
|
1826
|
-
server.closeAllConnections?.();
|
|
1827
|
-
server.close(() => resolve());
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
async function startOfficialHopDoor(opts) {
|
|
1831
|
-
const dest = asHostInferenceUrl(opts.hostInferenceUrl);
|
|
1832
|
-
const hopId = opts.hopId.trim();
|
|
1833
|
-
const jwt = opts.jwt.trim();
|
|
1834
|
-
if (!dest || !hopId || !jwt) return null;
|
|
1835
|
-
const fetchFn = opts.fetchFn ?? fetch;
|
|
1836
|
-
const upstream = `${dest}/chat/completions`;
|
|
1837
|
-
const server = import_node_http.default.createServer((req, res) => {
|
|
1838
|
-
void handleDoorRequest(req, res, {
|
|
1839
|
-
upstream,
|
|
1840
|
-
hopId,
|
|
1841
|
-
agentId: opts.agentId,
|
|
1842
|
-
jwt,
|
|
1843
|
-
fetchFn
|
|
1844
|
-
});
|
|
1845
|
-
});
|
|
1846
|
-
try {
|
|
1847
|
-
await new Promise((resolve, reject) => {
|
|
1848
|
-
server.once("error", reject);
|
|
1849
|
-
server.listen(0, "127.0.0.1", () => resolve());
|
|
1850
|
-
});
|
|
1851
|
-
} catch {
|
|
1852
|
-
return null;
|
|
1853
|
-
}
|
|
1854
|
-
const addr = server.address();
|
|
1855
|
-
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
1856
|
-
if (!port) {
|
|
1857
|
-
await closeServer(server);
|
|
1858
|
-
return null;
|
|
1859
|
-
}
|
|
1860
|
-
return {
|
|
1861
|
-
baseUrl: `http://127.0.0.1:${port}/v1`,
|
|
1862
|
-
close: () => closeServer(server)
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
1764
|
|
|
1866
1765
|
// src/commands/chat-writeback.ts
|
|
1867
1766
|
var DEFAULT_COMPLETE_TIMEOUT_MS = 12e4;
|
|
@@ -1884,8 +1783,8 @@ function validateChatWritebackOptions(opts) {
|
|
|
1884
1783
|
}
|
|
1885
1784
|
const hasUrl = Boolean(opts.chatCompleteUrl?.trim());
|
|
1886
1785
|
const hasExec = Boolean(opts.chatCompleteExec?.trim());
|
|
1887
|
-
if (hasUrl
|
|
1888
|
-
return
|
|
1786
|
+
if (hasUrl && hasExec) {
|
|
1787
|
+
return "--chat-writeback: --chat-complete-url and --chat-complete-exec are mutually exclusive (omit both for official-only; BYO hops need exactly one).";
|
|
1889
1788
|
}
|
|
1890
1789
|
return null;
|
|
1891
1790
|
}
|
|
@@ -1919,6 +1818,28 @@ function extractContent(payload) {
|
|
|
1919
1818
|
}
|
|
1920
1819
|
return null;
|
|
1921
1820
|
}
|
|
1821
|
+
function extractChatCompletionContent(payload) {
|
|
1822
|
+
const rec = asRecord2(payload);
|
|
1823
|
+
if (!rec) return null;
|
|
1824
|
+
const choices = rec.choices;
|
|
1825
|
+
if (Array.isArray(choices) && choices.length > 0) {
|
|
1826
|
+
const first = asRecord2(choices[0]);
|
|
1827
|
+
if (first) {
|
|
1828
|
+
const msg = asRecord2(first.message);
|
|
1829
|
+
const fromMsg = msg?.content;
|
|
1830
|
+
if (typeof fromMsg === "string" && fromMsg.trim()) {
|
|
1831
|
+
const t = fromMsg.trim();
|
|
1832
|
+
if (t.toLowerCase() !== "accepted") return t;
|
|
1833
|
+
}
|
|
1834
|
+
const fromText = first.text;
|
|
1835
|
+
if (typeof fromText === "string" && fromText.trim()) {
|
|
1836
|
+
const t = fromText.trim();
|
|
1837
|
+
if (t.toLowerCase() !== "accepted") return t;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
return extractContent(payload);
|
|
1842
|
+
}
|
|
1922
1843
|
function asNonNegInt(v) {
|
|
1923
1844
|
if (typeof v === "number" && Number.isFinite(v) && v >= 0) {
|
|
1924
1845
|
return Math.floor(v);
|
|
@@ -2138,44 +2059,72 @@ function spawnCompleteExec(event, opts, deps, jwt, door) {
|
|
|
2138
2059
|
child.stdin?.end(body);
|
|
2139
2060
|
});
|
|
2140
2061
|
}
|
|
2141
|
-
async function
|
|
2142
|
-
|
|
2062
|
+
async function completeOfficialViaHost(event, opts, deps, jwt) {
|
|
2063
|
+
const chat = event.chat;
|
|
2064
|
+
if (!chat) return { ok: false, reason: "no_chat_envelope" };
|
|
2065
|
+
if (!canCompleteOfficialHop({
|
|
2066
|
+
inferencePath: chat.inference_path,
|
|
2067
|
+
hopId: chat.hop_id,
|
|
2068
|
+
hostInferenceUrl: chat.host_inference_url,
|
|
2069
|
+
jwt
|
|
2070
|
+
})) {
|
|
2071
|
+
return { ok: false, reason: "official_complete_skipped" };
|
|
2072
|
+
}
|
|
2073
|
+
const model = chat.requested_model?.trim();
|
|
2074
|
+
const text = chat.user_text?.trim();
|
|
2075
|
+
if (!model) return { ok: false, reason: "official_complete_missing_model" };
|
|
2076
|
+
if (!text) return { ok: false, reason: "official_complete_missing_text" };
|
|
2143
2077
|
const logFn = deps.logFn ?? ((line) => console.error(line));
|
|
2078
|
+
logFn(
|
|
2079
|
+
`[acn listen] official_complete chat_id=${chat.chat_id} model=${model}`
|
|
2080
|
+
);
|
|
2081
|
+
const fetchFn = deps.fetchFn ?? fetch;
|
|
2082
|
+
const timeoutMs = opts.completeTimeoutMs ?? DEFAULT_COMPLETE_TIMEOUT_MS;
|
|
2083
|
+
const controller = new AbortController();
|
|
2084
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2144
2085
|
try {
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
`[acn listen] official_door chat_id=${event.chat.chat_id} base=${door.baseUrl}`
|
|
2165
|
-
);
|
|
2166
|
-
} else {
|
|
2167
|
-
logFn(
|
|
2168
|
-
`[acn listen] official_door_skipped chat_id=${event.chat.chat_id}`
|
|
2169
|
-
);
|
|
2170
|
-
}
|
|
2086
|
+
const res = await fetchFn(`${chat.host_inference_url}/chat/completions`, {
|
|
2087
|
+
method: "POST",
|
|
2088
|
+
headers: {
|
|
2089
|
+
authorization: `Bearer ${jwt}`,
|
|
2090
|
+
"content-type": "application/json",
|
|
2091
|
+
"X-Hop-Id": chat.hop_id,
|
|
2092
|
+
"X-Agent-Id": opts.agentId
|
|
2093
|
+
},
|
|
2094
|
+
body: JSON.stringify({
|
|
2095
|
+
model,
|
|
2096
|
+
messages: [{ role: "user", content: text }],
|
|
2097
|
+
hop_id: chat.hop_id,
|
|
2098
|
+
...chat.max_output_tokens && chat.max_output_tokens > 0 ? { max_tokens: chat.max_output_tokens } : {}
|
|
2099
|
+
}),
|
|
2100
|
+
signal: controller.signal
|
|
2101
|
+
});
|
|
2102
|
+
const raw = await res.text();
|
|
2103
|
+
if (res.status < 200 || res.status >= 300) {
|
|
2104
|
+
return { ok: false, reason: `official_complete_http_${res.status}` };
|
|
2171
2105
|
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2106
|
+
let payload;
|
|
2107
|
+
try {
|
|
2108
|
+
payload = JSON.parse(raw);
|
|
2109
|
+
} catch {
|
|
2110
|
+
return { ok: false, reason: "official_complete_invalid_json" };
|
|
2111
|
+
}
|
|
2112
|
+
const content = extractChatCompletionContent(payload);
|
|
2113
|
+
if (!content) return { ok: false, reason: "official_complete_missing_content" };
|
|
2114
|
+
return { ok: true, result: { content } };
|
|
2115
|
+
} catch (err) {
|
|
2116
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2117
|
+
if (controller.signal.aborted || /abort|timeout/i.test(msg)) {
|
|
2118
|
+
return { ok: false, reason: "official_complete_timeout" };
|
|
2176
2119
|
}
|
|
2120
|
+
return { ok: false, reason: msg.slice(0, 200) };
|
|
2121
|
+
} finally {
|
|
2122
|
+
clearTimeout(timer);
|
|
2177
2123
|
}
|
|
2178
2124
|
}
|
|
2125
|
+
async function completeViaExec(event, opts, deps, jwt) {
|
|
2126
|
+
return spawnCompleteExec(event, opts, deps, jwt);
|
|
2127
|
+
}
|
|
2179
2128
|
async function postWriteback(event, complete, opts, deps) {
|
|
2180
2129
|
const chat = event.chat;
|
|
2181
2130
|
if (!chat) return { ok: false, reason: "no_chat_envelope" };
|
|
@@ -2295,11 +2244,26 @@ async function handleChatWriteback(event, opts, deps = {}) {
|
|
|
2295
2244
|
const logFn = deps.logFn ?? ((line) => console.error(line));
|
|
2296
2245
|
if (!event.chat) return { ok: false, reason: "no_chat_envelope" };
|
|
2297
2246
|
let jwt = null;
|
|
2298
|
-
if (event.chat.inference_path === "official"
|
|
2247
|
+
if (event.chat.inference_path === "official") {
|
|
2299
2248
|
const minted = await mintAgentJwt(opts, deps.fetchFn ?? fetch);
|
|
2300
|
-
if (minted.ok)
|
|
2249
|
+
if (!minted.ok) {
|
|
2250
|
+
logFn(
|
|
2251
|
+
`[acn listen] official_jwt_failed chat_id=${event.chat.chat_id} reason=${minted.reason}`
|
|
2252
|
+
);
|
|
2253
|
+
return { ok: false, reason: minted.reason };
|
|
2254
|
+
}
|
|
2255
|
+
jwt = minted.token;
|
|
2256
|
+
}
|
|
2257
|
+
let completed;
|
|
2258
|
+
if (event.chat.inference_path === "official") {
|
|
2259
|
+
completed = await completeOfficialViaHost(event, opts, deps, jwt);
|
|
2260
|
+
} else if (opts.completeUrl) {
|
|
2261
|
+
completed = await completeViaHttp(event, opts, deps);
|
|
2262
|
+
} else if (opts.completeExec) {
|
|
2263
|
+
completed = await completeViaExec(event, opts, deps, jwt);
|
|
2264
|
+
} else {
|
|
2265
|
+
completed = { ok: false, reason: "byo_complete_missing" };
|
|
2301
2266
|
}
|
|
2302
|
-
const completed = opts.completeUrl ? await completeViaHttp(event, opts, deps) : await completeViaExec(event, opts, deps, jwt);
|
|
2303
2267
|
if (!completed.ok) {
|
|
2304
2268
|
logFn(
|
|
2305
2269
|
`[acn listen] chat_complete_failed chat_id=${event.chat.chat_id} message_id=${event.message_id} reason=${completed.reason}`
|
|
@@ -2524,7 +2488,7 @@ function formatWakeFailed(event, reason) {
|
|
|
2524
2488
|
function formatDeduped(event) {
|
|
2525
2489
|
return `[acn listen] deduped key=${dedupeKey(event)}`;
|
|
2526
2490
|
}
|
|
2527
|
-
function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore,
|
|
2491
|
+
function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore, send, deps = {}) {
|
|
2528
2492
|
const logFn = deps.logFn ?? ((line) => console.error(line));
|
|
2529
2493
|
const result = processIncomingRequest(
|
|
2530
2494
|
correlationId,
|
|
@@ -2533,7 +2497,7 @@ function dispatchLocalReceiver(correlationId, bodyText, opts, dedupeStore, send2
|
|
|
2533
2497
|
dedupeStore,
|
|
2534
2498
|
deps
|
|
2535
2499
|
);
|
|
2536
|
-
|
|
2500
|
+
send(result.response);
|
|
2537
2501
|
if (result.dedupeHit && result.event) {
|
|
2538
2502
|
logFn(formatDeduped(result.event));
|
|
2539
2503
|
return;
|
|
@@ -2594,7 +2558,7 @@ function errorResponse(id, status, detail) {
|
|
|
2594
2558
|
body: JSON.stringify({ error: detail })
|
|
2595
2559
|
};
|
|
2596
2560
|
}
|
|
2597
|
-
async function dispatchA2aRequest(frame, opts,
|
|
2561
|
+
async function dispatchA2aRequest(frame, opts, send, deps = {}) {
|
|
2598
2562
|
const bodyBuf = decodeBody(frame);
|
|
2599
2563
|
try {
|
|
2600
2564
|
if (opts.runtime) {
|
|
@@ -2604,7 +2568,7 @@ async function dispatchA2aRequest(frame, opts, send2, deps = {}) {
|
|
|
2604
2568
|
bodyBuf.toString("utf-8"),
|
|
2605
2569
|
opts.runtime,
|
|
2606
2570
|
store,
|
|
2607
|
-
|
|
2571
|
+
send,
|
|
2608
2572
|
{
|
|
2609
2573
|
fetchFn: deps.fetchFn,
|
|
2610
2574
|
spawnFn: deps.spawnFn,
|
|
@@ -2614,17 +2578,17 @@ async function dispatchA2aRequest(frame, opts, send2, deps = {}) {
|
|
|
2614
2578
|
return;
|
|
2615
2579
|
}
|
|
2616
2580
|
if (opts.forward) {
|
|
2617
|
-
await forwardToHttp(frame, bodyBuf, opts.forward, deps.fetchFn ?? fetch,
|
|
2581
|
+
await forwardToHttp(frame, bodyBuf, opts.forward, deps.fetchFn ?? fetch, send);
|
|
2618
2582
|
return;
|
|
2619
2583
|
}
|
|
2620
2584
|
if (opts.exec) {
|
|
2621
|
-
|
|
2585
|
+
send(await runExec(frame, bodyBuf, opts.exec, deps.spawnFn ?? import_child_process3.spawn));
|
|
2622
2586
|
return;
|
|
2623
2587
|
}
|
|
2624
|
-
|
|
2588
|
+
send(errorResponse(frame.id, 500, "no handler configured"));
|
|
2625
2589
|
} catch (err) {
|
|
2626
2590
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2627
|
-
|
|
2591
|
+
send(errorResponse(frame.id, 502, `handler failed: ${msg}`));
|
|
2628
2592
|
}
|
|
2629
2593
|
}
|
|
2630
2594
|
function buildForwardHeaders(frame) {
|
|
@@ -2634,7 +2598,7 @@ function buildForwardHeaders(frame) {
|
|
|
2634
2598
|
}
|
|
2635
2599
|
return headers;
|
|
2636
2600
|
}
|
|
2637
|
-
async function forwardToHttp(frame, bodyBuf, base, fetchFn,
|
|
2601
|
+
async function forwardToHttp(frame, bodyBuf, base, fetchFn, send) {
|
|
2638
2602
|
const suffix = frame.path && frame.path !== "/" ? "/" + frame.path.replace(/^\//, "") : "";
|
|
2639
2603
|
const targetUrl = base.replace(/\/$/, "") + suffix;
|
|
2640
2604
|
const method = (frame.method ?? "POST").toUpperCase();
|
|
@@ -2652,7 +2616,7 @@ async function forwardToHttp(frame, bodyBuf, base, fetchFn, send2) {
|
|
|
2652
2616
|
const { done, value } = await reader.read();
|
|
2653
2617
|
if (done) break;
|
|
2654
2618
|
if (value && value.length > 0) {
|
|
2655
|
-
|
|
2619
|
+
send({
|
|
2656
2620
|
type: "a2a_stream_chunk",
|
|
2657
2621
|
id: frame.id,
|
|
2658
2622
|
seq: seq++,
|
|
@@ -2661,17 +2625,17 @@ async function forwardToHttp(frame, bodyBuf, base, fetchFn, send2) {
|
|
|
2661
2625
|
});
|
|
2662
2626
|
}
|
|
2663
2627
|
}
|
|
2664
|
-
|
|
2628
|
+
send({ type: "a2a_stream_end", id: frame.id, status: res.status });
|
|
2665
2629
|
} catch (err) {
|
|
2666
2630
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2667
|
-
|
|
2631
|
+
send({ type: "a2a_stream_end", id: frame.id, error: msg });
|
|
2668
2632
|
} finally {
|
|
2669
2633
|
reader.releaseLock();
|
|
2670
2634
|
}
|
|
2671
2635
|
return;
|
|
2672
2636
|
}
|
|
2673
2637
|
const respText = await res.text();
|
|
2674
|
-
|
|
2638
|
+
send({
|
|
2675
2639
|
type: "a2a_response",
|
|
2676
2640
|
id: frame.id,
|
|
2677
2641
|
status: res.status,
|
|
@@ -2782,12 +2746,12 @@ function runListener(cfg) {
|
|
|
2782
2746
|
if (!frame || typeof frame !== "object") return;
|
|
2783
2747
|
const f = frame;
|
|
2784
2748
|
if (f.type === "a2a_request" && typeof f.id === "string") {
|
|
2785
|
-
const
|
|
2749
|
+
const send = (out) => {
|
|
2786
2750
|
if (ws.readyState === import_ws.default.OPEN) {
|
|
2787
2751
|
ws.send(JSON.stringify(out));
|
|
2788
2752
|
}
|
|
2789
2753
|
};
|
|
2790
|
-
void dispatchA2aRequest(f, cfg,
|
|
2754
|
+
void dispatchA2aRequest(f, cfg, send, { dedupeStore });
|
|
2791
2755
|
}
|
|
2792
2756
|
});
|
|
2793
2757
|
ws.on("close", (code, reason) => {
|
|
@@ -2870,10 +2834,10 @@ function listenCommand() {
|
|
|
2870
2834
|
"Deprecated/ignored: writeback now mints ACN agent JWT from api-key"
|
|
2871
2835
|
).option(
|
|
2872
2836
|
"--chat-complete-url <url>",
|
|
2873
|
-
|
|
2837
|
+
"BYO complete URL (optional if official-only; mutually exclusive with --chat-complete-exec)"
|
|
2874
2838
|
).option(
|
|
2875
2839
|
"--chat-complete-exec <cmd>",
|
|
2876
|
-
'
|
|
2840
|
+
'BYO only (optional if official-only): stdin event \u2192 stdout {"content"}. Official hops complete via Host.'
|
|
2877
2841
|
).option(
|
|
2878
2842
|
"--chat-complete-timeout <ms>",
|
|
2879
2843
|
`Host complete timeout in ms (default ${DEFAULT_COMPLETE_TIMEOUT_MS})`,
|