@pwrdrvr/agent-acp 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-64RRSPSB.js +51 -0
- package/dist/chunk-64RRSPSB.js.map +1 -0
- package/dist/chunk-GCWY5T5H.js +675 -0
- package/dist/chunk-GCWY5T5H.js.map +1 -0
- package/dist/chunk-OFMIS7W6.js +163 -0
- package/dist/chunk-OFMIS7W6.js.map +1 -0
- package/dist/discovery.d.ts +223 -0
- package/dist/discovery.js +32 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +28 -360
- package/dist/index.js +140 -881
- package/dist/index.js.map +1 -1
- package/dist/mcp-server-config.d.ts +40 -0
- package/dist/mcp-server-config.js +9 -0
- package/dist/mcp-server-config.js.map +1 -0
- package/dist/strategies.d.ts +20 -0
- package/dist/strategies.js +25 -0
- package/dist/strategies.js.map +1 -0
- package/dist/strategy-types-C7yw6yQL.d.ts +82 -0
- package/package.json +14 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ACP_REGISTRY_URL,
|
|
3
|
+
AcpAgentAllowlist,
|
|
4
|
+
AcpRegistryService,
|
|
5
|
+
BANNED_ACP_REGISTRY_IDS,
|
|
6
|
+
DEFAULT_ACP_AGENT_ALLOWLIST,
|
|
7
|
+
DEFAULT_ACP_PROBE_TIMEOUT_MS,
|
|
8
|
+
defaultAcpAgentAllowlist,
|
|
9
|
+
discoverLocalAcpAgentInstances,
|
|
10
|
+
discoverLocalAcpAgents,
|
|
11
|
+
evaluateAcpDistributionPolicy,
|
|
12
|
+
isBannedAcpRegistryId,
|
|
13
|
+
normalizeRegistry,
|
|
14
|
+
wellKnownAgentBinDirs
|
|
15
|
+
} from "./chunk-GCWY5T5H.js";
|
|
16
|
+
import {
|
|
17
|
+
BUILT_IN_ACP_STRATEGIES,
|
|
18
|
+
buildAcpBackendId,
|
|
19
|
+
buildStrategyTable,
|
|
20
|
+
defaultQuirks,
|
|
21
|
+
geminiStrategy,
|
|
22
|
+
grokStrategy,
|
|
23
|
+
kimiStrategy,
|
|
24
|
+
qwenStrategy,
|
|
25
|
+
strategyByBackendId,
|
|
26
|
+
strategyById
|
|
27
|
+
} from "./chunk-OFMIS7W6.js";
|
|
28
|
+
import {
|
|
29
|
+
normalizeAcpMcpServerConfig,
|
|
30
|
+
normalizeAcpMcpServerConfigs,
|
|
31
|
+
redactAcpMcpCredentials
|
|
32
|
+
} from "./chunk-64RRSPSB.js";
|
|
33
|
+
|
|
1
34
|
// src/acp-connection.ts
|
|
2
35
|
import { spawn } from "child_process";
|
|
3
36
|
import { Readable, Writable } from "stream";
|
|
@@ -265,9 +298,9 @@ function readFirstLocationPath(record) {
|
|
|
265
298
|
return void 0;
|
|
266
299
|
}
|
|
267
300
|
for (const location of locations) {
|
|
268
|
-
const
|
|
269
|
-
if (
|
|
270
|
-
return
|
|
301
|
+
const path = readString(asRecord(location), "path");
|
|
302
|
+
if (path && path.trim()) {
|
|
303
|
+
return path;
|
|
271
304
|
}
|
|
272
305
|
}
|
|
273
306
|
return void 0;
|
|
@@ -303,7 +336,7 @@ function normalizeStatus(status) {
|
|
|
303
336
|
return void 0;
|
|
304
337
|
}
|
|
305
338
|
}
|
|
306
|
-
function toolKindFor(acpKind, command,
|
|
339
|
+
function toolKindFor(acpKind, command, path, label) {
|
|
307
340
|
switch (acpKind) {
|
|
308
341
|
case "edit":
|
|
309
342
|
case "write":
|
|
@@ -324,18 +357,18 @@ function toolKindFor(acpKind, command, path5, label) {
|
|
|
324
357
|
if (command) return "command";
|
|
325
358
|
const inferred = inferToolKind(label);
|
|
326
359
|
if (inferred !== "other") return inferred;
|
|
327
|
-
return
|
|
360
|
+
return path ? "read" : "other";
|
|
328
361
|
}
|
|
329
362
|
function toolCallFromUpdate(update, kind, sessionId) {
|
|
330
363
|
const id = readToolCallId(update, kind, sessionId);
|
|
331
364
|
const label = readString(update, "title") ?? readString(update, "name") ?? readString(update, "kind") ?? kind.replaceAll("_", " ");
|
|
332
365
|
const acpKind = readString(update, "kind");
|
|
333
|
-
const
|
|
366
|
+
const path = readString(update, "path") ?? readFirstLocationPath(update);
|
|
334
367
|
const command = readString(update, "command");
|
|
335
368
|
const output = readToolOutput(update);
|
|
336
369
|
const exitCode = readNumber(update, "exitCode") ?? readNumber(update, "exit_code");
|
|
337
370
|
const status = normalizeStatus(readString(update, "status"));
|
|
338
|
-
const toolKind = toolKindFor(acpKind, command,
|
|
371
|
+
const toolKind = toolKindFor(acpKind, command, path, label);
|
|
339
372
|
const call = {
|
|
340
373
|
id,
|
|
341
374
|
name: readString(update, "name") ?? acpKind ?? label,
|
|
@@ -369,9 +402,9 @@ function readToolLocations(update) {
|
|
|
369
402
|
const out = [];
|
|
370
403
|
for (const item of raw) {
|
|
371
404
|
const record = asRecord(item);
|
|
372
|
-
const
|
|
373
|
-
if (
|
|
374
|
-
const location = { path
|
|
405
|
+
const path = record ? readString(record, "path") : void 0;
|
|
406
|
+
if (path === void 0) continue;
|
|
407
|
+
const location = { path };
|
|
375
408
|
const line = record ? readNumber(record, "line") : void 0;
|
|
376
409
|
if (line !== void 0) location.line = line;
|
|
377
410
|
out.push(location);
|
|
@@ -702,9 +735,10 @@ function acpRuntimeSupportsSseMcp(capabilities) {
|
|
|
702
735
|
return capabilities?.agentCapabilities?.mcp?.sse === true;
|
|
703
736
|
}
|
|
704
737
|
function modelConfigOption(capabilities) {
|
|
705
|
-
return capabilities?.configOptions?.find(
|
|
706
|
-
|
|
707
|
-
|
|
738
|
+
return capabilities?.configOptions?.find(isModelConfigOption);
|
|
739
|
+
}
|
|
740
|
+
function isModelConfigOption(option) {
|
|
741
|
+
return option.category === "model" || option.id === "model";
|
|
708
742
|
}
|
|
709
743
|
function modelIdFromCapabilities(capabilities) {
|
|
710
744
|
return capabilities?.models?.currentModelId ?? modelConfigOption(capabilities)?.currentValue;
|
|
@@ -751,6 +785,8 @@ function acpSessionRuntimeStateFromUpdate(update, now) {
|
|
|
751
785
|
return currentModeId ? { currentModeId, updatedAt: now } : void 0;
|
|
752
786
|
}
|
|
753
787
|
if (kind === "config_option_update") {
|
|
788
|
+
const fullSet = configOptionUpdateSet(update);
|
|
789
|
+
if (fullSet !== void 0) return runtimeStateFromConfigOptionSet(fullSet, now);
|
|
754
790
|
const configOption = asRecord(update.configOption ?? update.config_option) ?? update;
|
|
755
791
|
const id = readString(configOption, "id") ?? readString(configOption, "configOptionId") ?? readString(configOption, "configId");
|
|
756
792
|
const value = readString(configOption, "currentValue") ?? readString(configOption, "value");
|
|
@@ -768,6 +804,24 @@ function mergeAcpRuntimeState(existing, update) {
|
|
|
768
804
|
}
|
|
769
805
|
};
|
|
770
806
|
}
|
|
807
|
+
function configOptionUpdateSet(update) {
|
|
808
|
+
if (readKind(update) !== "config_option_update") return void 0;
|
|
809
|
+
const set = update.configOptions ?? update.config_options;
|
|
810
|
+
return Array.isArray(set) ? set : void 0;
|
|
811
|
+
}
|
|
812
|
+
function runtimeStateFromConfigOptionSet(set, now) {
|
|
813
|
+
const options = readConfigOptions(set);
|
|
814
|
+
const configValues = Object.fromEntries(
|
|
815
|
+
options.flatMap(
|
|
816
|
+
(option) => option.currentValue !== void 0 ? [[option.id, option.currentValue]] : []
|
|
817
|
+
)
|
|
818
|
+
);
|
|
819
|
+
if (Object.keys(configValues).length === 0) return void 0;
|
|
820
|
+
const state = { configValues, updatedAt: now };
|
|
821
|
+
const model = options.find(isModelConfigOption)?.currentValue;
|
|
822
|
+
if (model !== void 0) state.currentModelId = model;
|
|
823
|
+
return state;
|
|
824
|
+
}
|
|
771
825
|
function modeLabelFor(capabilities, modeId) {
|
|
772
826
|
return capabilities?.modes?.availableModes.find((mode) => mode.id === modeId)?.label;
|
|
773
827
|
}
|
|
@@ -929,51 +983,6 @@ function readAgentInfo(value) {
|
|
|
929
983
|
return Object.keys(agentInfo).length > 0 ? agentInfo : void 0;
|
|
930
984
|
}
|
|
931
985
|
|
|
932
|
-
// src/mcp-server-config.ts
|
|
933
|
-
function isKeyValueArray(collection) {
|
|
934
|
-
return Array.isArray(collection);
|
|
935
|
-
}
|
|
936
|
-
function normalizeKeyValues(collection) {
|
|
937
|
-
if (collection === void 0) return [];
|
|
938
|
-
if (isKeyValueArray(collection)) {
|
|
939
|
-
return collection.map(({ name, value }) => ({ name, value }));
|
|
940
|
-
}
|
|
941
|
-
return Object.entries(collection).map(([name, value]) => ({ name, value }));
|
|
942
|
-
}
|
|
943
|
-
function normalizeAcpMcpServerConfig(server) {
|
|
944
|
-
if ("type" in server) {
|
|
945
|
-
return {
|
|
946
|
-
name: server.name,
|
|
947
|
-
type: server.type,
|
|
948
|
-
url: server.url,
|
|
949
|
-
headers: normalizeKeyValues(server.headers)
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
return {
|
|
953
|
-
name: server.name,
|
|
954
|
-
command: server.command,
|
|
955
|
-
args: [...server.args ?? []],
|
|
956
|
-
env: normalizeKeyValues(server.env)
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
|
-
function normalizeAcpMcpServerConfigs(servers) {
|
|
960
|
-
return servers.map(normalizeAcpMcpServerConfig);
|
|
961
|
-
}
|
|
962
|
-
function redactAcpMcpCredentials(message, servers) {
|
|
963
|
-
const secrets = /* @__PURE__ */ new Set();
|
|
964
|
-
for (const server of servers) {
|
|
965
|
-
const values = "type" in server ? [server.url, ...normalizeKeyValues(server.headers).map(({ value }) => value)] : normalizeKeyValues(server.env).map(({ value }) => value);
|
|
966
|
-
for (const value of values) {
|
|
967
|
-
if (value.length > 0) secrets.add(value);
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
let redacted = message;
|
|
971
|
-
for (const secret of [...secrets].sort((a, b) => b.length - a.length)) {
|
|
972
|
-
redacted = redacted.split(secret).join("[REDACTED]");
|
|
973
|
-
}
|
|
974
|
-
return redacted;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
986
|
// src/acp-client.ts
|
|
978
987
|
var ACP_PROTOCOL_VERSION = 1;
|
|
979
988
|
var ACP_PROMPT_REQUEST_TIMEOUT_MS = 60 * 6e4;
|
|
@@ -1006,6 +1015,11 @@ var AcpAgentClient = class {
|
|
|
1006
1015
|
unsubscribeRequest = void 0;
|
|
1007
1016
|
initialized = false;
|
|
1008
1017
|
runtimeCapabilities;
|
|
1018
|
+
/** What `initialize` reported: agent-level, and never any one session's. The
|
|
1019
|
+
* base a session's own menus are built on — the client-wide snapshot above is
|
|
1020
|
+
* whichever session wrote last, and a fresh session must not inherit its
|
|
1021
|
+
* menus or their current values. */
|
|
1022
|
+
initializeCapabilities;
|
|
1009
1023
|
constructor(options) {
|
|
1010
1024
|
this.transport = options.transport;
|
|
1011
1025
|
this.strategy = options.strategy;
|
|
@@ -1219,6 +1233,7 @@ var AcpAgentClient = class {
|
|
|
1219
1233
|
normalizer: new AcpSessionNormalizer({ quirks: this.strategy.quirks }),
|
|
1220
1234
|
turnId: void 0,
|
|
1221
1235
|
runtimeState: void 0,
|
|
1236
|
+
runtimeCapabilities: void 0,
|
|
1222
1237
|
pendingTurn: void 0,
|
|
1223
1238
|
pendingInstructions: options.instructions,
|
|
1224
1239
|
mcpServerNames: configuredMcpServers.map((server) => server.name)
|
|
@@ -1260,6 +1275,7 @@ var AcpAgentClient = class {
|
|
|
1260
1275
|
normalizer: new AcpSessionNormalizer({ quirks: this.strategy.quirks }),
|
|
1261
1276
|
turnId: void 0,
|
|
1262
1277
|
runtimeState: void 0,
|
|
1278
|
+
runtimeCapabilities: void 0,
|
|
1263
1279
|
pendingTurn: void 0,
|
|
1264
1280
|
pendingInstructions: options.instructions,
|
|
1265
1281
|
// Remember ONLY the MCP servers attached to THIS session so the host's
|
|
@@ -1272,9 +1288,11 @@ var AcpAgentClient = class {
|
|
|
1272
1288
|
}
|
|
1273
1289
|
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1274
1290
|
options.lifecycle === "load" ? "session-load" : "session-new",
|
|
1275
|
-
result
|
|
1291
|
+
result,
|
|
1292
|
+
this.sessionBase(session)
|
|
1276
1293
|
);
|
|
1277
1294
|
if (runtimeCapabilities) {
|
|
1295
|
+
session.runtimeCapabilities = runtimeCapabilities;
|
|
1278
1296
|
const runtimeState = acpSessionRuntimeStateFromCapabilities(
|
|
1279
1297
|
runtimeCapabilities,
|
|
1280
1298
|
this.now()
|
|
@@ -1459,11 +1477,12 @@ var AcpAgentClient = class {
|
|
|
1459
1477
|
* `session/set_model` answers `{}` and refreshes only via an async
|
|
1460
1478
|
* notification. On Kimi the model DETERMINES which thought levels exist —
|
|
1461
1479
|
* `K2.7 Coding` offers only `on`, `K3` offers `low`/`high`/`max` — and
|
|
1462
|
-
* `applyReasoning` picks from
|
|
1480
|
+
* `applyReasoning` picks from THIS session's menus on the following
|
|
1463
1481
|
* `startTurn`, so the refresh this write leaves behind is what it reads. */
|
|
1464
1482
|
async setModel(threadId, modelId) {
|
|
1465
|
-
const
|
|
1466
|
-
const
|
|
1483
|
+
const capabilities = this.sessionCapabilities(threadId);
|
|
1484
|
+
const advertisesModelsCapability = (capabilities?.models?.availableModels?.length ?? 0) > 0;
|
|
1485
|
+
const option = modelConfigOption(capabilities);
|
|
1467
1486
|
if (!advertisesModelsCapability && option !== void 0) {
|
|
1468
1487
|
await this.setRuntimeOption(threadId, "modelConfigOption", option.id, modelId);
|
|
1469
1488
|
return;
|
|
@@ -1516,8 +1535,13 @@ var AcpAgentClient = class {
|
|
|
1516
1535
|
},
|
|
1517
1536
|
ACP_REQUEST_TIMEOUT_MS
|
|
1518
1537
|
);
|
|
1519
|
-
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1538
|
+
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1539
|
+
"initialize",
|
|
1540
|
+
result,
|
|
1541
|
+
this.initializeCapabilities
|
|
1542
|
+
);
|
|
1520
1543
|
if (runtimeCapabilities) {
|
|
1544
|
+
this.initializeCapabilities = runtimeCapabilities;
|
|
1521
1545
|
this.notifyRuntimeCapabilities({ runtimeCapabilities });
|
|
1522
1546
|
}
|
|
1523
1547
|
this.initialized = true;
|
|
@@ -1543,17 +1567,28 @@ var AcpAgentClient = class {
|
|
|
1543
1567
|
this.logger.debug("acp session/update for unknown session", { protocolSessionId });
|
|
1544
1568
|
return;
|
|
1545
1569
|
}
|
|
1570
|
+
if (configOptionUpdateSet(update) !== void 0) {
|
|
1571
|
+
const refreshed = this.captureRuntimeCapabilities(
|
|
1572
|
+
// A live refresh of this session's menus, not a load: keep the source
|
|
1573
|
+
// they were opened with.
|
|
1574
|
+
session.runtimeCapabilities?.source ?? "session-new",
|
|
1575
|
+
update,
|
|
1576
|
+
this.sessionBase(session)
|
|
1577
|
+
);
|
|
1578
|
+
if (refreshed) session.runtimeCapabilities = refreshed;
|
|
1579
|
+
}
|
|
1546
1580
|
const runtimeState = acpSessionRuntimeStateFromUpdate(update, this.now());
|
|
1547
1581
|
if (runtimeState) {
|
|
1548
1582
|
session.runtimeState = mergeAcpRuntimeState(session.runtimeState, runtimeState);
|
|
1549
|
-
|
|
1583
|
+
const capabilities = this.sessionCapabilities(session.threadId);
|
|
1584
|
+
if (capabilities) {
|
|
1550
1585
|
this.notifyRuntimeCapabilities({
|
|
1551
1586
|
threadId: session.threadId,
|
|
1552
|
-
runtimeCapabilities:
|
|
1587
|
+
runtimeCapabilities: capabilities,
|
|
1553
1588
|
runtimeState: session.runtimeState
|
|
1554
1589
|
});
|
|
1555
1590
|
}
|
|
1556
|
-
this.emitThreadSettings(session,
|
|
1591
|
+
this.emitThreadSettings(session, capabilities, session.runtimeState);
|
|
1557
1592
|
return;
|
|
1558
1593
|
}
|
|
1559
1594
|
const updateKind = readString2(update, "sessionUpdate");
|
|
@@ -1623,7 +1658,12 @@ var AcpAgentClient = class {
|
|
|
1623
1658
|
optionId,
|
|
1624
1659
|
value
|
|
1625
1660
|
);
|
|
1626
|
-
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1661
|
+
const runtimeCapabilities = this.captureRuntimeCapabilities(
|
|
1662
|
+
"session-load",
|
|
1663
|
+
result,
|
|
1664
|
+
this.sessionBase(session)
|
|
1665
|
+
);
|
|
1666
|
+
if (runtimeCapabilities) session.runtimeCapabilities = runtimeCapabilities;
|
|
1627
1667
|
const requested = source === "configOption" ? { configValues: { [optionId]: value }, updatedAt: this.now() } : source === "mode" ? { currentModeId: value, updatedAt: this.now() } : source === "modelConfigOption" ? (
|
|
1628
1668
|
// Record BOTH: it IS the config option's value, and it IS the
|
|
1629
1669
|
// session's model. A consumer reading `currentModelId` must not
|
|
@@ -1635,7 +1675,13 @@ var AcpAgentClient = class {
|
|
|
1635
1675
|
}
|
|
1636
1676
|
) : { currentModelId: value, updatedAt: this.now() };
|
|
1637
1677
|
session.runtimeState = mergeAcpRuntimeState(session.runtimeState, requested);
|
|
1638
|
-
const
|
|
1678
|
+
const reply = asRecord2(result);
|
|
1679
|
+
const reported = runtimeStateFromConfigOptionSet(
|
|
1680
|
+
reply?.configOptions ?? reply?.config_options,
|
|
1681
|
+
this.now()
|
|
1682
|
+
);
|
|
1683
|
+
if (reported) session.runtimeState = mergeAcpRuntimeState(session.runtimeState, reported);
|
|
1684
|
+
const effectiveCapabilities = this.sessionCapabilities(threadId);
|
|
1639
1685
|
if (effectiveCapabilities) {
|
|
1640
1686
|
this.notifyRuntimeCapabilities({
|
|
1641
1687
|
threadId,
|
|
@@ -1676,7 +1722,8 @@ var AcpAgentClient = class {
|
|
|
1676
1722
|
* If nothing matches we leave it alone — the caller's `.catch` debug-logs. */
|
|
1677
1723
|
async applyReasoning(threadId, reasoning) {
|
|
1678
1724
|
const target = reasoning.toLowerCase();
|
|
1679
|
-
const
|
|
1725
|
+
const capabilities = this.sessionCapabilities(threadId);
|
|
1726
|
+
const modes = capabilities?.modes?.availableModes ?? [];
|
|
1680
1727
|
const modeMatch = modes.find(
|
|
1681
1728
|
(mode) => mode.id.toLowerCase() === target || typeof mode.label === "string" && mode.label.toLowerCase() === target
|
|
1682
1729
|
);
|
|
@@ -1684,7 +1731,7 @@ var AcpAgentClient = class {
|
|
|
1684
1731
|
await this.setMode(threadId, modeMatch.id);
|
|
1685
1732
|
return;
|
|
1686
1733
|
}
|
|
1687
|
-
const thinking =
|
|
1734
|
+
const thinking = capabilities?.configOptions?.find(
|
|
1688
1735
|
(option) => option.category === "thought_level" || option.id === "thinking"
|
|
1689
1736
|
);
|
|
1690
1737
|
if (thinking !== void 0) {
|
|
@@ -1714,18 +1761,32 @@ var AcpAgentClient = class {
|
|
|
1714
1761
|
data: data.toString("base64")
|
|
1715
1762
|
};
|
|
1716
1763
|
}
|
|
1717
|
-
|
|
1764
|
+
/** Normalize `result` into a capability snapshot, filling what it omits from
|
|
1765
|
+
* `base`. Required, not defaulted: an explicit `undefined` would trigger a
|
|
1766
|
+
* default parameter, and the only default on hand is the client-wide
|
|
1767
|
+
* snapshot — which may be another session's. */
|
|
1768
|
+
captureRuntimeCapabilities(source, result, base) {
|
|
1718
1769
|
const runtimeCapabilities = normalizeAcpRuntimeCapabilities({
|
|
1719
1770
|
value: result,
|
|
1720
1771
|
now: this.now(),
|
|
1721
1772
|
source,
|
|
1722
|
-
...
|
|
1773
|
+
...base !== void 0 ? { initialize: base } : {}
|
|
1723
1774
|
});
|
|
1724
1775
|
if (runtimeCapabilities) {
|
|
1725
1776
|
this.runtimeCapabilities = runtimeCapabilities;
|
|
1726
1777
|
}
|
|
1727
1778
|
return runtimeCapabilities;
|
|
1728
1779
|
}
|
|
1780
|
+
/** The option menus to consult when choosing a value for `threadId`. A
|
|
1781
|
+
* session with none of its own gets the agent-level `initialize` menus, never
|
|
1782
|
+
* another session's. */
|
|
1783
|
+
sessionCapabilities(threadId) {
|
|
1784
|
+
return this.sessionBase(this.sessions.get(threadId));
|
|
1785
|
+
}
|
|
1786
|
+
/** What a session's menus are built on: its own, or `initialize`'s. */
|
|
1787
|
+
sessionBase(session) {
|
|
1788
|
+
return session?.runtimeCapabilities ?? this.initializeCapabilities;
|
|
1789
|
+
}
|
|
1729
1790
|
notifyRuntimeCapabilities(event) {
|
|
1730
1791
|
this.runtimeCapabilitiesHandler?.({
|
|
1731
1792
|
...event.threadId !== void 0 ? { threadId: event.threadId } : {},
|
|
@@ -1803,6 +1864,7 @@ function thoughtLevelPolarity(value) {
|
|
|
1803
1864
|
const haystack = `${value.value} ${value.label ?? ""}`.toLowerCase();
|
|
1804
1865
|
if (/\b(off|none|disabled?|no)\b/.test(haystack)) return "off";
|
|
1805
1866
|
if (/\b(on|enabled?|high|full|yes)\b/.test(haystack)) return "on";
|
|
1867
|
+
if (/\b(low|minimal|min|minimum)\b/.test(haystack)) return "low";
|
|
1806
1868
|
return "other";
|
|
1807
1869
|
}
|
|
1808
1870
|
function reasoningValueForThoughtLevel(reasoning, values) {
|
|
@@ -1810,8 +1872,9 @@ function reasoningValueForThoughtLevel(reasoning, values) {
|
|
|
1810
1872
|
const wantOff = LOW_EFFORT_TOKENS.has(token);
|
|
1811
1873
|
const wantOn = HIGH_EFFORT_TOKENS.has(token);
|
|
1812
1874
|
if (!wantOff && !wantOn) return void 0;
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1875
|
+
const withPolarity = (polarity) => values.find((value) => thoughtLevelPolarity(value) === polarity);
|
|
1876
|
+
if (wantOn) return withPolarity("on")?.value;
|
|
1877
|
+
return (withPolarity("off") ?? withPolarity("low"))?.value;
|
|
1815
1878
|
}
|
|
1816
1879
|
function buildApprovalRequest(args) {
|
|
1817
1880
|
const toolCall = asRecord2(args.params.toolCall) ?? {};
|
|
@@ -2003,10 +2066,10 @@ function describeUsageShape(result) {
|
|
|
2003
2066
|
const paths = [];
|
|
2004
2067
|
const walk = (record, prefix, depth) => {
|
|
2005
2068
|
for (const [key, value] of Object.entries(record)) {
|
|
2006
|
-
const
|
|
2007
|
-
if (/token|usage|quota/i.test(key)) paths.push(
|
|
2069
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
2070
|
+
if (/token|usage|quota/i.test(key)) paths.push(path);
|
|
2008
2071
|
const child = asRecord2(value);
|
|
2009
|
-
if (child && depth < 3) walk(child,
|
|
2072
|
+
if (child && depth < 3) walk(child, path, depth + 1);
|
|
2010
2073
|
}
|
|
2011
2074
|
};
|
|
2012
2075
|
walk(root, "", 0);
|
|
@@ -2218,810 +2281,6 @@ var AcpOneShotClient = class {
|
|
|
2218
2281
|
await this.client.close();
|
|
2219
2282
|
}
|
|
2220
2283
|
};
|
|
2221
|
-
|
|
2222
|
-
// src/strategies/strategy-types.ts
|
|
2223
|
-
function buildAcpBackendId(registryId) {
|
|
2224
|
-
return `acp:${registryId}`;
|
|
2225
|
-
}
|
|
2226
|
-
var DEFAULT_QUIRKS = {
|
|
2227
|
-
surfaceThoughts: true,
|
|
2228
|
-
titleFrom: "topic-update"
|
|
2229
|
-
};
|
|
2230
|
-
function defaultQuirks(overrides = {}) {
|
|
2231
|
-
return { ...DEFAULT_QUIRKS, ...overrides };
|
|
2232
|
-
}
|
|
2233
|
-
|
|
2234
|
-
// src/strategies/gemini.ts
|
|
2235
|
-
var geminiStrategy = {
|
|
2236
|
-
id: "gemini",
|
|
2237
|
-
backendId: buildAcpBackendId("gemini"),
|
|
2238
|
-
displayName: "Gemini CLI",
|
|
2239
|
-
authors: ["Google"],
|
|
2240
|
-
discoveryProbe: {
|
|
2241
|
-
command: "gemini",
|
|
2242
|
-
versionArgs: ["--version"],
|
|
2243
|
-
helpArgs: ["--help"],
|
|
2244
|
-
helpMatches: /(^|\s)--acp(\s|,|$)/
|
|
2245
|
-
},
|
|
2246
|
-
spawn: {
|
|
2247
|
-
command: "gemini",
|
|
2248
|
-
args: ["--acp"],
|
|
2249
|
-
// Gemini refuses to operate without workspace trust; matching PwrAgnt's
|
|
2250
|
-
// launch-descriptor normalization, append --skip-trust + set the env flag.
|
|
2251
|
-
ensureArgs: ["--skip-trust"],
|
|
2252
|
-
env: { GEMINI_CLI_TRUST_WORKSPACE: "true" }
|
|
2253
|
-
},
|
|
2254
|
-
quirks: defaultQuirks({ surfaceThoughts: true, titleFrom: "topic-update" })
|
|
2255
|
-
};
|
|
2256
|
-
|
|
2257
|
-
// src/strategies/grok.ts
|
|
2258
|
-
import { homedir } from "os";
|
|
2259
|
-
import path from "path";
|
|
2260
|
-
var grokStrategy = {
|
|
2261
|
-
id: "grok",
|
|
2262
|
-
backendId: buildAcpBackendId("grok"),
|
|
2263
|
-
displayName: "Grok",
|
|
2264
|
-
authors: ["xAI"],
|
|
2265
|
-
discoveryProbe: {
|
|
2266
|
-
command: "grok",
|
|
2267
|
-
versionArgs: ["--version"],
|
|
2268
|
-
helpArgs: ["agent", "stdio", "--help"],
|
|
2269
|
-
helpMatches: /Run the agent over stdio/i,
|
|
2270
|
-
fallbackCommands: [
|
|
2271
|
-
path.join(homedir(), ".grok", "bin", "grok"),
|
|
2272
|
-
"/opt/homebrew/bin/grok",
|
|
2273
|
-
"/usr/local/bin/grok"
|
|
2274
|
-
]
|
|
2275
|
-
},
|
|
2276
|
-
spawn: {
|
|
2277
|
-
command: "grok",
|
|
2278
|
-
args: ["agent", "stdio"]
|
|
2279
|
-
},
|
|
2280
|
-
// Grok auto-generates the thread title via its vendor notification
|
|
2281
|
-
// `_x.ai/session_notification` carrying `session_summary_generated`.
|
|
2282
|
-
quirks: defaultQuirks({
|
|
2283
|
-
surfaceThoughts: true,
|
|
2284
|
-
titleFrom: "session-summary",
|
|
2285
|
-
vendorNotificationMethods: ["_x.ai/session_notification"]
|
|
2286
|
-
})
|
|
2287
|
-
};
|
|
2288
|
-
|
|
2289
|
-
// src/strategies/kimi.ts
|
|
2290
|
-
import { homedir as homedir2 } from "os";
|
|
2291
|
-
import path2 from "path";
|
|
2292
|
-
var kimiStrategy = {
|
|
2293
|
-
id: "kimi",
|
|
2294
|
-
backendId: buildAcpBackendId("kimi"),
|
|
2295
|
-
displayName: "Kimi Code CLI",
|
|
2296
|
-
authors: ["Moonshot AI"],
|
|
2297
|
-
discoveryProbe: {
|
|
2298
|
-
command: "kimi",
|
|
2299
|
-
versionArgs: ["--version"],
|
|
2300
|
-
// Capability signal is the EXIT CODE of `kimi acp --help`, NOT its prose.
|
|
2301
|
-
// kimi's commander CLI exits non-zero for an unknown subcommand, so a
|
|
2302
|
-
// zero-exit proves the `acp` subcommand exists. No `helpMatches`: the help
|
|
2303
|
-
// wording has already drifted across kimi versions (0.11.0 prints "Agent
|
|
2304
|
-
// Client Protocol (ACP) server over stdio"), so matching it is fragile.
|
|
2305
|
-
helpArgs: ["acp", "--help"],
|
|
2306
|
-
// The official Kimi Code installer drops a standalone binary at
|
|
2307
|
-
// ~/.kimi-code/bin/kimi and does NOT add it to PATH — so a Finder-launched
|
|
2308
|
-
// app misses it without this fallback (mirrors grok/qwen).
|
|
2309
|
-
fallbackCommands: [
|
|
2310
|
-
path2.join(homedir2(), ".kimi-code", "bin", "kimi"),
|
|
2311
|
-
"/opt/homebrew/bin/kimi",
|
|
2312
|
-
"/usr/local/bin/kimi"
|
|
2313
|
-
]
|
|
2314
|
-
},
|
|
2315
|
-
spawn: {
|
|
2316
|
-
command: "kimi",
|
|
2317
|
-
args: ["acp"]
|
|
2318
|
-
},
|
|
2319
|
-
quirks: defaultQuirks({ surfaceThoughts: true, titleFrom: "topic-update" })
|
|
2320
|
-
};
|
|
2321
|
-
|
|
2322
|
-
// src/strategies/qwen.ts
|
|
2323
|
-
import { homedir as homedir3 } from "os";
|
|
2324
|
-
import path3 from "path";
|
|
2325
|
-
var qwenStrategy = {
|
|
2326
|
-
id: "qwen",
|
|
2327
|
-
backendId: buildAcpBackendId("qwen"),
|
|
2328
|
-
displayName: "Qwen Code",
|
|
2329
|
-
authors: ["Qwen Team"],
|
|
2330
|
-
license: "Apache-2.0",
|
|
2331
|
-
repositoryUrl: "https://github.com/QwenLM/qwen-code",
|
|
2332
|
-
discoveryProbe: {
|
|
2333
|
-
command: "qwen",
|
|
2334
|
-
versionArgs: ["--version"],
|
|
2335
|
-
helpArgs: ["--help"],
|
|
2336
|
-
helpMatches: /(^|\s)--acp(\s|,|$)/,
|
|
2337
|
-
fallbackCommands: [
|
|
2338
|
-
path3.join(homedir3(), ".qwen", "bin", "qwen"),
|
|
2339
|
-
"/opt/homebrew/bin/qwen",
|
|
2340
|
-
"/usr/local/bin/qwen"
|
|
2341
|
-
]
|
|
2342
|
-
},
|
|
2343
|
-
spawn: {
|
|
2344
|
-
command: "qwen",
|
|
2345
|
-
args: ["--acp"]
|
|
2346
|
-
},
|
|
2347
|
-
// Qwen's thought chunks are noisy internal scaffolding — suppress them.
|
|
2348
|
-
quirks: defaultQuirks({ surfaceThoughts: false, titleFrom: "topic-update" })
|
|
2349
|
-
};
|
|
2350
|
-
|
|
2351
|
-
// src/strategies/index.ts
|
|
2352
|
-
var BUILT_IN_ACP_STRATEGIES = [
|
|
2353
|
-
geminiStrategy,
|
|
2354
|
-
kimiStrategy,
|
|
2355
|
-
grokStrategy,
|
|
2356
|
-
qwenStrategy
|
|
2357
|
-
];
|
|
2358
|
-
function buildStrategyTable(strategies = BUILT_IN_ACP_STRATEGIES) {
|
|
2359
|
-
const table = /* @__PURE__ */ new Map();
|
|
2360
|
-
for (const strategy of strategies) {
|
|
2361
|
-
table.set(strategy.id, strategy);
|
|
2362
|
-
}
|
|
2363
|
-
return table;
|
|
2364
|
-
}
|
|
2365
|
-
function strategyById(id, table = buildStrategyTable()) {
|
|
2366
|
-
return table.get(id);
|
|
2367
|
-
}
|
|
2368
|
-
function strategyByBackendId(backendId, strategies = BUILT_IN_ACP_STRATEGIES) {
|
|
2369
|
-
return strategies.find((strategy) => strategy.backendId === backendId);
|
|
2370
|
-
}
|
|
2371
|
-
|
|
2372
|
-
// src/discovery/acp-local-discovery.ts
|
|
2373
|
-
import { execFile as execFileCallback } from "child_process";
|
|
2374
|
-
import { readdirSync, realpathSync, statSync } from "fs";
|
|
2375
|
-
import { homedir as homedir4 } from "os";
|
|
2376
|
-
import path4 from "path";
|
|
2377
|
-
import { promisify } from "util";
|
|
2378
|
-
import {
|
|
2379
|
-
createCommandInvocation as createCommandInvocation2,
|
|
2380
|
-
prependCommandDirToPath as prependCommandDirToPath2
|
|
2381
|
-
} from "@pwrdrvr/agent-transport";
|
|
2382
|
-
var execFile = promisify(execFileCallback);
|
|
2383
|
-
var DEFAULT_ACP_PROBE_TIMEOUT_MS = 5e3;
|
|
2384
|
-
async function discoverLocalAcpAgentInstances(options = {}) {
|
|
2385
|
-
const strategies = options.strategies ?? BUILT_IN_ACP_STRATEGIES;
|
|
2386
|
-
const env = options.env ?? process.env;
|
|
2387
|
-
const probe = options.probe ?? makeDefaultProbe(env);
|
|
2388
|
-
const listExecutables = options.listExecutables ?? defaultListExecutables;
|
|
2389
|
-
const now = options.now ?? Date.now;
|
|
2390
|
-
const probeOptions = {
|
|
2391
|
-
timeoutMs: options.probeTimeoutMs ?? DEFAULT_ACP_PROBE_TIMEOUT_MS,
|
|
2392
|
-
signal: options.signal
|
|
2393
|
-
};
|
|
2394
|
-
const groups = await Promise.all(
|
|
2395
|
-
strategies.map(
|
|
2396
|
-
(strategy) => discoverStrategyInstances(
|
|
2397
|
-
strategy,
|
|
2398
|
-
probe,
|
|
2399
|
-
now,
|
|
2400
|
-
env,
|
|
2401
|
-
listExecutables,
|
|
2402
|
-
options.overrides?.[strategy.id],
|
|
2403
|
-
options.includeRejectedCandidates === true,
|
|
2404
|
-
probeOptions
|
|
2405
|
-
)
|
|
2406
|
-
)
|
|
2407
|
-
);
|
|
2408
|
-
return groups.filter((group) => group !== void 0);
|
|
2409
|
-
}
|
|
2410
|
-
async function discoverLocalAcpAgents(options = {}) {
|
|
2411
|
-
const groups = await discoverLocalAcpAgentInstances(options);
|
|
2412
|
-
return groups.flatMap((group) => {
|
|
2413
|
-
const first = group.instances[0];
|
|
2414
|
-
if (first === void 0) return [];
|
|
2415
|
-
const agent = {
|
|
2416
|
-
strategyId: group.strategyId,
|
|
2417
|
-
backendId: group.backendId,
|
|
2418
|
-
name: group.name,
|
|
2419
|
-
command: first.command,
|
|
2420
|
-
args: group.args,
|
|
2421
|
-
env: group.env,
|
|
2422
|
-
discoveredAt: group.discoveredAt
|
|
2423
|
-
};
|
|
2424
|
-
if (first.version !== void 0) agent.version = first.version;
|
|
2425
|
-
return [agent];
|
|
2426
|
-
});
|
|
2427
|
-
}
|
|
2428
|
-
async function discoverStrategyInstances(strategy, probe, now, env, listExecutables, override, includeRejectedCandidates = false, probeOptions = {}) {
|
|
2429
|
-
const candidates = candidateCommands(strategy, env, listExecutables, override);
|
|
2430
|
-
const instances = [];
|
|
2431
|
-
const rejectedInstances = [];
|
|
2432
|
-
for (const candidate of candidates) {
|
|
2433
|
-
if (probeOptions.signal?.aborted) break;
|
|
2434
|
-
const [versionAttempt, helpAttempt] = await Promise.all([
|
|
2435
|
-
runProbe(probe, candidate.command, strategy.discoveryProbe.versionArgs, probeOptions),
|
|
2436
|
-
runProbe(probe, candidate.command, strategy.discoveryProbe.helpArgs, probeOptions)
|
|
2437
|
-
]);
|
|
2438
|
-
if (probeOptions.signal?.aborted) break;
|
|
2439
|
-
const versionResult = versionAttempt.ok ? versionAttempt.result : void 0;
|
|
2440
|
-
const helpResult = helpAttempt.ok ? helpAttempt.result : void 0;
|
|
2441
|
-
const version = versionResult ? parseCliVersion(resultText(versionResult)) : void 0;
|
|
2442
|
-
if (!versionResult || !helpResult) {
|
|
2443
|
-
const timedOut = !versionAttempt.ok && versionAttempt.timedOut || !helpAttempt.ok && helpAttempt.timedOut;
|
|
2444
|
-
if (candidate.detected && (timedOut || includeRejectedCandidates)) {
|
|
2445
|
-
const rejected = {
|
|
2446
|
-
command: candidate.command,
|
|
2447
|
-
source: candidate.source,
|
|
2448
|
-
reason: timedOut ? "probe-timed-out" : versionResult ? "acp-probe-failed" : "version-probe-failed"
|
|
2449
|
-
};
|
|
2450
|
-
if (version !== void 0) rejected.version = version;
|
|
2451
|
-
rejectedInstances.push(rejected);
|
|
2452
|
-
}
|
|
2453
|
-
continue;
|
|
2454
|
-
}
|
|
2455
|
-
if (strategy.discoveryProbe.helpMatches !== void 0 && !strategy.discoveryProbe.helpMatches.test(resultText(helpResult))) {
|
|
2456
|
-
if (includeRejectedCandidates && candidate.detected) {
|
|
2457
|
-
const rejected = {
|
|
2458
|
-
command: candidate.command,
|
|
2459
|
-
source: candidate.source,
|
|
2460
|
-
reason: "acp-help-mismatch"
|
|
2461
|
-
};
|
|
2462
|
-
if (version !== void 0) rejected.version = version;
|
|
2463
|
-
rejectedInstances.push(rejected);
|
|
2464
|
-
}
|
|
2465
|
-
continue;
|
|
2466
|
-
}
|
|
2467
|
-
const instance = {
|
|
2468
|
-
command: candidate.command,
|
|
2469
|
-
source: candidate.source
|
|
2470
|
-
};
|
|
2471
|
-
if (version !== void 0) instance.version = version;
|
|
2472
|
-
instances.push(instance);
|
|
2473
|
-
}
|
|
2474
|
-
if (instances.length === 0 && rejectedInstances.length === 0) {
|
|
2475
|
-
return void 0;
|
|
2476
|
-
}
|
|
2477
|
-
return {
|
|
2478
|
-
strategyId: strategy.id,
|
|
2479
|
-
backendId: strategy.backendId,
|
|
2480
|
-
name: strategy.displayName,
|
|
2481
|
-
args: ensureArgs(strategy.spawn.args, strategy.spawn.ensureArgs),
|
|
2482
|
-
env: strategy.spawn.env ?? {},
|
|
2483
|
-
instances,
|
|
2484
|
-
...rejectedInstances.length > 0 ? { rejectedInstances } : {},
|
|
2485
|
-
discoveredAt: now()
|
|
2486
|
-
};
|
|
2487
|
-
}
|
|
2488
|
-
function candidateCommands(strategy, env, listExecutables, override) {
|
|
2489
|
-
const out = [];
|
|
2490
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2491
|
-
const push = (command, source, detected) => {
|
|
2492
|
-
const trimmed = command.trim();
|
|
2493
|
-
if (trimmed.length === 0) return;
|
|
2494
|
-
const key = canonicalize(trimmed);
|
|
2495
|
-
if (seen.has(key)) return;
|
|
2496
|
-
seen.add(key);
|
|
2497
|
-
out.push({ command: trimmed, source, detected });
|
|
2498
|
-
};
|
|
2499
|
-
if (override && override.trim().length > 0) {
|
|
2500
|
-
push(override, "override", isExecutableFile(override));
|
|
2501
|
-
}
|
|
2502
|
-
const pathMatches = listExecutables(strategy.discoveryProbe.command, env);
|
|
2503
|
-
if (pathMatches.length > 0) {
|
|
2504
|
-
for (const match of pathMatches) push(match, "path", true);
|
|
2505
|
-
} else {
|
|
2506
|
-
push(strategy.discoveryProbe.command, "path", false);
|
|
2507
|
-
}
|
|
2508
|
-
for (const fallback of strategy.discoveryProbe.fallbackCommands ?? []) {
|
|
2509
|
-
push(fallback, "fallback", isExecutableFile(fallback));
|
|
2510
|
-
}
|
|
2511
|
-
return out;
|
|
2512
|
-
}
|
|
2513
|
-
function isExecutableFile(command) {
|
|
2514
|
-
try {
|
|
2515
|
-
const stat = statSync(command);
|
|
2516
|
-
return stat.isFile() && (process.platform === "win32" || (stat.mode & 73) !== 0);
|
|
2517
|
-
} catch {
|
|
2518
|
-
return false;
|
|
2519
|
-
}
|
|
2520
|
-
}
|
|
2521
|
-
function canonicalize(command) {
|
|
2522
|
-
if (!path4.isAbsolute(command)) {
|
|
2523
|
-
return process.platform === "win32" ? command.toLowerCase() : command;
|
|
2524
|
-
}
|
|
2525
|
-
try {
|
|
2526
|
-
const canonical = realpathSync(command);
|
|
2527
|
-
return process.platform === "win32" ? canonical.toLowerCase() : canonical;
|
|
2528
|
-
} catch {
|
|
2529
|
-
return process.platform === "win32" ? command.toLowerCase() : command;
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
function defaultListExecutables(command, env) {
|
|
2533
|
-
if (command.includes("/") || command.includes("\\")) return [];
|
|
2534
|
-
const found = [];
|
|
2535
|
-
const seenDir = /* @__PURE__ */ new Set();
|
|
2536
|
-
for (const dir of discoveryDirs(env)) {
|
|
2537
|
-
const normalizedDir = normalizePathEntry(dir);
|
|
2538
|
-
const dirKey = process.platform === "win32" ? normalizedDir.toLowerCase() : normalizedDir;
|
|
2539
|
-
if (normalizedDir.length === 0 || seenDir.has(dirKey)) continue;
|
|
2540
|
-
seenDir.add(dirKey);
|
|
2541
|
-
for (const commandName of executableCommandNames(command, env)) {
|
|
2542
|
-
const candidate = path4.join(normalizedDir, commandName);
|
|
2543
|
-
try {
|
|
2544
|
-
const stat = statSync(candidate);
|
|
2545
|
-
if (stat.isFile() && (process.platform === "win32" || (stat.mode & 73) !== 0)) {
|
|
2546
|
-
found.push(candidate);
|
|
2547
|
-
}
|
|
2548
|
-
} catch {
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
return found;
|
|
2553
|
-
}
|
|
2554
|
-
function executableCommandNames(command, env) {
|
|
2555
|
-
if (process.platform !== "win32") return [command];
|
|
2556
|
-
const extensions = (readWindowsEnv(env, "PATHEXT")?.trim() || ".COM;.EXE;.BAT;.CMD").split(";").map((extension) => extension.trim()).filter(Boolean).map((extension) => extension.startsWith(".") ? extension : `.${extension}`);
|
|
2557
|
-
const commandExtension = path4.win32.extname(command);
|
|
2558
|
-
if (commandExtension.length > 0 && extensions.some((extension) => extension.toLowerCase() === commandExtension.toLowerCase())) {
|
|
2559
|
-
return [command];
|
|
2560
|
-
}
|
|
2561
|
-
return extensions.map((extension) => `${command}${extension}`);
|
|
2562
|
-
}
|
|
2563
|
-
function readWindowsEnv(env, name) {
|
|
2564
|
-
const key = Object.keys(env).find((candidate) => candidate.toLowerCase() === name.toLowerCase());
|
|
2565
|
-
return key ? env[key] : void 0;
|
|
2566
|
-
}
|
|
2567
|
-
function normalizePathEntry(entry) {
|
|
2568
|
-
const trimmed = entry.trim();
|
|
2569
|
-
const quoted = trimmed.match(/^"(.*)"$/);
|
|
2570
|
-
return quoted?.[1] ?? trimmed;
|
|
2571
|
-
}
|
|
2572
|
-
function discoveryDirs(env) {
|
|
2573
|
-
const pathValue = process.platform === "win32" ? readWindowsEnv(env, "PATH") ?? "" : env.PATH ?? "";
|
|
2574
|
-
const pathDirs = pathValue.split(path4.delimiter);
|
|
2575
|
-
return process.platform === "win32" ? pathDirs : [...pathDirs, ...wellKnownAgentBinDirs(homedir4())];
|
|
2576
|
-
}
|
|
2577
|
-
function wellKnownAgentBinDirs(home = homedir4()) {
|
|
2578
|
-
return [
|
|
2579
|
-
// Every installed nvm node version's bin (the agent may be under any one).
|
|
2580
|
-
...nvmNodeBinDirs(home),
|
|
2581
|
-
// Other version / package managers.
|
|
2582
|
-
path4.join(home, ".volta", "bin"),
|
|
2583
|
-
path4.join(home, ".bun", "bin"),
|
|
2584
|
-
path4.join(home, ".asdf", "shims"),
|
|
2585
|
-
path4.join(home, ".deno", "bin"),
|
|
2586
|
-
path4.join(home, ".local", "bin"),
|
|
2587
|
-
path4.join(home, ".npm-global", "bin"),
|
|
2588
|
-
// Common system install prefixes.
|
|
2589
|
-
"/opt/homebrew/bin",
|
|
2590
|
-
"/usr/local/bin"
|
|
2591
|
-
];
|
|
2592
|
-
}
|
|
2593
|
-
function nvmNodeBinDirs(home) {
|
|
2594
|
-
const base = path4.join(home, ".nvm", "versions", "node");
|
|
2595
|
-
try {
|
|
2596
|
-
return readdirSync(base).sort().reverse().map((version) => path4.join(base, version, "bin"));
|
|
2597
|
-
} catch {
|
|
2598
|
-
return [];
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
function ensureArgs(args, ensure) {
|
|
2602
|
-
if (!ensure || ensure.length === 0) {
|
|
2603
|
-
return args;
|
|
2604
|
-
}
|
|
2605
|
-
const result = [...args];
|
|
2606
|
-
for (const arg of ensure) {
|
|
2607
|
-
if (!result.includes(arg)) result.push(arg);
|
|
2608
|
-
}
|
|
2609
|
-
return result;
|
|
2610
|
-
}
|
|
2611
|
-
var AcpProbeTimeoutError = class extends Error {
|
|
2612
|
-
constructor(command, timeoutMs) {
|
|
2613
|
-
super(`acp probe did not answer within ${timeoutMs}ms: ${command}`);
|
|
2614
|
-
this.name = "AcpProbeTimeoutError";
|
|
2615
|
-
}
|
|
2616
|
-
};
|
|
2617
|
-
function isProbeTimeout(error) {
|
|
2618
|
-
if (error instanceof AcpProbeTimeoutError) return true;
|
|
2619
|
-
const name = error?.name;
|
|
2620
|
-
return error?.killed === true || name === "AbortError" || name === "TimeoutError";
|
|
2621
|
-
}
|
|
2622
|
-
function normalizeProbeTimeoutMs(timeoutMs) {
|
|
2623
|
-
if (timeoutMs === void 0 || !Number.isFinite(timeoutMs)) {
|
|
2624
|
-
return DEFAULT_ACP_PROBE_TIMEOUT_MS;
|
|
2625
|
-
}
|
|
2626
|
-
return Math.max(1, Math.floor(timeoutMs));
|
|
2627
|
-
}
|
|
2628
|
-
function makeDefaultProbe(env) {
|
|
2629
|
-
return async (command, args, options = {}) => {
|
|
2630
|
-
const timeoutMs = normalizeProbeTimeoutMs(options.timeoutMs);
|
|
2631
|
-
const launchEnv = prependCommandDirToPath2(command, env);
|
|
2632
|
-
const invocation = createCommandInvocation2({ command, args, env: launchEnv });
|
|
2633
|
-
const controller = new AbortController();
|
|
2634
|
-
let timedOut = false;
|
|
2635
|
-
const onAbort = () => controller.abort();
|
|
2636
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2637
|
-
const timer = setTimeout(() => {
|
|
2638
|
-
timedOut = true;
|
|
2639
|
-
controller.abort();
|
|
2640
|
-
}, timeoutMs);
|
|
2641
|
-
const expired = new Promise((resolve) => {
|
|
2642
|
-
controller.signal.addEventListener("abort", () => resolve("expired"), {
|
|
2643
|
-
once: true
|
|
2644
|
-
});
|
|
2645
|
-
});
|
|
2646
|
-
try {
|
|
2647
|
-
const execution = execFile(invocation.command, invocation.args, {
|
|
2648
|
-
timeout: timeoutMs,
|
|
2649
|
-
signal: controller.signal,
|
|
2650
|
-
maxBuffer: 1024 * 1024,
|
|
2651
|
-
// A Windows `.cmd` shim runs through `cmd.exe`; without this every
|
|
2652
|
-
// discovery pass flashes console windows over the host app.
|
|
2653
|
-
windowsHide: true,
|
|
2654
|
-
// Prepend the candidate's own dir so a node-script CLI (e.g. an nvm-
|
|
2655
|
-
// installed `qwen`) finds its sibling `node` during the probe.
|
|
2656
|
-
env: launchEnv,
|
|
2657
|
-
windowsVerbatimArguments: invocation.windowsVerbatimArguments
|
|
2658
|
-
}).then(
|
|
2659
|
-
(result) => ({ ok: true, result }),
|
|
2660
|
-
(error) => ({ ok: false, error })
|
|
2661
|
-
);
|
|
2662
|
-
const settled = await Promise.race([execution, expired]);
|
|
2663
|
-
if (settled === "expired") {
|
|
2664
|
-
if (timedOut) throw new AcpProbeTimeoutError(command, timeoutMs);
|
|
2665
|
-
throw new Error(`acp probe aborted: ${command}`);
|
|
2666
|
-
}
|
|
2667
|
-
if (!settled.ok) {
|
|
2668
|
-
if (timedOut) throw new AcpProbeTimeoutError(command, timeoutMs);
|
|
2669
|
-
throw settled.error;
|
|
2670
|
-
}
|
|
2671
|
-
return settled.result;
|
|
2672
|
-
} finally {
|
|
2673
|
-
clearTimeout(timer);
|
|
2674
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
2675
|
-
}
|
|
2676
|
-
};
|
|
2677
|
-
}
|
|
2678
|
-
async function runProbe(probe, command, args, options) {
|
|
2679
|
-
try {
|
|
2680
|
-
return { ok: true, result: await probe(command, args, options) };
|
|
2681
|
-
} catch (error) {
|
|
2682
|
-
return { ok: false, timedOut: isProbeTimeout(error) };
|
|
2683
|
-
}
|
|
2684
|
-
}
|
|
2685
|
-
function resultText(result) {
|
|
2686
|
-
return [result.stdout, result.stderr].flatMap((value) => value === void 0 ? [] : [value]).map((value) => Buffer.isBuffer(value) ? value.toString("utf8") : value).join("\n");
|
|
2687
|
-
}
|
|
2688
|
-
function parseCliVersion(output) {
|
|
2689
|
-
const trimmed = output.trim();
|
|
2690
|
-
if (!trimmed) {
|
|
2691
|
-
return void 0;
|
|
2692
|
-
}
|
|
2693
|
-
return trimmed.match(/\d+\.\d+\.\d+(?:[-+][\w.-]+)?/)?.[0] ?? trimmed;
|
|
2694
|
-
}
|
|
2695
|
-
|
|
2696
|
-
// src/discovery/acp-agent-allowlist.ts
|
|
2697
|
-
var BANNED_ACP_REGISTRY_IDS = /* @__PURE__ */ new Set(["codex-acp"]);
|
|
2698
|
-
var DEFAULT_ACP_AGENT_ALLOWLIST = [
|
|
2699
|
-
{ id: "local-grok-cli", registryId: "grok", distributionKinds: ["local"] },
|
|
2700
|
-
{ id: "local-qwen-code-cli", registryId: "qwen", distributionKinds: ["local"] }
|
|
2701
|
-
];
|
|
2702
|
-
var AcpAgentAllowlist = class {
|
|
2703
|
-
constructor(rules) {
|
|
2704
|
-
this.rules = rules;
|
|
2705
|
-
}
|
|
2706
|
-
rules;
|
|
2707
|
-
evaluate(agent) {
|
|
2708
|
-
if (isBannedAcpRegistryId(agent.id)) {
|
|
2709
|
-
return { allowed: false, reason: "banned" };
|
|
2710
|
-
}
|
|
2711
|
-
const matchingRules = this.rules.filter((rule) => rule.registryId === agent.id);
|
|
2712
|
-
if (matchingRules.length === 0) {
|
|
2713
|
-
return { allowed: false, reason: "not-allowlisted" };
|
|
2714
|
-
}
|
|
2715
|
-
for (const rule of matchingRules) {
|
|
2716
|
-
const denial = evaluateRule(rule, agent);
|
|
2717
|
-
if (!denial) {
|
|
2718
|
-
return {
|
|
2719
|
-
allowed: true,
|
|
2720
|
-
ruleId: rule.id,
|
|
2721
|
-
unverifiedBinaryAllowed: rule.allowUnverifiedBinary === true
|
|
2722
|
-
};
|
|
2723
|
-
}
|
|
2724
|
-
}
|
|
2725
|
-
return { allowed: false, reason: "allowlist-rule-mismatch" };
|
|
2726
|
-
}
|
|
2727
|
-
evaluateDistribution(agent, distribution) {
|
|
2728
|
-
if (isBannedAcpRegistryId(agent.id)) {
|
|
2729
|
-
return { allowed: false, reason: "banned" };
|
|
2730
|
-
}
|
|
2731
|
-
const matchingRules = this.rules.filter((rule) => rule.registryId === agent.id);
|
|
2732
|
-
if (matchingRules.length === 0) {
|
|
2733
|
-
return { allowed: false, reason: "not-allowlisted" };
|
|
2734
|
-
}
|
|
2735
|
-
for (const rule of matchingRules) {
|
|
2736
|
-
const denial = evaluateDistributionRule(rule, agent, distribution);
|
|
2737
|
-
if (!denial) {
|
|
2738
|
-
return {
|
|
2739
|
-
allowed: true,
|
|
2740
|
-
ruleId: rule.id,
|
|
2741
|
-
unverifiedBinaryAllowed: rule.allowUnverifiedBinary === true
|
|
2742
|
-
};
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
return { allowed: false, reason: "allowlist-rule-mismatch" };
|
|
2746
|
-
}
|
|
2747
|
-
};
|
|
2748
|
-
var defaultAcpAgentAllowlist = new AcpAgentAllowlist(
|
|
2749
|
-
DEFAULT_ACP_AGENT_ALLOWLIST
|
|
2750
|
-
);
|
|
2751
|
-
function isBannedAcpRegistryId(registryId) {
|
|
2752
|
-
return BANNED_ACP_REGISTRY_IDS.has(registryId);
|
|
2753
|
-
}
|
|
2754
|
-
function evaluateRule(rule, agent) {
|
|
2755
|
-
if (rule.versions && (!agent.version || !rule.versions.includes(agent.version))) {
|
|
2756
|
-
return "version-not-allowed";
|
|
2757
|
-
}
|
|
2758
|
-
if (isGplFamilyLicense(agent.license) && !rule.allowGplFamilyLicense) {
|
|
2759
|
-
return "license-not-allowed";
|
|
2760
|
-
}
|
|
2761
|
-
let distributionDeniedBySource = false;
|
|
2762
|
-
for (const distribution of agent.distributions) {
|
|
2763
|
-
const denial = evaluateDistributionRule(rule, agent, distribution, {
|
|
2764
|
-
skipAgentChecks: true
|
|
2765
|
-
});
|
|
2766
|
-
if (!denial) {
|
|
2767
|
-
return void 0;
|
|
2768
|
-
}
|
|
2769
|
-
if (denial === "distribution-source-not-allowed") {
|
|
2770
|
-
distributionDeniedBySource = true;
|
|
2771
|
-
}
|
|
2772
|
-
}
|
|
2773
|
-
return distributionDeniedBySource ? "distribution-source-not-allowed" : "distribution-not-allowed";
|
|
2774
|
-
}
|
|
2775
|
-
function evaluateDistributionRule(rule, agent, distribution, options = {}) {
|
|
2776
|
-
if (!options.skipAgentChecks && rule.versions && (!agent.version || !rule.versions.includes(agent.version))) {
|
|
2777
|
-
return "version-not-allowed";
|
|
2778
|
-
}
|
|
2779
|
-
if (!options.skipAgentChecks && isGplFamilyLicense(agent.license) && !rule.allowGplFamilyLicense) {
|
|
2780
|
-
return "license-not-allowed";
|
|
2781
|
-
}
|
|
2782
|
-
if (!distributionAllowedByKind(rule, distribution)) {
|
|
2783
|
-
return "distribution-not-allowed";
|
|
2784
|
-
}
|
|
2785
|
-
if (!distributionSourceAllowed(rule, distribution)) {
|
|
2786
|
-
return "distribution-source-not-allowed";
|
|
2787
|
-
}
|
|
2788
|
-
return void 0;
|
|
2789
|
-
}
|
|
2790
|
-
function distributionAllowedByKind(rule, distribution) {
|
|
2791
|
-
return !rule.distributionKinds || rule.distributionKinds.includes(distribution.kind);
|
|
2792
|
-
}
|
|
2793
|
-
function distributionSourceAllowed(rule, distribution) {
|
|
2794
|
-
if (distribution.kind === "npx" || distribution.kind === "uvx") {
|
|
2795
|
-
return !rule.allowedPackageNames || rule.allowedPackageNames.includes(distribution.packageName);
|
|
2796
|
-
}
|
|
2797
|
-
if (distribution.kind !== "binary") {
|
|
2798
|
-
return false;
|
|
2799
|
-
}
|
|
2800
|
-
if (!rule.allowedArchiveHosts) {
|
|
2801
|
-
return true;
|
|
2802
|
-
}
|
|
2803
|
-
try {
|
|
2804
|
-
return rule.allowedArchiveHosts.includes(new URL(distribution.archiveUrl).host);
|
|
2805
|
-
} catch {
|
|
2806
|
-
return false;
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
function isGplFamilyLicense(license) {
|
|
2810
|
-
return /\b(?:GPL|AGPL|LGPL)\b/i.test(license ?? "");
|
|
2811
|
-
}
|
|
2812
|
-
|
|
2813
|
-
// src/discovery/acp-registry-types.ts
|
|
2814
|
-
var ACP_REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json";
|
|
2815
|
-
|
|
2816
|
-
// src/discovery/acp-registry-service.ts
|
|
2817
|
-
var AcpRegistryService = class {
|
|
2818
|
-
allowlist;
|
|
2819
|
-
fetcher;
|
|
2820
|
-
now;
|
|
2821
|
-
registryUrl;
|
|
2822
|
-
constructor(options = {}) {
|
|
2823
|
-
this.allowlist = options.allowlist ?? defaultAcpAgentAllowlist;
|
|
2824
|
-
this.fetcher = options.fetch ?? (async (input, init) => {
|
|
2825
|
-
const response = await globalThis.fetch(input, init);
|
|
2826
|
-
return {
|
|
2827
|
-
ok: response.ok,
|
|
2828
|
-
status: response.status,
|
|
2829
|
-
statusText: response.statusText,
|
|
2830
|
-
json: () => response.json()
|
|
2831
|
-
};
|
|
2832
|
-
});
|
|
2833
|
-
this.now = options.now ?? Date.now;
|
|
2834
|
-
this.registryUrl = options.registryUrl ?? ACP_REGISTRY_URL;
|
|
2835
|
-
}
|
|
2836
|
-
async fetchRegistry() {
|
|
2837
|
-
const response = await this.fetcher(this.registryUrl, {
|
|
2838
|
-
headers: { accept: "application/json" }
|
|
2839
|
-
});
|
|
2840
|
-
if (!response.ok) {
|
|
2841
|
-
throw new Error(
|
|
2842
|
-
`ACP registry request failed: ${response.status} ${response.statusText}`
|
|
2843
|
-
);
|
|
2844
|
-
}
|
|
2845
|
-
const raw = await response.json();
|
|
2846
|
-
return { fetchedAt: this.now(), agents: normalizeRegistry(raw), raw };
|
|
2847
|
-
}
|
|
2848
|
-
applyAllowlist(snapshot) {
|
|
2849
|
-
return snapshot.agents.map((agent) => {
|
|
2850
|
-
const allowlist = this.allowlist.evaluate(agent);
|
|
2851
|
-
const distributionPolicies = agent.distributions.map(
|
|
2852
|
-
(distribution) => this.evaluateDistribution(agent, distribution)
|
|
2853
|
-
);
|
|
2854
|
-
const installablePolicy = distributionPolicies.find((policy) => policy.installable);
|
|
2855
|
-
const firstBlockedAllowedPolicy = distributionPolicies.find(
|
|
2856
|
-
(policy) => policy.allowlist.allowed
|
|
2857
|
-
);
|
|
2858
|
-
const installable = Boolean(installablePolicy);
|
|
2859
|
-
const result = {
|
|
2860
|
-
...agent,
|
|
2861
|
-
allowlist,
|
|
2862
|
-
installable,
|
|
2863
|
-
verificationStatus: installablePolicy?.verificationStatus ?? firstBlockedAllowedPolicy?.verificationStatus ?? "not-applicable"
|
|
2864
|
-
};
|
|
2865
|
-
const unavailableReason = installable ? void 0 : firstBlockedAllowedPolicy?.unavailableReason ?? (allowlist.allowed ? "distribution-not-installable" : allowlist.reason);
|
|
2866
|
-
if (unavailableReason !== void 0) result.unavailableReason = unavailableReason;
|
|
2867
|
-
return result;
|
|
2868
|
-
});
|
|
2869
|
-
}
|
|
2870
|
-
evaluateDistribution(agent, distribution) {
|
|
2871
|
-
const allowlist = this.allowlist.evaluateDistribution(agent, distribution);
|
|
2872
|
-
return evaluateAcpDistributionPolicy(distribution, allowlist);
|
|
2873
|
-
}
|
|
2874
|
-
};
|
|
2875
|
-
function evaluateAcpDistributionPolicy(distribution, allowlist) {
|
|
2876
|
-
if (!allowlist.allowed) {
|
|
2877
|
-
return {
|
|
2878
|
-
allowlist,
|
|
2879
|
-
installable: false,
|
|
2880
|
-
verificationStatus: "not-applicable",
|
|
2881
|
-
unavailableReason: allowlist.reason
|
|
2882
|
-
};
|
|
2883
|
-
}
|
|
2884
|
-
if (distribution.kind !== "binary") {
|
|
2885
|
-
return { allowlist, installable: true, verificationStatus: "not-applicable" };
|
|
2886
|
-
}
|
|
2887
|
-
if (distribution.checksum) {
|
|
2888
|
-
return { allowlist, installable: true, verificationStatus: "verified" };
|
|
2889
|
-
}
|
|
2890
|
-
return {
|
|
2891
|
-
allowlist,
|
|
2892
|
-
installable: allowlist.unverifiedBinaryAllowed,
|
|
2893
|
-
verificationStatus: allowlist.unverifiedBinaryAllowed ? "unverified-allowed" : "unverified-blocked",
|
|
2894
|
-
...allowlist.unverifiedBinaryAllowed ? {} : { unavailableReason: "binary-integrity-metadata-missing" }
|
|
2895
|
-
};
|
|
2896
|
-
}
|
|
2897
|
-
function normalizeRegistry(raw) {
|
|
2898
|
-
const record = asRecord3(raw);
|
|
2899
|
-
const rawAgents = Array.isArray(record?.agents) ? record.agents : [];
|
|
2900
|
-
return rawAgents.flatMap((item) => {
|
|
2901
|
-
const agent = normalizeAgent(item);
|
|
2902
|
-
return agent ? [agent] : [];
|
|
2903
|
-
});
|
|
2904
|
-
}
|
|
2905
|
-
function normalizeAgent(raw) {
|
|
2906
|
-
const record = asRecord3(raw);
|
|
2907
|
-
if (!record || typeof record.id !== "string" || typeof record.name !== "string") {
|
|
2908
|
-
return void 0;
|
|
2909
|
-
}
|
|
2910
|
-
const distributions = normalizeDistributions(record.distribution);
|
|
2911
|
-
if (distributions.length === 0) {
|
|
2912
|
-
return void 0;
|
|
2913
|
-
}
|
|
2914
|
-
const agent = {
|
|
2915
|
-
id: record.id,
|
|
2916
|
-
backendId: buildAcpBackendId(record.id),
|
|
2917
|
-
name: record.name,
|
|
2918
|
-
authors: Array.isArray(record.authors) ? record.authors.filter((author) => typeof author === "string") : [],
|
|
2919
|
-
distributions,
|
|
2920
|
-
distributionKinds: [...new Set(distributions.map((d) => d.kind))],
|
|
2921
|
-
auth: normalizeAuth(record.auth),
|
|
2922
|
-
raw
|
|
2923
|
-
};
|
|
2924
|
-
const version = stringValue(record.version);
|
|
2925
|
-
if (version !== void 0) agent.version = version;
|
|
2926
|
-
const description = stringValue(record.description);
|
|
2927
|
-
if (description !== void 0) agent.description = description;
|
|
2928
|
-
const license = stringValue(record.license);
|
|
2929
|
-
if (license !== void 0) agent.license = license;
|
|
2930
|
-
const repositoryUrl = stringValue(record.repository);
|
|
2931
|
-
if (repositoryUrl !== void 0) agent.repositoryUrl = repositoryUrl;
|
|
2932
|
-
const websiteUrl = stringValue(record.website);
|
|
2933
|
-
if (websiteUrl !== void 0) agent.websiteUrl = websiteUrl;
|
|
2934
|
-
const iconUrl = stringValue(record.icon);
|
|
2935
|
-
if (iconUrl !== void 0) agent.iconUrl = iconUrl;
|
|
2936
|
-
return agent;
|
|
2937
|
-
}
|
|
2938
|
-
function normalizeDistributions(raw) {
|
|
2939
|
-
const record = asRecord3(raw);
|
|
2940
|
-
if (!record) {
|
|
2941
|
-
return [];
|
|
2942
|
-
}
|
|
2943
|
-
const distributions = [];
|
|
2944
|
-
const npx = normalizePackageDistribution("npx", record.npx);
|
|
2945
|
-
if (npx) distributions.push(npx);
|
|
2946
|
-
const uvx = normalizePackageDistribution("uvx", record.uvx);
|
|
2947
|
-
if (uvx) distributions.push(uvx);
|
|
2948
|
-
const binaryRecord = asRecord3(record.binary);
|
|
2949
|
-
if (binaryRecord) {
|
|
2950
|
-
for (const [platform, value] of Object.entries(binaryRecord)) {
|
|
2951
|
-
const binary = normalizeBinaryDistribution(platform, value);
|
|
2952
|
-
if (binary) distributions.push(binary);
|
|
2953
|
-
}
|
|
2954
|
-
}
|
|
2955
|
-
return distributions;
|
|
2956
|
-
}
|
|
2957
|
-
function normalizePackageDistribution(kind, raw) {
|
|
2958
|
-
const record = asRecord3(raw);
|
|
2959
|
-
if (!record || typeof record.package !== "string") {
|
|
2960
|
-
return void 0;
|
|
2961
|
-
}
|
|
2962
|
-
return {
|
|
2963
|
-
kind,
|
|
2964
|
-
packageName: record.package,
|
|
2965
|
-
args: stringArray(record.args),
|
|
2966
|
-
env: stringRecord(record.env)
|
|
2967
|
-
};
|
|
2968
|
-
}
|
|
2969
|
-
function normalizeBinaryDistribution(platform, raw) {
|
|
2970
|
-
const record = asRecord3(raw);
|
|
2971
|
-
if (!record || typeof record.archive !== "string" || typeof record.cmd !== "string") {
|
|
2972
|
-
return void 0;
|
|
2973
|
-
}
|
|
2974
|
-
const distribution = {
|
|
2975
|
-
kind: "binary",
|
|
2976
|
-
platform,
|
|
2977
|
-
archiveUrl: record.archive,
|
|
2978
|
-
command: record.cmd,
|
|
2979
|
-
args: stringArray(record.args),
|
|
2980
|
-
env: stringRecord(record.env)
|
|
2981
|
-
};
|
|
2982
|
-
const checksum = stringValue(record.checksum) ?? stringValue(record.sha256);
|
|
2983
|
-
if (checksum !== void 0) distribution.checksum = checksum;
|
|
2984
|
-
const signatureUrl = stringValue(record.signature) ?? stringValue(record.signatureUrl);
|
|
2985
|
-
if (signatureUrl !== void 0) distribution.signatureUrl = signatureUrl;
|
|
2986
|
-
return distribution;
|
|
2987
|
-
}
|
|
2988
|
-
function normalizeAuth(raw) {
|
|
2989
|
-
if (!raw) {
|
|
2990
|
-
return { required: false, methods: [] };
|
|
2991
|
-
}
|
|
2992
|
-
const record = asRecord3(raw);
|
|
2993
|
-
if (!record) {
|
|
2994
|
-
return { required: true, methods: ["unknown"], raw };
|
|
2995
|
-
}
|
|
2996
|
-
const methods = stringArray(record.methods).map(
|
|
2997
|
-
(method) => method === "agent-managed" || method === "terminal" ? method : "unknown"
|
|
2998
|
-
);
|
|
2999
|
-
return {
|
|
3000
|
-
required: record.required === false ? false : true,
|
|
3001
|
-
methods: methods.length > 0 ? [...new Set(methods)] : ["unknown"],
|
|
3002
|
-
raw
|
|
3003
|
-
};
|
|
3004
|
-
}
|
|
3005
|
-
function asRecord3(value) {
|
|
3006
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3007
|
-
}
|
|
3008
|
-
function stringValue(value) {
|
|
3009
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
3010
|
-
}
|
|
3011
|
-
function stringArray(value) {
|
|
3012
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
3013
|
-
}
|
|
3014
|
-
function stringRecord(value) {
|
|
3015
|
-
const record = asRecord3(value);
|
|
3016
|
-
if (!record) {
|
|
3017
|
-
return {};
|
|
3018
|
-
}
|
|
3019
|
-
return Object.fromEntries(
|
|
3020
|
-
Object.entries(record).filter(
|
|
3021
|
-
(entry) => typeof entry[1] === "string"
|
|
3022
|
-
)
|
|
3023
|
-
);
|
|
3024
|
-
}
|
|
3025
2284
|
export {
|
|
3026
2285
|
ACP_REGISTRY_URL,
|
|
3027
2286
|
ACP_REQUEST_TIMEOUT_MESSAGE_PREFIX,
|