@vedmalex/ai-connect 0.2.0 → 0.4.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/README.md +137 -67
- package/dist/browser/index.js +113 -17
- package/dist/browser/index.js.map +2 -2
- package/dist/bun/index.js +345 -38
- package/dist/bun/index.js.map +2 -2
- package/dist/bun/local.js +345 -38
- package/dist/bun/local.js.map +2 -2
- package/dist/node/index.js +345 -38
- package/dist/node/index.js.map +2 -2
- package/dist/node/local.js +345 -38
- package/dist/node/local.js.map +2 -2
- package/dist/types/acp.d.ts.map +1 -1
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/default-handlers.d.ts +3 -1
- package/dist/types/default-handlers.d.ts.map +1 -1
- package/dist/types/local-handlers.d.ts.map +1 -1
- package/dist/types/model-reference.d.ts +19 -1
- package/dist/types/model-reference.d.ts.map +1 -1
- package/dist/types/types.d.ts +90 -4
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/bun/local.js
CHANGED
|
@@ -220,11 +220,7 @@ function normalizeTransport(providerId, input) {
|
|
|
220
220
|
...selector.whereEquals !== void 0 ? { whereEquals: selector.whereEquals } : {}
|
|
221
221
|
};
|
|
222
222
|
};
|
|
223
|
-
const
|
|
224
|
-
if (!descriptor.cli?.parser) {
|
|
225
|
-
return void 0;
|
|
226
|
-
}
|
|
227
|
-
const cliParser = descriptor.cli.parser;
|
|
223
|
+
const normalizeCliParser = (cliParser) => {
|
|
228
224
|
if (cliParser.kind === "json") {
|
|
229
225
|
assert(
|
|
230
226
|
cliParser.textPath.trim().length > 0,
|
|
@@ -237,13 +233,22 @@ function normalizeTransport(providerId, input) {
|
|
|
237
233
|
...cliParser.usagePath?.trim() ? { usagePath: cliParser.usagePath.trim() } : {}
|
|
238
234
|
};
|
|
239
235
|
}
|
|
236
|
+
if (cliParser.kind === "text") {
|
|
237
|
+
return {
|
|
238
|
+
kind: "text",
|
|
239
|
+
// Default trim=true, stripAnsi=false; only persist explicit overrides.
|
|
240
|
+
...cliParser.trim === false ? { trim: false } : {},
|
|
241
|
+
...cliParser.stripAnsi === true ? { stripAnsi: true } : {}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
240
244
|
return {
|
|
241
245
|
kind: "jsonl",
|
|
242
246
|
text: normalizeSelector(cliParser.text, "text"),
|
|
243
247
|
...cliParser.error ? { error: normalizeSelector(cliParser.error, "error") } : {},
|
|
244
248
|
...cliParser.usage ? { usage: normalizeSelector(cliParser.usage, "usage") } : {}
|
|
245
249
|
};
|
|
246
|
-
}
|
|
250
|
+
};
|
|
251
|
+
const parser = descriptor.cli?.parser ? normalizeCliParser(descriptor.cli.parser) : void 0;
|
|
247
252
|
const normalized = {
|
|
248
253
|
...descriptor.cli.preset ? { preset: descriptor.cli.preset } : {},
|
|
249
254
|
...descriptor.cli.argsTemplate ? {
|
|
@@ -255,11 +260,13 @@ function normalizeTransport(providerId, input) {
|
|
|
255
260
|
if (!descriptor.cli?.discovery) {
|
|
256
261
|
return void 0;
|
|
257
262
|
}
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
+
const explicitVia = descriptor.cli.discovery.via;
|
|
264
|
+
if (explicitVia !== void 0) {
|
|
265
|
+
assert(
|
|
266
|
+
explicitVia === "none" || explicitVia === "acp" || explicitVia === "command" || explicitVia === "static",
|
|
267
|
+
`Unsupported CLI discovery mode "${String(explicitVia)}" for provider "${providerId}".`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
263
270
|
const launch = descriptor.cli.discovery.acp?.launch ? (() => {
|
|
264
271
|
const contextMode = descriptor.cli.discovery?.acp?.launch?.contextMode ?? "workspace";
|
|
265
272
|
const skillsMode = descriptor.cli.discovery?.acp?.launch?.skillsMode ?? "default";
|
|
@@ -280,7 +287,7 @@ function normalizeTransport(providerId, input) {
|
|
|
280
287
|
methodId: descriptor.cli.discovery.acp.auth.methodId.trim(),
|
|
281
288
|
params: descriptor.cli.discovery.acp.auth.params ?? {}
|
|
282
289
|
} : void 0;
|
|
283
|
-
const acp =
|
|
290
|
+
const acp = descriptor.cli.discovery.acp ? {
|
|
284
291
|
...descriptor.cli.discovery.acp?.providerId?.trim() ? { providerId: descriptor.cli.discovery.acp.providerId.trim() } : {},
|
|
285
292
|
...descriptor.cli.discovery.acp?.transportId?.trim() ? {
|
|
286
293
|
transportId: descriptor.cli.discovery.acp.transportId.trim()
|
|
@@ -288,9 +295,55 @@ function normalizeTransport(providerId, input) {
|
|
|
288
295
|
...auth ? { auth } : {},
|
|
289
296
|
...launch ? { launch } : {}
|
|
290
297
|
} : void 0;
|
|
298
|
+
const commandInput = descriptor.cli.discovery.command;
|
|
299
|
+
const command = commandInput ? (() => {
|
|
300
|
+
assert(
|
|
301
|
+
Array.isArray(commandInput.argsTemplate) && commandInput.argsTemplate.length > 0,
|
|
302
|
+
`CLI discovery command.argsTemplate must be a non-empty array for provider "${providerId}".`
|
|
303
|
+
);
|
|
304
|
+
const cmdParserInput = commandInput.parser ?? { kind: "text" };
|
|
305
|
+
const cmdParser = cmdParserInput.kind === "text" ? {
|
|
306
|
+
kind: "text",
|
|
307
|
+
...cmdParserInput.trim === false ? { trim: false } : {},
|
|
308
|
+
...cmdParserInput.stripAnsi === true ? { stripAnsi: true } : {}
|
|
309
|
+
} : { kind: cmdParserInput.kind };
|
|
310
|
+
if (cmdParser.kind !== "text") {
|
|
311
|
+
assert(
|
|
312
|
+
Boolean(commandInput.models?.idPath?.trim()),
|
|
313
|
+
`CLI discovery command.models.idPath is required for a ${cmdParser.kind} parser for provider "${providerId}".`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const m = commandInput.models;
|
|
317
|
+
const models = m?.idPath?.trim() ? {
|
|
318
|
+
...m.path?.trim() ? { path: m.path.trim() } : {},
|
|
319
|
+
idPath: m.idPath.trim(),
|
|
320
|
+
...m.namePath?.trim() ? { namePath: m.namePath.trim() } : {},
|
|
321
|
+
...m.descriptionPath?.trim() ? { descriptionPath: m.descriptionPath.trim() } : {},
|
|
322
|
+
...m.contextLengthPath?.trim() ? { contextLengthPath: m.contextLengthPath.trim() } : {}
|
|
323
|
+
} : void 0;
|
|
324
|
+
return {
|
|
325
|
+
...commandInput.command?.trim() ? { command: commandInput.command.trim() } : {},
|
|
326
|
+
argsTemplate: commandInput.argsTemplate.map((part) => String(part)),
|
|
327
|
+
parser: cmdParser,
|
|
328
|
+
...models ? { models } : {}
|
|
329
|
+
};
|
|
330
|
+
})() : void 0;
|
|
331
|
+
assert(
|
|
332
|
+
explicitVia !== "command" || command !== void 0,
|
|
333
|
+
`CLI discovery via:"command" requires a discovery.command block for provider "${providerId}".`
|
|
334
|
+
);
|
|
335
|
+
const fallback = descriptor.cli.discovery.fallback;
|
|
336
|
+
if (fallback !== void 0) {
|
|
337
|
+
assert(
|
|
338
|
+
fallback === "static" || fallback === "none",
|
|
339
|
+
`Unsupported CLI discovery fallback "${String(fallback)}" for provider "${providerId}".`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
291
342
|
return {
|
|
292
|
-
via,
|
|
293
|
-
...acp ? { acp } : {}
|
|
343
|
+
...explicitVia !== void 0 ? { via: explicitVia } : {},
|
|
344
|
+
...acp ? { acp } : {},
|
|
345
|
+
...command ? { command } : {},
|
|
346
|
+
...fallback !== void 0 ? { fallback } : {}
|
|
294
347
|
};
|
|
295
348
|
})();
|
|
296
349
|
return {
|
|
@@ -1721,6 +1774,25 @@ function resolveModelContextWindow(input) {
|
|
|
1721
1774
|
const fallback = isUsableContextLength(input.defaultContextWindow) ? input.defaultContextWindow : DEFAULT_CONTEXT_WINDOW;
|
|
1722
1775
|
return { contextWindow: fallback, source: "default" };
|
|
1723
1776
|
}
|
|
1777
|
+
function fillConfiguredContextLength(route, models) {
|
|
1778
|
+
if (!isUsableContextLength(route.contextWindow)) {
|
|
1779
|
+
return models;
|
|
1780
|
+
}
|
|
1781
|
+
const configured = route.contextWindow;
|
|
1782
|
+
return models.map((entry) => {
|
|
1783
|
+
if (isUsableContextLength(entry.contextLength)) {
|
|
1784
|
+
return entry;
|
|
1785
|
+
}
|
|
1786
|
+
if (route.model !== void 0 && entry.modelId !== route.model) {
|
|
1787
|
+
return entry;
|
|
1788
|
+
}
|
|
1789
|
+
return {
|
|
1790
|
+
...entry,
|
|
1791
|
+
contextLength: configured,
|
|
1792
|
+
metadata: { ...entry.metadata ?? {}, contextWindowSource: "configured" }
|
|
1793
|
+
};
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1724
1796
|
function modelContextCacheKey(input) {
|
|
1725
1797
|
if (typeof input === "string") {
|
|
1726
1798
|
return { key: `::${input}`, model: input };
|
|
@@ -2163,6 +2235,7 @@ function normalizeResult(route, output, attempts) {
|
|
|
2163
2235
|
attempts,
|
|
2164
2236
|
...output.toolCalls ? { toolCalls: output.toolCalls } : {},
|
|
2165
2237
|
...output.text !== void 0 ? { text: output.text } : {},
|
|
2238
|
+
...output.reasoning !== void 0 ? { reasoning: output.reasoning } : {},
|
|
2166
2239
|
...output.data !== void 0 ? { data: output.data } : {},
|
|
2167
2240
|
...output.usage !== void 0 ? { usage: output.usage } : {}
|
|
2168
2241
|
};
|
|
@@ -4695,18 +4768,19 @@ function canonicalGeminiImageModelId(modelId) {
|
|
|
4695
4768
|
}
|
|
4696
4769
|
function buildModelCatalog(route, availableModels, currentModelId) {
|
|
4697
4770
|
const requestedModelId = route.model;
|
|
4698
|
-
const
|
|
4771
|
+
const filledModels = fillConfiguredContextLength(route, availableModels);
|
|
4772
|
+
const requestedModelAdvertised = filledModels.some(
|
|
4699
4773
|
(model) => model.modelId === requestedModelId
|
|
4700
4774
|
);
|
|
4701
4775
|
const canonicalModelId = route.provider === "gemini" ? canonicalGeminiImageModelId(requestedModelId) : void 0;
|
|
4702
|
-
const resolvedModelId = requestedModelAdvertised ? requestedModelId : canonicalModelId &&
|
|
4776
|
+
const resolvedModelId = requestedModelAdvertised ? requestedModelId : canonicalModelId && filledModels.some((model) => model.modelId === canonicalModelId) ? canonicalModelId : void 0;
|
|
4703
4777
|
return {
|
|
4704
4778
|
requestedModelId,
|
|
4705
4779
|
requestedModelAdvertised,
|
|
4706
4780
|
...canonicalModelId ? { canonicalModelId } : {},
|
|
4707
4781
|
...resolvedModelId ? { resolvedModelId } : {},
|
|
4708
4782
|
...currentModelId ? { currentModelId } : {},
|
|
4709
|
-
availableModels
|
|
4783
|
+
availableModels: filledModels
|
|
4710
4784
|
};
|
|
4711
4785
|
}
|
|
4712
4786
|
function parseOpenAiModelCatalog(route, payload) {
|
|
@@ -5521,6 +5595,9 @@ function extractText(value) {
|
|
|
5521
5595
|
return [part];
|
|
5522
5596
|
}
|
|
5523
5597
|
if (part && typeof part === "object") {
|
|
5598
|
+
if (part.thought === true) {
|
|
5599
|
+
return [];
|
|
5600
|
+
}
|
|
5524
5601
|
const candidate = part.text;
|
|
5525
5602
|
if (typeof candidate === "string") {
|
|
5526
5603
|
return [candidate];
|
|
@@ -5531,6 +5608,22 @@ function extractText(value) {
|
|
|
5531
5608
|
}
|
|
5532
5609
|
return "";
|
|
5533
5610
|
}
|
|
5611
|
+
function extractThoughts(value) {
|
|
5612
|
+
if (!Array.isArray(value)) {
|
|
5613
|
+
return void 0;
|
|
5614
|
+
}
|
|
5615
|
+
const thoughts = value.flatMap((part) => {
|
|
5616
|
+
if (part && typeof part === "object" && part.thought === true) {
|
|
5617
|
+
const candidate = part.text;
|
|
5618
|
+
if (typeof candidate === "string") {
|
|
5619
|
+
return [candidate];
|
|
5620
|
+
}
|
|
5621
|
+
}
|
|
5622
|
+
return [];
|
|
5623
|
+
});
|
|
5624
|
+
const joined = thoughts.join("\n").trim();
|
|
5625
|
+
return joined.length > 0 ? joined : void 0;
|
|
5626
|
+
}
|
|
5534
5627
|
function imageAttachmentsFromPayload(payload, ...sources) {
|
|
5535
5628
|
return extractImagePayloads(payload, ...sources).map(
|
|
5536
5629
|
(item) => preparePortableFile(item)
|
|
@@ -6073,8 +6166,11 @@ async function runGemini(fetchImpl, context) {
|
|
|
6073
6166
|
throw classifyApiError(provider, response, payload);
|
|
6074
6167
|
}
|
|
6075
6168
|
const data = payload;
|
|
6169
|
+
const geminiThoughts = extractThoughts(data.candidates?.[0]?.content?.parts);
|
|
6076
6170
|
return {
|
|
6077
6171
|
text: extractText(data.candidates?.[0]?.content?.parts),
|
|
6172
|
+
// The model's `{ thought: true }` parts, separated from the answer (consumers route this to a thinking UI).
|
|
6173
|
+
...geminiThoughts ? { reasoning: geminiThoughts } : {},
|
|
6078
6174
|
data,
|
|
6079
6175
|
...geminiToolCallsFromPayload(data).length > 0 ? { toolCalls: geminiToolCallsFromPayload(data) } : {},
|
|
6080
6176
|
...(() => {
|
|
@@ -7530,6 +7626,10 @@ var AcpConnection = class {
|
|
|
7530
7626
|
`ACP route "${context.route.id}" did not return an ACP model catalog in session/new.`
|
|
7531
7627
|
);
|
|
7532
7628
|
}
|
|
7629
|
+
catalog.availableModels = fillConfiguredContextLength(
|
|
7630
|
+
context.route,
|
|
7631
|
+
catalog.availableModels
|
|
7632
|
+
);
|
|
7533
7633
|
return catalog;
|
|
7534
7634
|
} finally {
|
|
7535
7635
|
this.bumpIdleTimer();
|
|
@@ -8606,14 +8706,26 @@ function normalizeCliDiscoveryAcpSource(route, discovery) {
|
|
|
8606
8706
|
};
|
|
8607
8707
|
}
|
|
8608
8708
|
function resolveCliDiscoverySource(route) {
|
|
8609
|
-
const
|
|
8610
|
-
|
|
8611
|
-
|
|
8709
|
+
const discovery = route.transport.cli?.discovery;
|
|
8710
|
+
const hasModels = (route.advertisedModels?.length ?? 0) > 0;
|
|
8711
|
+
const hasAcpDefault = Boolean(defaultCliDiscoveryTransportIdForRoute(route));
|
|
8712
|
+
const via = discovery?.via ?? (discovery?.command ? "command" : hasAcpDefault ? "acp" : hasModels ? "static" : "none");
|
|
8713
|
+
switch (via) {
|
|
8714
|
+
case "none":
|
|
8715
|
+
return { via: "none" };
|
|
8716
|
+
case "static":
|
|
8717
|
+
return { via: "static" };
|
|
8718
|
+
case "command": {
|
|
8719
|
+
const command = discovery?.command;
|
|
8720
|
+
if (!command) {
|
|
8721
|
+
return { via: "none" };
|
|
8722
|
+
}
|
|
8723
|
+
const fallback = discovery?.fallback ?? (hasModels ? "static" : "none");
|
|
8724
|
+
return { via: "command", command, fallback };
|
|
8725
|
+
}
|
|
8726
|
+
default:
|
|
8727
|
+
return normalizeCliDiscoveryAcpSource(route, discovery?.acp);
|
|
8612
8728
|
}
|
|
8613
|
-
return normalizeCliDiscoveryAcpSource(
|
|
8614
|
-
route,
|
|
8615
|
-
route.transport.cli?.discovery?.acp
|
|
8616
|
-
);
|
|
8617
8729
|
}
|
|
8618
8730
|
function createCliDiscoveryAcpRoute(route) {
|
|
8619
8731
|
const discovery = resolveCliDiscoverySource(route);
|
|
@@ -8722,6 +8834,42 @@ async function buildCliInvocation(context, options) {
|
|
|
8722
8834
|
parameterKeys
|
|
8723
8835
|
};
|
|
8724
8836
|
}
|
|
8837
|
+
function buildCliDiscoveryInvocation(route, command, options) {
|
|
8838
|
+
const commandLine = command.command?.trim() ? command.command : resolveCliCommand(route, options);
|
|
8839
|
+
const resolved = splitCommandLine2(commandLine);
|
|
8840
|
+
const parameterKeys = [];
|
|
8841
|
+
const args = [
|
|
8842
|
+
...resolved.args,
|
|
8843
|
+
...command.argsTemplate.map((part) => {
|
|
8844
|
+
if (part === "{model}") {
|
|
8845
|
+
parameterKeys.push("model");
|
|
8846
|
+
return route.model;
|
|
8847
|
+
}
|
|
8848
|
+
if (part.startsWith("--")) {
|
|
8849
|
+
parameterKeys.push(part);
|
|
8850
|
+
}
|
|
8851
|
+
return part;
|
|
8852
|
+
})
|
|
8853
|
+
];
|
|
8854
|
+
return {
|
|
8855
|
+
command: resolved.command,
|
|
8856
|
+
args,
|
|
8857
|
+
cwd: path2.resolve(options?.cwd ?? process.cwd()),
|
|
8858
|
+
env: buildCliEnvironment(options),
|
|
8859
|
+
parameterKeys
|
|
8860
|
+
};
|
|
8861
|
+
}
|
|
8862
|
+
function buildStaticCliCatalog(route) {
|
|
8863
|
+
const models = route.advertisedModels.map((id) => ({
|
|
8864
|
+
modelId: id,
|
|
8865
|
+
name: id
|
|
8866
|
+
}));
|
|
8867
|
+
return buildModelCatalog(
|
|
8868
|
+
route,
|
|
8869
|
+
models,
|
|
8870
|
+
currentModelIdForRoute(route, route.advertisedModels)
|
|
8871
|
+
);
|
|
8872
|
+
}
|
|
8725
8873
|
function statsToUsage(stats) {
|
|
8726
8874
|
if (!stats || typeof stats !== "object") {
|
|
8727
8875
|
return void 0;
|
|
@@ -8759,6 +8907,9 @@ function getValueByPath(value, dotPath) {
|
|
|
8759
8907
|
}
|
|
8760
8908
|
return current;
|
|
8761
8909
|
}
|
|
8910
|
+
function splitJsonlLines(stdout) {
|
|
8911
|
+
return stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
8912
|
+
}
|
|
8762
8913
|
function normalizeErrorMessage(value) {
|
|
8763
8914
|
if (typeof value === "string" && value.trim()) {
|
|
8764
8915
|
return value.trim();
|
|
@@ -8806,7 +8957,7 @@ function parseGenericJsonCli(stdout, parser) {
|
|
|
8806
8957
|
};
|
|
8807
8958
|
}
|
|
8808
8959
|
function parseGenericJsonlCli(stdout, parser) {
|
|
8809
|
-
const entries = stdout
|
|
8960
|
+
const entries = splitJsonlLines(stdout);
|
|
8810
8961
|
const errorValue = parser.error ? findJsonlSelection(entries, parser.error) : void 0;
|
|
8811
8962
|
const errorMessage = normalizeErrorMessage(errorValue);
|
|
8812
8963
|
if (errorMessage) {
|
|
@@ -8827,6 +8978,70 @@ function parseGenericJsonlCli(stdout, parser) {
|
|
|
8827
8978
|
data: entries
|
|
8828
8979
|
};
|
|
8829
8980
|
}
|
|
8981
|
+
var ANSI_ESCAPE_PATTERN = /\[[0-?]*[ -/]*[@-~]/g;
|
|
8982
|
+
function parseTextCli(stdout, parser) {
|
|
8983
|
+
let text = stdout;
|
|
8984
|
+
if (parser.stripAnsi) {
|
|
8985
|
+
text = text.replace(ANSI_ESCAPE_PATTERN, "");
|
|
8986
|
+
}
|
|
8987
|
+
if (parser.trim !== false) {
|
|
8988
|
+
text = text.trim();
|
|
8989
|
+
}
|
|
8990
|
+
if (!text) {
|
|
8991
|
+
throw new AiConnectError(
|
|
8992
|
+
"temporary_unavailable",
|
|
8993
|
+
"CLI text parser produced no output."
|
|
8994
|
+
);
|
|
8995
|
+
}
|
|
8996
|
+
return { text, data: stdout };
|
|
8997
|
+
}
|
|
8998
|
+
function modelInfoFromRecord(record, selector) {
|
|
8999
|
+
const rawId = getValueByPath(record, selector.idPath);
|
|
9000
|
+
const modelId = typeof rawId === "string" ? rawId.trim() : "";
|
|
9001
|
+
if (!modelId) {
|
|
9002
|
+
return void 0;
|
|
9003
|
+
}
|
|
9004
|
+
const rawName = selector.namePath ? getValueByPath(record, selector.namePath) : void 0;
|
|
9005
|
+
const name = typeof rawName === "string" && rawName.trim().length > 0 ? rawName : modelId;
|
|
9006
|
+
const rawDescription = selector.descriptionPath ? getValueByPath(record, selector.descriptionPath) : void 0;
|
|
9007
|
+
const description = typeof rawDescription === "string" && rawDescription.trim().length > 0 ? rawDescription : void 0;
|
|
9008
|
+
const rawContext = selector.contextLengthPath ? getValueByPath(record, selector.contextLengthPath) : void 0;
|
|
9009
|
+
const contextLength = typeof rawContext === "number" && Number.isFinite(rawContext) && rawContext > 0 ? Math.floor(rawContext) : void 0;
|
|
9010
|
+
return {
|
|
9011
|
+
modelId,
|
|
9012
|
+
name,
|
|
9013
|
+
...description ? { description } : {},
|
|
9014
|
+
...contextLength !== void 0 ? { contextLength } : {}
|
|
9015
|
+
};
|
|
9016
|
+
}
|
|
9017
|
+
function parseCliModelList(stdout, parser, selector) {
|
|
9018
|
+
if (parser.kind === "text") {
|
|
9019
|
+
let text = stdout;
|
|
9020
|
+
if (parser.stripAnsi) {
|
|
9021
|
+
text = text.replace(ANSI_ESCAPE_PATTERN, "");
|
|
9022
|
+
}
|
|
9023
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => ({ modelId: line, name: line }));
|
|
9024
|
+
}
|
|
9025
|
+
if (!selector) {
|
|
9026
|
+
throw new AiConnectError(
|
|
9027
|
+
"validation_error",
|
|
9028
|
+
`CLI ${parser.kind} discovery parser requires a models selector with idPath.`
|
|
9029
|
+
);
|
|
9030
|
+
}
|
|
9031
|
+
const records = parser.kind === "jsonl" ? splitJsonlLines(stdout) : (() => {
|
|
9032
|
+
const payload = JSON.parse(stdout);
|
|
9033
|
+
const arr = selector.path ? getValueByPath(payload, selector.path) : payload;
|
|
9034
|
+
return Array.isArray(arr) ? arr : [];
|
|
9035
|
+
})();
|
|
9036
|
+
const models = [];
|
|
9037
|
+
for (const record of records) {
|
|
9038
|
+
const info = modelInfoFromRecord(record, selector);
|
|
9039
|
+
if (info) {
|
|
9040
|
+
models.push(info);
|
|
9041
|
+
}
|
|
9042
|
+
}
|
|
9043
|
+
return models;
|
|
9044
|
+
}
|
|
8830
9045
|
function parseGeminiCli(stdout) {
|
|
8831
9046
|
const payload = JSON.parse(stdout);
|
|
8832
9047
|
if (typeof payload.error === "string") {
|
|
@@ -8898,8 +9113,7 @@ function parseClaudeCli(stdout) {
|
|
|
8898
9113
|
};
|
|
8899
9114
|
}
|
|
8900
9115
|
function parseCodexCli(stdout, outputFileContent) {
|
|
8901
|
-
const
|
|
8902
|
-
const events = lines.map((line) => JSON.parse(line));
|
|
9116
|
+
const events = splitJsonlLines(stdout);
|
|
8903
9117
|
let text = outputFileContent?.trim() || void 0;
|
|
8904
9118
|
let usage;
|
|
8905
9119
|
for (const event of events) {
|
|
@@ -8957,7 +9171,14 @@ function parseCliResult(route, result, outputFileContent) {
|
|
|
8957
9171
|
`CLI route "${route.id}" declared a parser override but normalization did not preserve it.`
|
|
8958
9172
|
);
|
|
8959
9173
|
}
|
|
8960
|
-
|
|
9174
|
+
switch (parser.kind) {
|
|
9175
|
+
case "text":
|
|
9176
|
+
return parseTextCli(result.stdout, parser);
|
|
9177
|
+
case "json":
|
|
9178
|
+
return parseGenericJsonCli(stdout, parser);
|
|
9179
|
+
default:
|
|
9180
|
+
return parseGenericJsonlCli(stdout, parser);
|
|
9181
|
+
}
|
|
8961
9182
|
}
|
|
8962
9183
|
switch (cliOptions.preset) {
|
|
8963
9184
|
case "gemini":
|
|
@@ -9060,6 +9281,88 @@ async function cleanupCliInvocation(invocation) {
|
|
|
9060
9281
|
}
|
|
9061
9282
|
function createCliTransportManager(options) {
|
|
9062
9283
|
return {
|
|
9284
|
+
async discoverModels(context) {
|
|
9285
|
+
const source = resolveCliDiscoverySource(context.route);
|
|
9286
|
+
if (source.via === "none") {
|
|
9287
|
+
throw new AiConnectError(
|
|
9288
|
+
"not_supported",
|
|
9289
|
+
`CLI transport "${context.route.transport.id}" does not support model discovery (no list command, no ACP sidecar, and no configured models[]).`
|
|
9290
|
+
);
|
|
9291
|
+
}
|
|
9292
|
+
if (source.via === "acp") {
|
|
9293
|
+
throw new AiConnectError(
|
|
9294
|
+
"not_supported",
|
|
9295
|
+
`CLI route "${context.route.id}" resolves discovery via acp; dispatch to the acp discovery route instead.`
|
|
9296
|
+
);
|
|
9297
|
+
}
|
|
9298
|
+
if (source.via === "static") {
|
|
9299
|
+
return buildStaticCliCatalog(context.route);
|
|
9300
|
+
}
|
|
9301
|
+
const phases = [];
|
|
9302
|
+
const invocation = buildCliDiscoveryInvocation(
|
|
9303
|
+
context.route,
|
|
9304
|
+
source.command,
|
|
9305
|
+
options
|
|
9306
|
+
);
|
|
9307
|
+
context.telemetry?.captureTransport({
|
|
9308
|
+
protocol: "cli",
|
|
9309
|
+
endpoint: invocation.command,
|
|
9310
|
+
method: "process",
|
|
9311
|
+
bodyKeys: ["argv"],
|
|
9312
|
+
parameterKeys: invocation.parameterKeys,
|
|
9313
|
+
phases,
|
|
9314
|
+
stream: false
|
|
9315
|
+
});
|
|
9316
|
+
try {
|
|
9317
|
+
let models;
|
|
9318
|
+
try {
|
|
9319
|
+
const execution = await executeCliInvocation(
|
|
9320
|
+
invocation,
|
|
9321
|
+
options?.timeoutMs ?? 6e4,
|
|
9322
|
+
phases,
|
|
9323
|
+
context.abort.signal
|
|
9324
|
+
);
|
|
9325
|
+
if (execution.exitCode !== 0 || !execution.stdout.trim()) {
|
|
9326
|
+
throw new AiConnectError(
|
|
9327
|
+
"temporary_unavailable",
|
|
9328
|
+
execution.stderr.trim() || `CLI discovery command for "${context.route.transport.id}" exited with code ${execution.exitCode ?? "null"}.`
|
|
9329
|
+
);
|
|
9330
|
+
}
|
|
9331
|
+
models = parseCliModelList(
|
|
9332
|
+
execution.stdout,
|
|
9333
|
+
source.command.parser,
|
|
9334
|
+
source.command.models
|
|
9335
|
+
);
|
|
9336
|
+
} catch (error) {
|
|
9337
|
+
if (error instanceof AiConnectError && error.code === "aborted") {
|
|
9338
|
+
throw error;
|
|
9339
|
+
}
|
|
9340
|
+
if (source.fallback === "static") {
|
|
9341
|
+
return buildStaticCliCatalog(context.route);
|
|
9342
|
+
}
|
|
9343
|
+
throw error;
|
|
9344
|
+
}
|
|
9345
|
+
if (models.length === 0) {
|
|
9346
|
+
if (source.fallback === "static") {
|
|
9347
|
+
return buildStaticCliCatalog(context.route);
|
|
9348
|
+
}
|
|
9349
|
+
throw new AiConnectError(
|
|
9350
|
+
"temporary_unavailable",
|
|
9351
|
+
`CLI discovery command for "${context.route.transport.id}" returned no models.`
|
|
9352
|
+
);
|
|
9353
|
+
}
|
|
9354
|
+
return buildModelCatalog(
|
|
9355
|
+
context.route,
|
|
9356
|
+
models,
|
|
9357
|
+
currentModelIdForRoute(
|
|
9358
|
+
context.route,
|
|
9359
|
+
models.map((model) => model.modelId)
|
|
9360
|
+
)
|
|
9361
|
+
);
|
|
9362
|
+
} finally {
|
|
9363
|
+
await cleanupCliInvocation(invocation);
|
|
9364
|
+
}
|
|
9365
|
+
},
|
|
9063
9366
|
async runPrompt(context) {
|
|
9064
9367
|
const invocation = await buildCliInvocation(context, options);
|
|
9065
9368
|
const phases = [];
|
|
@@ -9594,17 +9897,21 @@ function createLocalRouteHandlers(options = {}) {
|
|
|
9594
9897
|
return cliTransport.runPrompt(context);
|
|
9595
9898
|
},
|
|
9596
9899
|
async discoverModels(context) {
|
|
9597
|
-
const
|
|
9598
|
-
if (
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9900
|
+
const source = resolveCliDiscoverySource(context.route);
|
|
9901
|
+
if (source.via === "acp") {
|
|
9902
|
+
const discoveryRoute = createCliDiscoveryAcpRoute(context.route);
|
|
9903
|
+
if (!discoveryRoute) {
|
|
9904
|
+
throw new AiConnectError(
|
|
9905
|
+
"not_supported",
|
|
9906
|
+
`CLI route "${context.route.id}" does not define a model discovery backend.`
|
|
9907
|
+
);
|
|
9908
|
+
}
|
|
9909
|
+
return acpTransport.discoverModels({
|
|
9910
|
+
...context,
|
|
9911
|
+
route: discoveryRoute
|
|
9912
|
+
});
|
|
9603
9913
|
}
|
|
9604
|
-
return
|
|
9605
|
-
...context,
|
|
9606
|
-
route: discoveryRoute
|
|
9607
|
-
});
|
|
9914
|
+
return cliTransport.discoverModels(context);
|
|
9608
9915
|
},
|
|
9609
9916
|
async verify({ route, runtime }) {
|
|
9610
9917
|
try {
|