@vedmalex/ai-connect 0.2.1 → 0.5.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 +130 -67
- package/dist/browser/index.js +114 -104
- package/dist/browser/index.js.map +2 -2
- package/dist/bun/index.js +338 -412
- package/dist/bun/index.js.map +3 -3
- package/dist/bun/local.js +330 -374
- package/dist/bun/local.js.map +3 -3
- package/dist/node/index.js +338 -412
- package/dist/node/index.js.map +3 -3
- package/dist/node/local.js +330 -374
- package/dist/node/local.js.map +3 -3
- package/dist/types/acp-presets.d.ts.map +1 -1
- package/dist/types/acp.d.ts.map +1 -1
- package/dist/types/catalog.d.ts.map +1 -1
- package/dist/types/cli-presets.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/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 +86 -5
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/node/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 {
|
|
@@ -331,7 +384,7 @@ function normalizeTransport(providerId, input) {
|
|
|
331
384
|
};
|
|
332
385
|
}
|
|
333
386
|
if (descriptor.kind === "cli") {
|
|
334
|
-
const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "
|
|
387
|
+
const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "pi" ? "pi-cli" : "cli";
|
|
335
388
|
return {
|
|
336
389
|
kind: "cli",
|
|
337
390
|
id: descriptor.id?.trim() || inferredId2,
|
|
@@ -348,7 +401,7 @@ function normalizeTransport(providerId, input) {
|
|
|
348
401
|
...descriptor.baseUrl?.trim() ? { baseUrl: descriptor.baseUrl.trim() } : {}
|
|
349
402
|
};
|
|
350
403
|
}
|
|
351
|
-
const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" :
|
|
404
|
+
const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" : "acp";
|
|
352
405
|
const normalizedLaunch = descriptor.launch ? (() => {
|
|
353
406
|
const contextMode = descriptor.launch?.contextMode ?? "workspace";
|
|
354
407
|
const skillsMode = descriptor.launch?.skillsMode ?? "default";
|
|
@@ -1576,18 +1629,14 @@ var MODEL_REFERENCE = {
|
|
|
1576
1629
|
key: "gemini-3.1-flash-lite",
|
|
1577
1630
|
contextLength: 1048576
|
|
1578
1631
|
},
|
|
1632
|
+
"gemini-3.1-pro": { key: "gemini-3.1-pro", contextLength: 1048576 },
|
|
1579
1633
|
"gemini-3-pro": { key: "gemini-3-pro", contextLength: 2097152 },
|
|
1580
1634
|
"auto-gemini-3": { key: "auto-gemini-3", contextLength: 1048576 },
|
|
1581
1635
|
"gemini-2.5-flash": { key: "gemini-2.5-flash", contextLength: 1048576 },
|
|
1582
1636
|
"gemini-2.5-pro": { key: "gemini-2.5-pro", contextLength: 2097152 },
|
|
1583
1637
|
"gemini-2.0-flash": { key: "gemini-2.0-flash", contextLength: 1048576 },
|
|
1584
1638
|
"gemini-1.5-flash": { key: "gemini-1.5-flash", contextLength: 1048576 },
|
|
1585
|
-
"gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
|
|
1586
|
-
// --- Qwen (catalog: qwen3-coder-plus) ---
|
|
1587
|
-
"qwen3-coder-plus": { key: "qwen3-coder-plus", contextLength: 1048576 },
|
|
1588
|
-
"qwen3-coder": { key: "qwen3-coder", contextLength: 262144 },
|
|
1589
|
-
"qwen-max": { key: "qwen-max", contextLength: 32768 },
|
|
1590
|
-
"qwen-plus": { key: "qwen-plus", contextLength: 131072 }
|
|
1639
|
+
"gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
|
|
1591
1640
|
};
|
|
1592
1641
|
function normalizeModelKey(model) {
|
|
1593
1642
|
let key = model.trim().toLowerCase();
|
|
@@ -1721,6 +1770,25 @@ function resolveModelContextWindow(input) {
|
|
|
1721
1770
|
const fallback = isUsableContextLength(input.defaultContextWindow) ? input.defaultContextWindow : DEFAULT_CONTEXT_WINDOW;
|
|
1722
1771
|
return { contextWindow: fallback, source: "default" };
|
|
1723
1772
|
}
|
|
1773
|
+
function fillConfiguredContextLength(route, models) {
|
|
1774
|
+
if (!isUsableContextLength(route.contextWindow)) {
|
|
1775
|
+
return models;
|
|
1776
|
+
}
|
|
1777
|
+
const configured = route.contextWindow;
|
|
1778
|
+
return models.map((entry) => {
|
|
1779
|
+
if (isUsableContextLength(entry.contextLength)) {
|
|
1780
|
+
return entry;
|
|
1781
|
+
}
|
|
1782
|
+
if (route.model !== void 0 && entry.modelId !== route.model) {
|
|
1783
|
+
return entry;
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
...entry,
|
|
1787
|
+
contextLength: configured,
|
|
1788
|
+
metadata: { ...entry.metadata ?? {}, contextWindowSource: "configured" }
|
|
1789
|
+
};
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1724
1792
|
function modelContextCacheKey(input) {
|
|
1725
1793
|
if (typeof input === "string") {
|
|
1726
1794
|
return { key: `::${input}`, model: input };
|
|
@@ -4696,18 +4764,19 @@ function canonicalGeminiImageModelId(modelId) {
|
|
|
4696
4764
|
}
|
|
4697
4765
|
function buildModelCatalog(route, availableModels, currentModelId) {
|
|
4698
4766
|
const requestedModelId = route.model;
|
|
4699
|
-
const
|
|
4767
|
+
const filledModels = fillConfiguredContextLength(route, availableModels);
|
|
4768
|
+
const requestedModelAdvertised = filledModels.some(
|
|
4700
4769
|
(model) => model.modelId === requestedModelId
|
|
4701
4770
|
);
|
|
4702
4771
|
const canonicalModelId = route.provider === "gemini" ? canonicalGeminiImageModelId(requestedModelId) : void 0;
|
|
4703
|
-
const resolvedModelId = requestedModelAdvertised ? requestedModelId : canonicalModelId &&
|
|
4772
|
+
const resolvedModelId = requestedModelAdvertised ? requestedModelId : canonicalModelId && filledModels.some((model) => model.modelId === canonicalModelId) ? canonicalModelId : void 0;
|
|
4704
4773
|
return {
|
|
4705
4774
|
requestedModelId,
|
|
4706
4775
|
requestedModelAdvertised,
|
|
4707
4776
|
...canonicalModelId ? { canonicalModelId } : {},
|
|
4708
4777
|
...resolvedModelId ? { resolvedModelId } : {},
|
|
4709
4778
|
...currentModelId ? { currentModelId } : {},
|
|
4710
|
-
availableModels
|
|
4779
|
+
availableModels: filledModels
|
|
4711
4780
|
};
|
|
4712
4781
|
}
|
|
4713
4782
|
function parseOpenAiModelCatalog(route, payload) {
|
|
@@ -6247,8 +6316,6 @@ import { pathToFileURL } from "node:url";
|
|
|
6247
6316
|
var AI_CONNECT_DEFAULT_ACP_COMMANDS = {
|
|
6248
6317
|
"anthropic:claude-code-acp": "npx -y @agentclientprotocol/claude-agent-acp@^0.25.0",
|
|
6249
6318
|
"openai:codex-acp": "npx @zed-industries/codex-acp@^0.11.1",
|
|
6250
|
-
"gemini:gemini-acp": "gemini --acp",
|
|
6251
|
-
"qwen:qwen-acp": "qwen --acp",
|
|
6252
6319
|
"opencode:opencode-acp": "opencode acp"
|
|
6253
6320
|
};
|
|
6254
6321
|
|
|
@@ -6329,15 +6396,6 @@ function resolveHomeDir(env) {
|
|
|
6329
6396
|
function resolveXdgConfigHome(env) {
|
|
6330
6397
|
return env.XDG_CONFIG_HOME ?? path.join(resolveHomeDir(env), ".config");
|
|
6331
6398
|
}
|
|
6332
|
-
function resolveGeminiCliHome(env) {
|
|
6333
|
-
return env.GEMINI_CLI_HOME ?? resolveHomeDir(env);
|
|
6334
|
-
}
|
|
6335
|
-
function resolveGeminiDir(env) {
|
|
6336
|
-
return path.join(resolveGeminiCliHome(env), ".gemini");
|
|
6337
|
-
}
|
|
6338
|
-
function resolveQwenDir(env) {
|
|
6339
|
-
return path.join(resolveHomeDir(env), ".qwen");
|
|
6340
|
-
}
|
|
6341
6399
|
function resolveCodexHome(env) {
|
|
6342
6400
|
return env.CODEX_HOME ?? path.join(resolveHomeDir(env), ".codex");
|
|
6343
6401
|
}
|
|
@@ -6383,118 +6441,6 @@ async function removeIfExists(targetPath) {
|
|
|
6383
6441
|
}
|
|
6384
6442
|
});
|
|
6385
6443
|
}
|
|
6386
|
-
async function readJsonFile(filePath) {
|
|
6387
|
-
try {
|
|
6388
|
-
const raw = await fs.readFile(filePath, "utf8");
|
|
6389
|
-
const parsed = JSON.parse(raw);
|
|
6390
|
-
return isRecord2(parsed) ? parsed : void 0;
|
|
6391
|
-
} catch (error) {
|
|
6392
|
-
if (error instanceof SyntaxError || error.code === "ENOENT") {
|
|
6393
|
-
return void 0;
|
|
6394
|
-
}
|
|
6395
|
-
throw error;
|
|
6396
|
-
}
|
|
6397
|
-
}
|
|
6398
|
-
function mergeRecordValues(current, next) {
|
|
6399
|
-
const merged = { ...current };
|
|
6400
|
-
for (const [key, value] of Object.entries(next)) {
|
|
6401
|
-
const existing = merged[key];
|
|
6402
|
-
if (isRecord2(existing) && isRecord2(value)) {
|
|
6403
|
-
merged[key] = mergeRecordValues(existing, value);
|
|
6404
|
-
continue;
|
|
6405
|
-
}
|
|
6406
|
-
merged[key] = value;
|
|
6407
|
-
}
|
|
6408
|
-
return merged;
|
|
6409
|
-
}
|
|
6410
|
-
async function writeJsonFile(filePath, value) {
|
|
6411
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
6412
|
-
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
6413
|
-
`, "utf8");
|
|
6414
|
-
}
|
|
6415
|
-
async function prepareGeminiLaunchRuntime(base, launch) {
|
|
6416
|
-
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6417
|
-
return {
|
|
6418
|
-
...base,
|
|
6419
|
-
launch
|
|
6420
|
-
};
|
|
6421
|
-
}
|
|
6422
|
-
const sandbox = await createSandboxPaths("ai-connect-gemini-acp-");
|
|
6423
|
-
const originalGeminiDir = resolveGeminiDir(base.env);
|
|
6424
|
-
const sandboxGeminiDir = path.join(sandbox.root, ".gemini");
|
|
6425
|
-
const settingsPath = path.join(sandboxGeminiDir, "settings.json");
|
|
6426
|
-
const systemSettingsPath = path.join(sandbox.config, "gemini-settings.json");
|
|
6427
|
-
const systemDefaultsPath = path.join(
|
|
6428
|
-
sandbox.config,
|
|
6429
|
-
"gemini-system-defaults.json"
|
|
6430
|
-
);
|
|
6431
|
-
const isolatedCwd = launch.contextMode === "clean" || launch.skillsMode === "disabled" ? sandbox.root : base.cwd;
|
|
6432
|
-
await Promise.all(
|
|
6433
|
-
[
|
|
6434
|
-
"oauth_creds.json",
|
|
6435
|
-
"google_accounts.json",
|
|
6436
|
-
"mcp-oauth-tokens.json",
|
|
6437
|
-
"a2a-oauth-tokens.json",
|
|
6438
|
-
"installation_id"
|
|
6439
|
-
].map(
|
|
6440
|
-
(fileName) => maybeCopyFile(
|
|
6441
|
-
path.join(originalGeminiDir, fileName),
|
|
6442
|
-
path.join(sandboxGeminiDir, fileName)
|
|
6443
|
-
)
|
|
6444
|
-
)
|
|
6445
|
-
);
|
|
6446
|
-
const settings = mergeRecordValues(
|
|
6447
|
-
await readJsonFile(settingsPath) ?? {},
|
|
6448
|
-
{
|
|
6449
|
-
context: {
|
|
6450
|
-
fileName: "AI_CONNECT_CONTEXT_DISABLED.md",
|
|
6451
|
-
includeDirectoryTree: false,
|
|
6452
|
-
memoryBoundaryMarkers: [],
|
|
6453
|
-
includeDirectories: [],
|
|
6454
|
-
loadMemoryFromIncludeDirectories: false,
|
|
6455
|
-
discoveryMaxDirs: 0
|
|
6456
|
-
},
|
|
6457
|
-
admin: {
|
|
6458
|
-
mcp: {
|
|
6459
|
-
enabled: false,
|
|
6460
|
-
config: {},
|
|
6461
|
-
requiredConfig: {}
|
|
6462
|
-
},
|
|
6463
|
-
...launch.skillsMode === "disabled" ? {
|
|
6464
|
-
skills: {
|
|
6465
|
-
enabled: false
|
|
6466
|
-
}
|
|
6467
|
-
} : {}
|
|
6468
|
-
},
|
|
6469
|
-
...launch.skillsMode === "disabled" ? {
|
|
6470
|
-
skills: {
|
|
6471
|
-
enabled: false,
|
|
6472
|
-
disabled: []
|
|
6473
|
-
}
|
|
6474
|
-
} : {}
|
|
6475
|
-
}
|
|
6476
|
-
);
|
|
6477
|
-
await Promise.all([
|
|
6478
|
-
writeJsonFile(settingsPath, settings),
|
|
6479
|
-
writeJsonFile(systemSettingsPath, {}),
|
|
6480
|
-
writeJsonFile(systemDefaultsPath, {})
|
|
6481
|
-
]);
|
|
6482
|
-
return {
|
|
6483
|
-
commandLine: base.commandLine,
|
|
6484
|
-
cwd: isolatedCwd,
|
|
6485
|
-
env: {
|
|
6486
|
-
...base.env,
|
|
6487
|
-
HOME: sandbox.root,
|
|
6488
|
-
GEMINI_CLI_HOME: sandbox.root,
|
|
6489
|
-
GEMINI_CLI_SYSTEM_SETTINGS_PATH: systemSettingsPath,
|
|
6490
|
-
GEMINI_CLI_SYSTEM_DEFAULTS_PATH: systemDefaultsPath
|
|
6491
|
-
},
|
|
6492
|
-
launch,
|
|
6493
|
-
cleanup: async () => {
|
|
6494
|
-
await removeIfExists(sandbox.root);
|
|
6495
|
-
}
|
|
6496
|
-
};
|
|
6497
|
-
}
|
|
6498
6444
|
async function prepareOpenCodeLaunchRuntime(base, launch) {
|
|
6499
6445
|
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6500
6446
|
return {
|
|
@@ -6543,70 +6489,6 @@ async function prepareOpenCodeLaunchRuntime(base, launch) {
|
|
|
6543
6489
|
}
|
|
6544
6490
|
};
|
|
6545
6491
|
}
|
|
6546
|
-
async function prepareQwenLaunchRuntime(base, launch) {
|
|
6547
|
-
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6548
|
-
return {
|
|
6549
|
-
...base,
|
|
6550
|
-
launch
|
|
6551
|
-
};
|
|
6552
|
-
}
|
|
6553
|
-
const sandbox = await createSandboxPaths("ai-connect-qwen-acp-");
|
|
6554
|
-
const originalQwenDir = resolveQwenDir(base.env);
|
|
6555
|
-
const sandboxQwenDir = path.join(sandbox.home, ".qwen");
|
|
6556
|
-
const settingsPath = path.join(sandboxQwenDir, "settings.json");
|
|
6557
|
-
const systemSettingsPath = path.join(sandbox.config, "qwen-settings.json");
|
|
6558
|
-
const systemDefaultsPath = path.join(
|
|
6559
|
-
sandbox.config,
|
|
6560
|
-
"qwen-system-defaults.json"
|
|
6561
|
-
);
|
|
6562
|
-
await Promise.all(
|
|
6563
|
-
[
|
|
6564
|
-
"oauth_creds.json",
|
|
6565
|
-
"mcp-oauth-tokens.json",
|
|
6566
|
-
"installation_id",
|
|
6567
|
-
".env",
|
|
6568
|
-
"settings.json"
|
|
6569
|
-
].map(
|
|
6570
|
-
(fileName) => maybeCopyFile(
|
|
6571
|
-
path.join(originalQwenDir, fileName),
|
|
6572
|
-
path.join(sandboxQwenDir, fileName)
|
|
6573
|
-
)
|
|
6574
|
-
)
|
|
6575
|
-
);
|
|
6576
|
-
const settings = mergeRecordValues(
|
|
6577
|
-
await readJsonFile(settingsPath) ?? {},
|
|
6578
|
-
{
|
|
6579
|
-
context: {
|
|
6580
|
-
fileName: ["AI_CONNECT_CONTEXT_DISABLED.md"],
|
|
6581
|
-
includeDirectories: [],
|
|
6582
|
-
loadFromIncludeDirectories: false
|
|
6583
|
-
},
|
|
6584
|
-
model: {
|
|
6585
|
-
skipStartupContext: true
|
|
6586
|
-
}
|
|
6587
|
-
}
|
|
6588
|
-
);
|
|
6589
|
-
await Promise.all([
|
|
6590
|
-
writeJsonFile(settingsPath, settings),
|
|
6591
|
-
writeJsonFile(systemSettingsPath, {}),
|
|
6592
|
-
writeJsonFile(systemDefaultsPath, {})
|
|
6593
|
-
]);
|
|
6594
|
-
const isolatedCwd = launch.contextMode === "clean" || launch.skillsMode === "disabled" ? sandbox.root : base.cwd;
|
|
6595
|
-
return {
|
|
6596
|
-
commandLine: base.commandLine,
|
|
6597
|
-
cwd: isolatedCwd,
|
|
6598
|
-
env: {
|
|
6599
|
-
...base.env,
|
|
6600
|
-
HOME: sandbox.home,
|
|
6601
|
-
QWEN_CODE_SYSTEM_SETTINGS_PATH: systemSettingsPath,
|
|
6602
|
-
QWEN_CODE_SYSTEM_DEFAULTS_PATH: systemDefaultsPath
|
|
6603
|
-
},
|
|
6604
|
-
launch,
|
|
6605
|
-
cleanup: async () => {
|
|
6606
|
-
await removeIfExists(sandbox.root);
|
|
6607
|
-
}
|
|
6608
|
-
};
|
|
6609
|
-
}
|
|
6610
6492
|
async function prepareCodexLaunchRuntime(base, launch) {
|
|
6611
6493
|
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6612
6494
|
return {
|
|
@@ -6724,15 +6606,9 @@ async function prepareAcpLaunchRuntime(route, options, commandLine, cwdOverride)
|
|
|
6724
6606
|
...options?.env ?? {}
|
|
6725
6607
|
}
|
|
6726
6608
|
};
|
|
6727
|
-
if (route.transport.id === "gemini-acp") {
|
|
6728
|
-
return prepareGeminiLaunchRuntime(base, launch);
|
|
6729
|
-
}
|
|
6730
6609
|
if (route.transport.id === "opencode-acp") {
|
|
6731
6610
|
return prepareOpenCodeLaunchRuntime(base, launch);
|
|
6732
6611
|
}
|
|
6733
|
-
if (route.transport.id === "qwen-acp") {
|
|
6734
|
-
return prepareQwenLaunchRuntime(base, launch);
|
|
6735
|
-
}
|
|
6736
6612
|
if (route.transport.id === "codex-acp") {
|
|
6737
6613
|
return prepareCodexLaunchRuntime(base, launch);
|
|
6738
6614
|
}
|
|
@@ -7313,10 +7189,6 @@ function buildAcpLifecycle(route, authRequest, mode) {
|
|
|
7313
7189
|
steps.push("authenticate");
|
|
7314
7190
|
}
|
|
7315
7191
|
steps.push("session/new");
|
|
7316
|
-
if (mode === "prompt" && route.transport.id === "gemini-acp") {
|
|
7317
|
-
steps.push("session/set_model");
|
|
7318
|
-
keys.push("session/set_model.modelId");
|
|
7319
|
-
}
|
|
7320
7192
|
if (mode === "prompt") {
|
|
7321
7193
|
steps.push("session/prompt");
|
|
7322
7194
|
}
|
|
@@ -7452,16 +7324,6 @@ var AcpConnection = class {
|
|
|
7452
7324
|
);
|
|
7453
7325
|
}
|
|
7454
7326
|
const sessionId = session.sessionId;
|
|
7455
|
-
if (context.route.transport.id === "gemini-acp") {
|
|
7456
|
-
await measurePhase(
|
|
7457
|
-
transport.phases ?? [],
|
|
7458
|
-
"session/set_model",
|
|
7459
|
-
async () => this.request("session/set_model", {
|
|
7460
|
-
sessionId,
|
|
7461
|
-
modelId: context.route.model
|
|
7462
|
-
})
|
|
7463
|
-
);
|
|
7464
|
-
}
|
|
7465
7327
|
this.activePrompt = {
|
|
7466
7328
|
sessionId,
|
|
7467
7329
|
text: "",
|
|
@@ -7553,6 +7415,10 @@ var AcpConnection = class {
|
|
|
7553
7415
|
`ACP route "${context.route.id}" did not return an ACP model catalog in session/new.`
|
|
7554
7416
|
);
|
|
7555
7417
|
}
|
|
7418
|
+
catalog.availableModels = fillConfiguredContextLength(
|
|
7419
|
+
context.route,
|
|
7420
|
+
catalog.availableModels
|
|
7421
|
+
);
|
|
7556
7422
|
return catalog;
|
|
7557
7423
|
} finally {
|
|
7558
7424
|
this.bumpIdleTimer();
|
|
@@ -7970,9 +7836,6 @@ var AcpConnection = class {
|
|
|
7970
7836
|
});
|
|
7971
7837
|
}
|
|
7972
7838
|
};
|
|
7973
|
-
function isGeminiAcpFallbackCandidate(route, commandLine) {
|
|
7974
|
-
return route.transport.id === "gemini-acp" && commandLine.includes("--acp") && !commandLine.includes("--experimental-acp");
|
|
7975
|
-
}
|
|
7976
7839
|
function cacheKeyForConnection(route, runtime, options) {
|
|
7977
7840
|
const envEntries = Object.entries(options.env).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}=${value}`).join("");
|
|
7978
7841
|
return [
|
|
@@ -8219,18 +8082,7 @@ function createAcpTransportManager(options) {
|
|
|
8219
8082
|
}
|
|
8220
8083
|
async function drivePrompt(context, onDelta) {
|
|
8221
8084
|
const commandLine = resolveAcpCommand(context.route, options);
|
|
8222
|
-
|
|
8223
|
-
return await runWithCommand(context, commandLine, onDelta);
|
|
8224
|
-
} catch (error) {
|
|
8225
|
-
if (!isGeminiAcpFallbackCandidate(context.route, commandLine)) {
|
|
8226
|
-
throw error;
|
|
8227
|
-
}
|
|
8228
|
-
return await runWithCommand(
|
|
8229
|
-
context,
|
|
8230
|
-
commandLine.replace("--acp", "--experimental-acp"),
|
|
8231
|
-
onDelta
|
|
8232
|
-
);
|
|
8233
|
-
}
|
|
8085
|
+
return await runWithCommand(context, commandLine, onDelta);
|
|
8234
8086
|
}
|
|
8235
8087
|
return {
|
|
8236
8088
|
async runPrompt(context) {
|
|
@@ -8299,17 +8151,7 @@ function createAcpTransportManager(options) {
|
|
|
8299
8151
|
},
|
|
8300
8152
|
async discoverModels(context) {
|
|
8301
8153
|
const commandLine = resolveAcpCommand(context.route, options);
|
|
8302
|
-
|
|
8303
|
-
return await discoverWithCommand(context, commandLine);
|
|
8304
|
-
} catch (error) {
|
|
8305
|
-
if (!isGeminiAcpFallbackCandidate(context.route, commandLine)) {
|
|
8306
|
-
throw error;
|
|
8307
|
-
}
|
|
8308
|
-
return await discoverWithCommand(
|
|
8309
|
-
context,
|
|
8310
|
-
commandLine.replace("--acp", "--experimental-acp")
|
|
8311
|
-
);
|
|
8312
|
-
}
|
|
8154
|
+
return await discoverWithCommand(context, commandLine);
|
|
8313
8155
|
},
|
|
8314
8156
|
async dispose() {
|
|
8315
8157
|
const values = [...connectionPools.values()].flat();
|
|
@@ -8336,47 +8178,21 @@ import path2 from "node:path";
|
|
|
8336
8178
|
|
|
8337
8179
|
// src/cli-presets.ts
|
|
8338
8180
|
var AI_CONNECT_DEFAULT_CLI_PRESETS = {
|
|
8339
|
-
|
|
8340
|
-
id: "
|
|
8341
|
-
label: "
|
|
8342
|
-
command: "
|
|
8343
|
-
transportId: "
|
|
8344
|
-
options: {
|
|
8345
|
-
preset: "gemini",
|
|
8346
|
-
argsTemplate: ["-p", "{prompt}", "--output-format", "json", "--model", "{model}"],
|
|
8347
|
-
discovery: {
|
|
8348
|
-
via: "acp",
|
|
8349
|
-
acp: {
|
|
8350
|
-
transportId: "gemini-acp"
|
|
8351
|
-
}
|
|
8352
|
-
},
|
|
8353
|
-
parser: {
|
|
8354
|
-
kind: "json",
|
|
8355
|
-
textPath: "response",
|
|
8356
|
-
usagePath: "stats",
|
|
8357
|
-
errorPath: "error"
|
|
8358
|
-
}
|
|
8359
|
-
}
|
|
8360
|
-
},
|
|
8361
|
-
qwen: {
|
|
8362
|
-
id: "qwen",
|
|
8363
|
-
label: "Qwen CLI",
|
|
8364
|
-
command: "qwen",
|
|
8365
|
-
transportId: "qwen-cli",
|
|
8181
|
+
pi: {
|
|
8182
|
+
id: "pi",
|
|
8183
|
+
label: "pi (coding agent)",
|
|
8184
|
+
command: "pi",
|
|
8185
|
+
transportId: "pi-cli",
|
|
8366
8186
|
options: {
|
|
8367
|
-
preset: "
|
|
8368
|
-
|
|
8187
|
+
preset: "pi",
|
|
8188
|
+
// pi print mode emits the answer as plain text on stdout (no JSON wrapper),
|
|
8189
|
+
// and has no ACP mode — so it uses the raw `text` parser and no discovery sidecar.
|
|
8190
|
+
argsTemplate: ["--print", "--model", "{model}", "{prompt}"],
|
|
8369
8191
|
discovery: {
|
|
8370
|
-
via: "
|
|
8371
|
-
acp: {
|
|
8372
|
-
transportId: "qwen-acp"
|
|
8373
|
-
}
|
|
8192
|
+
via: "none"
|
|
8374
8193
|
},
|
|
8375
8194
|
parser: {
|
|
8376
|
-
kind: "
|
|
8377
|
-
textPath: "__preset__:qwen.result",
|
|
8378
|
-
usagePath: "__preset__:qwen.stats",
|
|
8379
|
-
errorPath: "__preset__:qwen.error"
|
|
8195
|
+
kind: "text"
|
|
8380
8196
|
}
|
|
8381
8197
|
}
|
|
8382
8198
|
},
|
|
@@ -8460,8 +8276,7 @@ var AI_CONNECT_DEFAULT_CLI_COMMANDS = {
|
|
|
8460
8276
|
"anthropic:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
8461
8277
|
"openclaude:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
8462
8278
|
"openai:codex-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.codex.command,
|
|
8463
|
-
"
|
|
8464
|
-
"qwen:qwen-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.qwen.command
|
|
8279
|
+
"pi:pi-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.pi.command
|
|
8465
8280
|
};
|
|
8466
8281
|
function getCliTransportPreset(presetId) {
|
|
8467
8282
|
return AI_CONNECT_DEFAULT_CLI_PRESETS[presetId];
|
|
@@ -8469,14 +8284,6 @@ function getCliTransportPreset(presetId) {
|
|
|
8469
8284
|
|
|
8470
8285
|
// src/cli.ts
|
|
8471
8286
|
var CLI_PRESET_ACP_DISCOVERY_DEFAULTS = {
|
|
8472
|
-
gemini: {
|
|
8473
|
-
transportId: "gemini-acp",
|
|
8474
|
-
providerId: "gemini"
|
|
8475
|
-
},
|
|
8476
|
-
qwen: {
|
|
8477
|
-
transportId: "qwen-acp",
|
|
8478
|
-
providerId: "qwen"
|
|
8479
|
-
},
|
|
8480
8287
|
claude: {
|
|
8481
8288
|
transportId: "claude-code-acp",
|
|
8482
8289
|
providerId: "anthropic"
|
|
@@ -8583,10 +8390,8 @@ function buildCliCwd(context, options) {
|
|
|
8583
8390
|
}
|
|
8584
8391
|
function defaultCliPresetIdForRoute(route) {
|
|
8585
8392
|
switch (route.transport.id) {
|
|
8586
|
-
case "
|
|
8587
|
-
return "
|
|
8588
|
-
case "qwen-cli":
|
|
8589
|
-
return "qwen";
|
|
8393
|
+
case "pi-cli":
|
|
8394
|
+
return "pi";
|
|
8590
8395
|
case "claude-cli":
|
|
8591
8396
|
return "claude";
|
|
8592
8397
|
case "openclaude-cli":
|
|
@@ -8629,14 +8434,26 @@ function normalizeCliDiscoveryAcpSource(route, discovery) {
|
|
|
8629
8434
|
};
|
|
8630
8435
|
}
|
|
8631
8436
|
function resolveCliDiscoverySource(route) {
|
|
8632
|
-
const
|
|
8633
|
-
|
|
8634
|
-
|
|
8437
|
+
const discovery = route.transport.cli?.discovery;
|
|
8438
|
+
const hasModels = (route.advertisedModels?.length ?? 0) > 0;
|
|
8439
|
+
const hasAcpDefault = Boolean(defaultCliDiscoveryTransportIdForRoute(route));
|
|
8440
|
+
const via = discovery?.via ?? (discovery?.command ? "command" : hasAcpDefault ? "acp" : hasModels ? "static" : "none");
|
|
8441
|
+
switch (via) {
|
|
8442
|
+
case "none":
|
|
8443
|
+
return { via: "none" };
|
|
8444
|
+
case "static":
|
|
8445
|
+
return { via: "static" };
|
|
8446
|
+
case "command": {
|
|
8447
|
+
const command = discovery?.command;
|
|
8448
|
+
if (!command) {
|
|
8449
|
+
return { via: "none" };
|
|
8450
|
+
}
|
|
8451
|
+
const fallback = discovery?.fallback ?? (hasModels ? "static" : "none");
|
|
8452
|
+
return { via: "command", command, fallback };
|
|
8453
|
+
}
|
|
8454
|
+
default:
|
|
8455
|
+
return normalizeCliDiscoveryAcpSource(route, discovery?.acp);
|
|
8635
8456
|
}
|
|
8636
|
-
return normalizeCliDiscoveryAcpSource(
|
|
8637
|
-
route,
|
|
8638
|
-
route.transport.cli?.discovery?.acp
|
|
8639
|
-
);
|
|
8640
8457
|
}
|
|
8641
8458
|
function createCliDiscoveryAcpRoute(route) {
|
|
8642
8459
|
const discovery = resolveCliDiscoverySource(route);
|
|
@@ -8745,6 +8562,42 @@ async function buildCliInvocation(context, options) {
|
|
|
8745
8562
|
parameterKeys
|
|
8746
8563
|
};
|
|
8747
8564
|
}
|
|
8565
|
+
function buildCliDiscoveryInvocation(route, command, options) {
|
|
8566
|
+
const commandLine = command.command?.trim() ? command.command : resolveCliCommand(route, options);
|
|
8567
|
+
const resolved = splitCommandLine2(commandLine);
|
|
8568
|
+
const parameterKeys = [];
|
|
8569
|
+
const args = [
|
|
8570
|
+
...resolved.args,
|
|
8571
|
+
...command.argsTemplate.map((part) => {
|
|
8572
|
+
if (part === "{model}") {
|
|
8573
|
+
parameterKeys.push("model");
|
|
8574
|
+
return route.model;
|
|
8575
|
+
}
|
|
8576
|
+
if (part.startsWith("--")) {
|
|
8577
|
+
parameterKeys.push(part);
|
|
8578
|
+
}
|
|
8579
|
+
return part;
|
|
8580
|
+
})
|
|
8581
|
+
];
|
|
8582
|
+
return {
|
|
8583
|
+
command: resolved.command,
|
|
8584
|
+
args,
|
|
8585
|
+
cwd: path2.resolve(options?.cwd ?? process.cwd()),
|
|
8586
|
+
env: buildCliEnvironment(options),
|
|
8587
|
+
parameterKeys
|
|
8588
|
+
};
|
|
8589
|
+
}
|
|
8590
|
+
function buildStaticCliCatalog(route) {
|
|
8591
|
+
const models = route.advertisedModels.map((id) => ({
|
|
8592
|
+
modelId: id,
|
|
8593
|
+
name: id
|
|
8594
|
+
}));
|
|
8595
|
+
return buildModelCatalog(
|
|
8596
|
+
route,
|
|
8597
|
+
models,
|
|
8598
|
+
currentModelIdForRoute(route, route.advertisedModels)
|
|
8599
|
+
);
|
|
8600
|
+
}
|
|
8748
8601
|
function statsToUsage(stats) {
|
|
8749
8602
|
if (!stats || typeof stats !== "object") {
|
|
8750
8603
|
return void 0;
|
|
@@ -8782,6 +8635,9 @@ function getValueByPath(value, dotPath) {
|
|
|
8782
8635
|
}
|
|
8783
8636
|
return current;
|
|
8784
8637
|
}
|
|
8638
|
+
function splitJsonlLines(stdout) {
|
|
8639
|
+
return stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
8640
|
+
}
|
|
8785
8641
|
function normalizeErrorMessage(value) {
|
|
8786
8642
|
if (typeof value === "string" && value.trim()) {
|
|
8787
8643
|
return value.trim();
|
|
@@ -8829,7 +8685,7 @@ function parseGenericJsonCli(stdout, parser) {
|
|
|
8829
8685
|
};
|
|
8830
8686
|
}
|
|
8831
8687
|
function parseGenericJsonlCli(stdout, parser) {
|
|
8832
|
-
const entries = stdout
|
|
8688
|
+
const entries = splitJsonlLines(stdout);
|
|
8833
8689
|
const errorValue = parser.error ? findJsonlSelection(entries, parser.error) : void 0;
|
|
8834
8690
|
const errorMessage = normalizeErrorMessage(errorValue);
|
|
8835
8691
|
if (errorMessage) {
|
|
@@ -8850,59 +8706,69 @@ function parseGenericJsonlCli(stdout, parser) {
|
|
|
8850
8706
|
data: entries
|
|
8851
8707
|
};
|
|
8852
8708
|
}
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8709
|
+
var ANSI_ESCAPE_PATTERN = /\[[0-?]*[ -/]*[@-~]/g;
|
|
8710
|
+
function parseTextCli(stdout, parser) {
|
|
8711
|
+
let text = stdout;
|
|
8712
|
+
if (parser.stripAnsi) {
|
|
8713
|
+
text = text.replace(ANSI_ESCAPE_PATTERN, "");
|
|
8714
|
+
}
|
|
8715
|
+
if (parser.trim !== false) {
|
|
8716
|
+
text = text.trim();
|
|
8857
8717
|
}
|
|
8858
|
-
if (
|
|
8718
|
+
if (!text) {
|
|
8859
8719
|
throw new AiConnectError(
|
|
8860
8720
|
"temporary_unavailable",
|
|
8861
|
-
"
|
|
8721
|
+
"CLI text parser produced no output."
|
|
8862
8722
|
);
|
|
8863
8723
|
}
|
|
8864
|
-
|
|
8724
|
+
return { text, data: stdout };
|
|
8725
|
+
}
|
|
8726
|
+
function modelInfoFromRecord(record, selector) {
|
|
8727
|
+
const rawId = getValueByPath(record, selector.idPath);
|
|
8728
|
+
const modelId = typeof rawId === "string" ? rawId.trim() : "";
|
|
8729
|
+
if (!modelId) {
|
|
8730
|
+
return void 0;
|
|
8731
|
+
}
|
|
8732
|
+
const rawName = selector.namePath ? getValueByPath(record, selector.namePath) : void 0;
|
|
8733
|
+
const name = typeof rawName === "string" && rawName.trim().length > 0 ? rawName : modelId;
|
|
8734
|
+
const rawDescription = selector.descriptionPath ? getValueByPath(record, selector.descriptionPath) : void 0;
|
|
8735
|
+
const description = typeof rawDescription === "string" && rawDescription.trim().length > 0 ? rawDescription : void 0;
|
|
8736
|
+
const rawContext = selector.contextLengthPath ? getValueByPath(record, selector.contextLengthPath) : void 0;
|
|
8737
|
+
const contextLength = typeof rawContext === "number" && Number.isFinite(rawContext) && rawContext > 0 ? Math.floor(rawContext) : void 0;
|
|
8865
8738
|
return {
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8739
|
+
modelId,
|
|
8740
|
+
name,
|
|
8741
|
+
...description ? { description } : {},
|
|
8742
|
+
...contextLength !== void 0 ? { contextLength } : {}
|
|
8869
8743
|
};
|
|
8870
8744
|
}
|
|
8871
|
-
function
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
"
|
|
8876
|
-
|
|
8877
|
-
);
|
|
8878
|
-
}
|
|
8879
|
-
const resultMessage = payload.find(
|
|
8880
|
-
(entry) => entry && typeof entry === "object" && entry.type === "result"
|
|
8881
|
-
);
|
|
8882
|
-
if (!resultMessage) {
|
|
8883
|
-
throw new AiConnectError(
|
|
8884
|
-
"temporary_unavailable",
|
|
8885
|
-
"Qwen CLI JSON output did not contain a result message."
|
|
8886
|
-
);
|
|
8745
|
+
function parseCliModelList(stdout, parser, selector) {
|
|
8746
|
+
if (parser.kind === "text") {
|
|
8747
|
+
let text = stdout;
|
|
8748
|
+
if (parser.stripAnsi) {
|
|
8749
|
+
text = text.replace(ANSI_ESCAPE_PATTERN, "");
|
|
8750
|
+
}
|
|
8751
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => ({ modelId: line, name: line }));
|
|
8887
8752
|
}
|
|
8888
|
-
if (
|
|
8753
|
+
if (!selector) {
|
|
8889
8754
|
throw new AiConnectError(
|
|
8890
|
-
"
|
|
8891
|
-
|
|
8755
|
+
"validation_error",
|
|
8756
|
+
`CLI ${parser.kind} discovery parser requires a models selector with idPath.`
|
|
8892
8757
|
);
|
|
8893
8758
|
}
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8759
|
+
const records = parser.kind === "jsonl" ? splitJsonlLines(stdout) : (() => {
|
|
8760
|
+
const payload = JSON.parse(stdout);
|
|
8761
|
+
const arr = selector.path ? getValueByPath(payload, selector.path) : payload;
|
|
8762
|
+
return Array.isArray(arr) ? arr : [];
|
|
8763
|
+
})();
|
|
8764
|
+
const models = [];
|
|
8765
|
+
for (const record of records) {
|
|
8766
|
+
const info = modelInfoFromRecord(record, selector);
|
|
8767
|
+
if (info) {
|
|
8768
|
+
models.push(info);
|
|
8769
|
+
}
|
|
8899
8770
|
}
|
|
8900
|
-
|
|
8901
|
-
return {
|
|
8902
|
-
text: resultMessage.result,
|
|
8903
|
-
...usage ? { usage } : {},
|
|
8904
|
-
data: payload
|
|
8905
|
-
};
|
|
8771
|
+
return models;
|
|
8906
8772
|
}
|
|
8907
8773
|
function parseClaudeCli(stdout) {
|
|
8908
8774
|
const payload = JSON.parse(stdout);
|
|
@@ -8921,8 +8787,7 @@ function parseClaudeCli(stdout) {
|
|
|
8921
8787
|
};
|
|
8922
8788
|
}
|
|
8923
8789
|
function parseCodexCli(stdout, outputFileContent) {
|
|
8924
|
-
const
|
|
8925
|
-
const events = lines.map((line) => JSON.parse(line));
|
|
8790
|
+
const events = splitJsonlLines(stdout);
|
|
8926
8791
|
let text = outputFileContent?.trim() || void 0;
|
|
8927
8792
|
let usage;
|
|
8928
8793
|
for (const event of events) {
|
|
@@ -8980,13 +8845,18 @@ function parseCliResult(route, result, outputFileContent) {
|
|
|
8980
8845
|
`CLI route "${route.id}" declared a parser override but normalization did not preserve it.`
|
|
8981
8846
|
);
|
|
8982
8847
|
}
|
|
8983
|
-
|
|
8848
|
+
switch (parser.kind) {
|
|
8849
|
+
case "text":
|
|
8850
|
+
return parseTextCli(result.stdout, parser);
|
|
8851
|
+
case "json":
|
|
8852
|
+
return parseGenericJsonCli(stdout, parser);
|
|
8853
|
+
default:
|
|
8854
|
+
return parseGenericJsonlCli(stdout, parser);
|
|
8855
|
+
}
|
|
8984
8856
|
}
|
|
8985
8857
|
switch (cliOptions.preset) {
|
|
8986
|
-
case "
|
|
8987
|
-
return
|
|
8988
|
-
case "qwen":
|
|
8989
|
-
return parseQwenCli(stdout);
|
|
8858
|
+
case "pi":
|
|
8859
|
+
return parseTextCli(stdout, { kind: "text" });
|
|
8990
8860
|
case "claude":
|
|
8991
8861
|
case "openclaude":
|
|
8992
8862
|
return parseClaudeCli(stdout);
|
|
@@ -9083,6 +8953,88 @@ async function cleanupCliInvocation(invocation) {
|
|
|
9083
8953
|
}
|
|
9084
8954
|
function createCliTransportManager(options) {
|
|
9085
8955
|
return {
|
|
8956
|
+
async discoverModels(context) {
|
|
8957
|
+
const source = resolveCliDiscoverySource(context.route);
|
|
8958
|
+
if (source.via === "none") {
|
|
8959
|
+
throw new AiConnectError(
|
|
8960
|
+
"not_supported",
|
|
8961
|
+
`CLI transport "${context.route.transport.id}" does not support model discovery (no list command, no ACP sidecar, and no configured models[]).`
|
|
8962
|
+
);
|
|
8963
|
+
}
|
|
8964
|
+
if (source.via === "acp") {
|
|
8965
|
+
throw new AiConnectError(
|
|
8966
|
+
"not_supported",
|
|
8967
|
+
`CLI route "${context.route.id}" resolves discovery via acp; dispatch to the acp discovery route instead.`
|
|
8968
|
+
);
|
|
8969
|
+
}
|
|
8970
|
+
if (source.via === "static") {
|
|
8971
|
+
return buildStaticCliCatalog(context.route);
|
|
8972
|
+
}
|
|
8973
|
+
const phases = [];
|
|
8974
|
+
const invocation = buildCliDiscoveryInvocation(
|
|
8975
|
+
context.route,
|
|
8976
|
+
source.command,
|
|
8977
|
+
options
|
|
8978
|
+
);
|
|
8979
|
+
context.telemetry?.captureTransport({
|
|
8980
|
+
protocol: "cli",
|
|
8981
|
+
endpoint: invocation.command,
|
|
8982
|
+
method: "process",
|
|
8983
|
+
bodyKeys: ["argv"],
|
|
8984
|
+
parameterKeys: invocation.parameterKeys,
|
|
8985
|
+
phases,
|
|
8986
|
+
stream: false
|
|
8987
|
+
});
|
|
8988
|
+
try {
|
|
8989
|
+
let models;
|
|
8990
|
+
try {
|
|
8991
|
+
const execution = await executeCliInvocation(
|
|
8992
|
+
invocation,
|
|
8993
|
+
options?.timeoutMs ?? 6e4,
|
|
8994
|
+
phases,
|
|
8995
|
+
context.abort.signal
|
|
8996
|
+
);
|
|
8997
|
+
if (execution.exitCode !== 0 || !execution.stdout.trim()) {
|
|
8998
|
+
throw new AiConnectError(
|
|
8999
|
+
"temporary_unavailable",
|
|
9000
|
+
execution.stderr.trim() || `CLI discovery command for "${context.route.transport.id}" exited with code ${execution.exitCode ?? "null"}.`
|
|
9001
|
+
);
|
|
9002
|
+
}
|
|
9003
|
+
models = parseCliModelList(
|
|
9004
|
+
execution.stdout,
|
|
9005
|
+
source.command.parser,
|
|
9006
|
+
source.command.models
|
|
9007
|
+
);
|
|
9008
|
+
} catch (error) {
|
|
9009
|
+
if (error instanceof AiConnectError && error.code === "aborted") {
|
|
9010
|
+
throw error;
|
|
9011
|
+
}
|
|
9012
|
+
if (source.fallback === "static") {
|
|
9013
|
+
return buildStaticCliCatalog(context.route);
|
|
9014
|
+
}
|
|
9015
|
+
throw error;
|
|
9016
|
+
}
|
|
9017
|
+
if (models.length === 0) {
|
|
9018
|
+
if (source.fallback === "static") {
|
|
9019
|
+
return buildStaticCliCatalog(context.route);
|
|
9020
|
+
}
|
|
9021
|
+
throw new AiConnectError(
|
|
9022
|
+
"temporary_unavailable",
|
|
9023
|
+
`CLI discovery command for "${context.route.transport.id}" returned no models.`
|
|
9024
|
+
);
|
|
9025
|
+
}
|
|
9026
|
+
return buildModelCatalog(
|
|
9027
|
+
context.route,
|
|
9028
|
+
models,
|
|
9029
|
+
currentModelIdForRoute(
|
|
9030
|
+
context.route,
|
|
9031
|
+
models.map((model) => model.modelId)
|
|
9032
|
+
)
|
|
9033
|
+
);
|
|
9034
|
+
} finally {
|
|
9035
|
+
await cleanupCliInvocation(invocation);
|
|
9036
|
+
}
|
|
9037
|
+
},
|
|
9086
9038
|
async runPrompt(context) {
|
|
9087
9039
|
const invocation = await buildCliInvocation(context, options);
|
|
9088
9040
|
const phases = [];
|
|
@@ -9617,17 +9569,21 @@ function createLocalRouteHandlers(options = {}) {
|
|
|
9617
9569
|
return cliTransport.runPrompt(context);
|
|
9618
9570
|
},
|
|
9619
9571
|
async discoverModels(context) {
|
|
9620
|
-
const
|
|
9621
|
-
if (
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9572
|
+
const source = resolveCliDiscoverySource(context.route);
|
|
9573
|
+
if (source.via === "acp") {
|
|
9574
|
+
const discoveryRoute = createCliDiscoveryAcpRoute(context.route);
|
|
9575
|
+
if (!discoveryRoute) {
|
|
9576
|
+
throw new AiConnectError(
|
|
9577
|
+
"not_supported",
|
|
9578
|
+
`CLI route "${context.route.id}" does not define a model discovery backend.`
|
|
9579
|
+
);
|
|
9580
|
+
}
|
|
9581
|
+
return acpTransport.discoverModels({
|
|
9582
|
+
...context,
|
|
9583
|
+
route: discoveryRoute
|
|
9584
|
+
});
|
|
9626
9585
|
}
|
|
9627
|
-
return
|
|
9628
|
-
...context,
|
|
9629
|
-
route: discoveryRoute
|
|
9630
|
-
});
|
|
9586
|
+
return cliTransport.discoverModels(context);
|
|
9631
9587
|
},
|
|
9632
9588
|
async verify({ route, runtime }) {
|
|
9633
9589
|
try {
|