@vedmalex/ai-connect 0.4.0 → 0.6.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 +63 -9
- package/dist/browser/index.js +202 -185
- package/dist/browser/index.js.map +3 -3
- package/dist/bun/index.js +333 -511
- package/dist/bun/index.js.map +4 -4
- package/dist/bun/local.js +325 -475
- package/dist/bun/local.js.map +4 -4
- package/dist/node/index.js +333 -511
- package/dist/node/index.js.map +4 -4
- package/dist/node/local.js +325 -475
- package/dist/node/local.js.map +4 -4
- 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.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/files.d.ts +6 -0
- package/dist/types/files.d.ts.map +1 -1
- package/dist/types/model-reference.d.ts.map +1 -1
- package/dist/types/types.d.ts +41 -1
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bun/index.js
CHANGED
|
@@ -1,3 +1,125 @@
|
|
|
1
|
+
// src/cli-presets.ts
|
|
2
|
+
var AI_CONNECT_DEFAULT_CLI_PRESETS = {
|
|
3
|
+
pi: {
|
|
4
|
+
id: "pi",
|
|
5
|
+
label: "pi (coding agent)",
|
|
6
|
+
command: "pi",
|
|
7
|
+
transportId: "pi-cli",
|
|
8
|
+
options: {
|
|
9
|
+
preset: "pi",
|
|
10
|
+
// pi print mode emits the answer as plain text on stdout (no JSON wrapper),
|
|
11
|
+
// and has no ACP mode — so it uses the raw `text` parser and no discovery sidecar.
|
|
12
|
+
// pi takes file/image attachments as separate `@<path>` argv tokens (REQ-024);
|
|
13
|
+
// {files} expands one `@<staged-path>` token per attachment. pi stages all
|
|
14
|
+
// categories (PDFs as-is, no extraction guarantee).
|
|
15
|
+
argsTemplate: ["--print", "--model", "{model}", "{files}", "{prompt}"],
|
|
16
|
+
discovery: {
|
|
17
|
+
via: "none"
|
|
18
|
+
},
|
|
19
|
+
parser: {
|
|
20
|
+
kind: "text"
|
|
21
|
+
},
|
|
22
|
+
fileInput: {
|
|
23
|
+
placement: "args",
|
|
24
|
+
perFileArgs: ["@{path}"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
claude: {
|
|
29
|
+
id: "claude",
|
|
30
|
+
label: "Claude CLI",
|
|
31
|
+
command: "claude",
|
|
32
|
+
transportId: "claude-cli",
|
|
33
|
+
options: {
|
|
34
|
+
preset: "claude",
|
|
35
|
+
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
36
|
+
discovery: {
|
|
37
|
+
via: "acp",
|
|
38
|
+
acp: {
|
|
39
|
+
transportId: "claude-code-acp"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
parser: {
|
|
43
|
+
kind: "json",
|
|
44
|
+
textPath: "__preset__:claude.result",
|
|
45
|
+
usagePath: "usage",
|
|
46
|
+
errorPath: "__preset__:claude.error"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
openclaude: {
|
|
51
|
+
id: "openclaude",
|
|
52
|
+
label: "OpenClaude CLI",
|
|
53
|
+
command: "openclaude",
|
|
54
|
+
transportId: "openclaude-cli",
|
|
55
|
+
options: {
|
|
56
|
+
preset: "openclaude",
|
|
57
|
+
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
58
|
+
parser: {
|
|
59
|
+
kind: "json",
|
|
60
|
+
textPath: "__preset__:claude.result",
|
|
61
|
+
usagePath: "usage",
|
|
62
|
+
errorPath: "__preset__:claude.error"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
codex: {
|
|
67
|
+
id: "codex",
|
|
68
|
+
label: "Codex CLI",
|
|
69
|
+
command: "codex",
|
|
70
|
+
transportId: "codex-cli",
|
|
71
|
+
options: {
|
|
72
|
+
preset: "codex",
|
|
73
|
+
// codex exec takes image attachments via `-i/--image <FILE>` flags
|
|
74
|
+
// (REQ-024); {files} expands a `--image <staged-path>` pair per image.
|
|
75
|
+
// Images only — a non-image attachment cleanly rejects pre-spawn.
|
|
76
|
+
argsTemplate: [
|
|
77
|
+
"exec",
|
|
78
|
+
"--json",
|
|
79
|
+
"--output-last-message",
|
|
80
|
+
"{output_file}",
|
|
81
|
+
"--model",
|
|
82
|
+
"{model}",
|
|
83
|
+
"{files}",
|
|
84
|
+
"{prompt}"
|
|
85
|
+
],
|
|
86
|
+
discovery: {
|
|
87
|
+
via: "acp",
|
|
88
|
+
acp: {
|
|
89
|
+
transportId: "codex-acp"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
fileInput: {
|
|
93
|
+
placement: "args",
|
|
94
|
+
perFileArgs: ["--image", "{path}"],
|
|
95
|
+
categories: ["image"]
|
|
96
|
+
},
|
|
97
|
+
parser: {
|
|
98
|
+
kind: "jsonl",
|
|
99
|
+
text: {
|
|
100
|
+
path: "__preset__:codex.text"
|
|
101
|
+
},
|
|
102
|
+
usage: {
|
|
103
|
+
path: "__preset__:codex.usage"
|
|
104
|
+
},
|
|
105
|
+
error: {
|
|
106
|
+
path: "__preset__:codex.error"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var AI_CONNECT_DEFAULT_CLI_COMMANDS = {
|
|
113
|
+
"anthropic:claude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.claude.command,
|
|
114
|
+
"anthropic:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
115
|
+
"openclaude:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
116
|
+
"openai:codex-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.codex.command,
|
|
117
|
+
"pi:pi-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.pi.command
|
|
118
|
+
};
|
|
119
|
+
function getCliTransportPreset(presetId) {
|
|
120
|
+
return AI_CONNECT_DEFAULT_CLI_PRESETS[presetId];
|
|
121
|
+
}
|
|
122
|
+
|
|
1
123
|
// src/errors.ts
|
|
2
124
|
var AiConnectError = class extends Error {
|
|
3
125
|
code;
|
|
@@ -345,9 +467,38 @@ function normalizeTransport(providerId, input) {
|
|
|
345
467
|
...fallback !== void 0 ? { fallback } : {}
|
|
346
468
|
};
|
|
347
469
|
})();
|
|
470
|
+
const fileInput = (() => {
|
|
471
|
+
const fi = descriptor.cli?.fileInput;
|
|
472
|
+
if (!fi) {
|
|
473
|
+
return void 0;
|
|
474
|
+
}
|
|
475
|
+
const validCategories = ["image", "document", "text", "other"];
|
|
476
|
+
if (fi.categories) {
|
|
477
|
+
for (const c of fi.categories) {
|
|
478
|
+
assert(
|
|
479
|
+
validCategories.includes(c),
|
|
480
|
+
`Unsupported CLI fileInput category "${String(c)}" for provider "${providerId}".`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const placement = fi.placement ?? "args";
|
|
485
|
+
assert(
|
|
486
|
+
placement === "args" || placement === "prompt",
|
|
487
|
+
`Unsupported CLI fileInput placement "${String(placement)}" for provider "${providerId}".`
|
|
488
|
+
);
|
|
489
|
+
return {
|
|
490
|
+
placement,
|
|
491
|
+
...fi.perFileArgs ? { perFileArgs: fi.perFileArgs.map((part) => String(part)) } : {},
|
|
492
|
+
...fi.mentionTemplate?.trim() ? { mentionTemplate: fi.mentionTemplate.trim() } : {},
|
|
493
|
+
...fi.separator !== void 0 ? { separator: fi.separator } : {},
|
|
494
|
+
...fi.categories ? { categories: [...fi.categories] } : {},
|
|
495
|
+
...fi.stagingDir?.trim() ? { stagingDir: fi.stagingDir.trim() } : {}
|
|
496
|
+
};
|
|
497
|
+
})();
|
|
348
498
|
return {
|
|
349
499
|
...normalized,
|
|
350
|
-
...discovery ? { discovery } : {}
|
|
500
|
+
...discovery ? { discovery } : {},
|
|
501
|
+
...fileInput ? { fileInput } : {}
|
|
351
502
|
};
|
|
352
503
|
})();
|
|
353
504
|
const normalizedAuth = descriptor.auth?.methodId?.trim() ? {
|
|
@@ -383,7 +534,7 @@ function normalizeTransport(providerId, input) {
|
|
|
383
534
|
};
|
|
384
535
|
}
|
|
385
536
|
if (descriptor.kind === "cli") {
|
|
386
|
-
const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "
|
|
537
|
+
const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "pi" ? "pi-cli" : "cli";
|
|
387
538
|
return {
|
|
388
539
|
kind: "cli",
|
|
389
540
|
id: descriptor.id?.trim() || inferredId2,
|
|
@@ -400,7 +551,7 @@ function normalizeTransport(providerId, input) {
|
|
|
400
551
|
...descriptor.baseUrl?.trim() ? { baseUrl: descriptor.baseUrl.trim() } : {}
|
|
401
552
|
};
|
|
402
553
|
}
|
|
403
|
-
const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" :
|
|
554
|
+
const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" : "acp";
|
|
404
555
|
const normalizedLaunch = descriptor.launch ? (() => {
|
|
405
556
|
const contextMode = descriptor.launch?.contextMode ?? "workspace";
|
|
406
557
|
const skillsMode = descriptor.launch?.skillsMode ?? "default";
|
|
@@ -435,7 +586,40 @@ function normalizeRuntime(transport, runtime) {
|
|
|
435
586
|
}
|
|
436
587
|
return "universal";
|
|
437
588
|
}
|
|
589
|
+
function cliPresetIdForTransportId(transportId) {
|
|
590
|
+
switch (transportId) {
|
|
591
|
+
case "pi-cli":
|
|
592
|
+
return "pi";
|
|
593
|
+
case "claude-cli":
|
|
594
|
+
return "claude";
|
|
595
|
+
case "openclaude-cli":
|
|
596
|
+
return "openclaude";
|
|
597
|
+
case "codex-cli":
|
|
598
|
+
return "codex";
|
|
599
|
+
default:
|
|
600
|
+
return void 0;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function cliFileInputCategories(transport) {
|
|
604
|
+
if (transport.kind !== "cli") {
|
|
605
|
+
return /* @__PURE__ */ new Set();
|
|
606
|
+
}
|
|
607
|
+
const presetId = transport.cli?.preset ?? cliPresetIdForTransportId(transport.id);
|
|
608
|
+
const presetFileInput = presetId ? AI_CONNECT_DEFAULT_CLI_PRESETS[presetId]?.options.fileInput : void 0;
|
|
609
|
+
const routeFileInput = transport.cli?.fileInput;
|
|
610
|
+
if (!presetFileInput && !routeFileInput) {
|
|
611
|
+
return /* @__PURE__ */ new Set();
|
|
612
|
+
}
|
|
613
|
+
const merged = {
|
|
614
|
+
...presetFileInput ?? {},
|
|
615
|
+
...routeFileInput ?? {}
|
|
616
|
+
};
|
|
617
|
+
return new Set(
|
|
618
|
+
merged.categories ?? ["image", "document", "text", "other"]
|
|
619
|
+
);
|
|
620
|
+
}
|
|
438
621
|
function defaultCapabilities(transport, runtime) {
|
|
622
|
+
const cliFileCategories = cliFileInputCategories(transport);
|
|
439
623
|
const localOnly = runtime === "local" || transport.kind === "acp" || transport.kind === "cli" || transport.kind === "server";
|
|
440
624
|
const browserSafe = !localOnly;
|
|
441
625
|
return {
|
|
@@ -450,10 +634,12 @@ function defaultCapabilities(transport, runtime) {
|
|
|
450
634
|
supportsClientToolExecution: transport.kind === "api",
|
|
451
635
|
// C6 / F-P-4: api routes now advertise document + image input parity with acp.
|
|
452
636
|
// The per-account `capabilities` override remains the escape hatch for
|
|
453
|
-
// text-only api proxies that must opt out.
|
|
454
|
-
|
|
637
|
+
// text-only api proxies that must opt out. REQ-024: a cli route advertises
|
|
638
|
+
// document/image input ONLY for the categories its fileInput actually stages
|
|
639
|
+
// (category-aware so e.g. an image-only cli cleanly rejects a PDF pre-spawn).
|
|
640
|
+
supportsFileUpload: transport.kind === "acp" || transport.kind === "api" || cliFileCategories.has("document"),
|
|
455
641
|
supportsFileOutput: transport.kind === "acp",
|
|
456
|
-
supportsImageInput: transport.kind === "acp" || transport.kind === "api",
|
|
642
|
+
supportsImageInput: transport.kind === "acp" || transport.kind === "api" || cliFileCategories.has("image"),
|
|
457
643
|
supportsImageOutput: transport.kind === "acp",
|
|
458
644
|
supportsProfileVerification: true,
|
|
459
645
|
supportsCredentialRotation: false,
|
|
@@ -1004,6 +1190,10 @@ function extension(value) {
|
|
|
1004
1190
|
}
|
|
1005
1191
|
return name.slice(index + 1).toLowerCase();
|
|
1006
1192
|
}
|
|
1193
|
+
function extensionForMime(mimeType) {
|
|
1194
|
+
const subtype = mimeType.split("/")[1]?.toLowerCase().replace(/[^a-z0-9.+-]/g, "");
|
|
1195
|
+
return subtype && subtype.length > 0 ? subtype : "bin";
|
|
1196
|
+
}
|
|
1007
1197
|
function isRemoteUrl(value) {
|
|
1008
1198
|
try {
|
|
1009
1199
|
const url = new URL(value);
|
|
@@ -1638,18 +1828,14 @@ var MODEL_REFERENCE = {
|
|
|
1638
1828
|
key: "gemini-3.1-flash-lite",
|
|
1639
1829
|
contextLength: 1048576
|
|
1640
1830
|
},
|
|
1831
|
+
"gemini-3.1-pro": { key: "gemini-3.1-pro", contextLength: 1048576 },
|
|
1641
1832
|
"gemini-3-pro": { key: "gemini-3-pro", contextLength: 2097152 },
|
|
1642
1833
|
"auto-gemini-3": { key: "auto-gemini-3", contextLength: 1048576 },
|
|
1643
1834
|
"gemini-2.5-flash": { key: "gemini-2.5-flash", contextLength: 1048576 },
|
|
1644
1835
|
"gemini-2.5-pro": { key: "gemini-2.5-pro", contextLength: 2097152 },
|
|
1645
1836
|
"gemini-2.0-flash": { key: "gemini-2.0-flash", contextLength: 1048576 },
|
|
1646
1837
|
"gemini-1.5-flash": { key: "gemini-1.5-flash", contextLength: 1048576 },
|
|
1647
|
-
"gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
|
|
1648
|
-
// --- Qwen (catalog: qwen3-coder-plus) ---
|
|
1649
|
-
"qwen3-coder-plus": { key: "qwen3-coder-plus", contextLength: 1048576 },
|
|
1650
|
-
"qwen3-coder": { key: "qwen3-coder", contextLength: 262144 },
|
|
1651
|
-
"qwen-max": { key: "qwen-max", contextLength: 32768 },
|
|
1652
|
-
"qwen-plus": { key: "qwen-plus", contextLength: 131072 }
|
|
1838
|
+
"gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
|
|
1653
1839
|
};
|
|
1654
1840
|
function normalizeModelKey(model) {
|
|
1655
1841
|
let key = model.trim().toLowerCase();
|
|
@@ -6343,144 +6529,9 @@ function createDefaultRouteHandlers(options = {}) {
|
|
|
6343
6529
|
var AI_CONNECT_DEFAULT_ACP_COMMANDS = {
|
|
6344
6530
|
"anthropic:claude-code-acp": "npx -y @agentclientprotocol/claude-agent-acp@^0.25.0",
|
|
6345
6531
|
"openai:codex-acp": "npx @zed-industries/codex-acp@^0.11.1",
|
|
6346
|
-
"gemini:gemini-acp": "gemini --acp",
|
|
6347
|
-
"qwen:qwen-acp": "qwen --acp",
|
|
6348
6532
|
"opencode:opencode-acp": "opencode acp"
|
|
6349
6533
|
};
|
|
6350
6534
|
|
|
6351
|
-
// src/cli-presets.ts
|
|
6352
|
-
var AI_CONNECT_DEFAULT_CLI_PRESETS = {
|
|
6353
|
-
gemini: {
|
|
6354
|
-
id: "gemini",
|
|
6355
|
-
label: "Gemini CLI",
|
|
6356
|
-
command: "gemini",
|
|
6357
|
-
transportId: "gemini-cli",
|
|
6358
|
-
options: {
|
|
6359
|
-
preset: "gemini",
|
|
6360
|
-
argsTemplate: ["-p", "{prompt}", "--output-format", "json", "--model", "{model}"],
|
|
6361
|
-
discovery: {
|
|
6362
|
-
via: "acp",
|
|
6363
|
-
acp: {
|
|
6364
|
-
transportId: "gemini-acp"
|
|
6365
|
-
}
|
|
6366
|
-
},
|
|
6367
|
-
parser: {
|
|
6368
|
-
kind: "json",
|
|
6369
|
-
textPath: "response",
|
|
6370
|
-
usagePath: "stats",
|
|
6371
|
-
errorPath: "error"
|
|
6372
|
-
}
|
|
6373
|
-
}
|
|
6374
|
-
},
|
|
6375
|
-
qwen: {
|
|
6376
|
-
id: "qwen",
|
|
6377
|
-
label: "Qwen CLI",
|
|
6378
|
-
command: "qwen",
|
|
6379
|
-
transportId: "qwen-cli",
|
|
6380
|
-
options: {
|
|
6381
|
-
preset: "qwen",
|
|
6382
|
-
argsTemplate: ["-p", "{prompt}", "--output-format", "json", "--model", "{model}"],
|
|
6383
|
-
discovery: {
|
|
6384
|
-
via: "acp",
|
|
6385
|
-
acp: {
|
|
6386
|
-
transportId: "qwen-acp"
|
|
6387
|
-
}
|
|
6388
|
-
},
|
|
6389
|
-
parser: {
|
|
6390
|
-
kind: "json",
|
|
6391
|
-
textPath: "__preset__:qwen.result",
|
|
6392
|
-
usagePath: "__preset__:qwen.stats",
|
|
6393
|
-
errorPath: "__preset__:qwen.error"
|
|
6394
|
-
}
|
|
6395
|
-
}
|
|
6396
|
-
},
|
|
6397
|
-
claude: {
|
|
6398
|
-
id: "claude",
|
|
6399
|
-
label: "Claude CLI",
|
|
6400
|
-
command: "claude",
|
|
6401
|
-
transportId: "claude-cli",
|
|
6402
|
-
options: {
|
|
6403
|
-
preset: "claude",
|
|
6404
|
-
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
6405
|
-
discovery: {
|
|
6406
|
-
via: "acp",
|
|
6407
|
-
acp: {
|
|
6408
|
-
transportId: "claude-code-acp"
|
|
6409
|
-
}
|
|
6410
|
-
},
|
|
6411
|
-
parser: {
|
|
6412
|
-
kind: "json",
|
|
6413
|
-
textPath: "__preset__:claude.result",
|
|
6414
|
-
usagePath: "usage",
|
|
6415
|
-
errorPath: "__preset__:claude.error"
|
|
6416
|
-
}
|
|
6417
|
-
}
|
|
6418
|
-
},
|
|
6419
|
-
openclaude: {
|
|
6420
|
-
id: "openclaude",
|
|
6421
|
-
label: "OpenClaude CLI",
|
|
6422
|
-
command: "openclaude",
|
|
6423
|
-
transportId: "openclaude-cli",
|
|
6424
|
-
options: {
|
|
6425
|
-
preset: "openclaude",
|
|
6426
|
-
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
6427
|
-
parser: {
|
|
6428
|
-
kind: "json",
|
|
6429
|
-
textPath: "__preset__:claude.result",
|
|
6430
|
-
usagePath: "usage",
|
|
6431
|
-
errorPath: "__preset__:claude.error"
|
|
6432
|
-
}
|
|
6433
|
-
}
|
|
6434
|
-
},
|
|
6435
|
-
codex: {
|
|
6436
|
-
id: "codex",
|
|
6437
|
-
label: "Codex CLI",
|
|
6438
|
-
command: "codex",
|
|
6439
|
-
transportId: "codex-cli",
|
|
6440
|
-
options: {
|
|
6441
|
-
preset: "codex",
|
|
6442
|
-
argsTemplate: [
|
|
6443
|
-
"exec",
|
|
6444
|
-
"--json",
|
|
6445
|
-
"--output-last-message",
|
|
6446
|
-
"{output_file}",
|
|
6447
|
-
"--model",
|
|
6448
|
-
"{model}",
|
|
6449
|
-
"{prompt}"
|
|
6450
|
-
],
|
|
6451
|
-
discovery: {
|
|
6452
|
-
via: "acp",
|
|
6453
|
-
acp: {
|
|
6454
|
-
transportId: "codex-acp"
|
|
6455
|
-
}
|
|
6456
|
-
},
|
|
6457
|
-
parser: {
|
|
6458
|
-
kind: "jsonl",
|
|
6459
|
-
text: {
|
|
6460
|
-
path: "__preset__:codex.text"
|
|
6461
|
-
},
|
|
6462
|
-
usage: {
|
|
6463
|
-
path: "__preset__:codex.usage"
|
|
6464
|
-
},
|
|
6465
|
-
error: {
|
|
6466
|
-
path: "__preset__:codex.error"
|
|
6467
|
-
}
|
|
6468
|
-
}
|
|
6469
|
-
}
|
|
6470
|
-
}
|
|
6471
|
-
};
|
|
6472
|
-
var AI_CONNECT_DEFAULT_CLI_COMMANDS = {
|
|
6473
|
-
"anthropic:claude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.claude.command,
|
|
6474
|
-
"anthropic:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
6475
|
-
"openclaude:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
6476
|
-
"openai:codex-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.codex.command,
|
|
6477
|
-
"gemini:gemini-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.gemini.command,
|
|
6478
|
-
"qwen:qwen-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.qwen.command
|
|
6479
|
-
};
|
|
6480
|
-
function getCliTransportPreset(presetId) {
|
|
6481
|
-
return AI_CONNECT_DEFAULT_CLI_PRESETS[presetId];
|
|
6482
|
-
}
|
|
6483
|
-
|
|
6484
6535
|
// src/server-presets.ts
|
|
6485
6536
|
var AI_CONNECT_DEFAULT_SERVER_PRESETS = {
|
|
6486
6537
|
opencode: {
|
|
@@ -6597,52 +6648,22 @@ var TEXT_PROVIDER_CATALOG = [
|
|
|
6597
6648
|
runtime: "universal",
|
|
6598
6649
|
defaultModel: "gemini-3.1-flash-lite",
|
|
6599
6650
|
defaultBaseUrl: ""
|
|
6600
|
-
},
|
|
6601
|
-
{
|
|
6602
|
-
providerId: "gemini",
|
|
6603
|
-
providerLabel: "Gemini",
|
|
6604
|
-
transportKind: "cli",
|
|
6605
|
-
transportId: "gemini-cli",
|
|
6606
|
-
transportLabel: "Gemini CLI",
|
|
6607
|
-
runtime: "local",
|
|
6608
|
-
defaultModel: "gemini-2.5-flash",
|
|
6609
|
-
defaultCommand: AI_CONNECT_DEFAULT_CLI_COMMANDS["gemini:gemini-cli"]
|
|
6610
|
-
},
|
|
6611
|
-
{
|
|
6612
|
-
providerId: "gemini",
|
|
6613
|
-
providerLabel: "Gemini",
|
|
6614
|
-
transportKind: "acp",
|
|
6615
|
-
transportId: "gemini-acp",
|
|
6616
|
-
transportLabel: "Gemini ACP",
|
|
6617
|
-
runtime: "local",
|
|
6618
|
-
defaultModel: "auto-gemini-3",
|
|
6619
|
-
defaultCommand: AI_CONNECT_DEFAULT_ACP_COMMANDS["gemini:gemini-acp"]
|
|
6620
6651
|
}
|
|
6621
6652
|
]
|
|
6622
6653
|
},
|
|
6623
6654
|
{
|
|
6624
|
-
providerId: "
|
|
6625
|
-
label: "
|
|
6655
|
+
providerId: "pi",
|
|
6656
|
+
label: "pi",
|
|
6626
6657
|
transports: [
|
|
6627
6658
|
{
|
|
6628
|
-
providerId: "
|
|
6629
|
-
providerLabel: "
|
|
6659
|
+
providerId: "pi",
|
|
6660
|
+
providerLabel: "pi",
|
|
6630
6661
|
transportKind: "cli",
|
|
6631
|
-
transportId: "
|
|
6632
|
-
transportLabel: "
|
|
6662
|
+
transportId: "pi-cli",
|
|
6663
|
+
transportLabel: "pi CLI",
|
|
6633
6664
|
runtime: "local",
|
|
6634
|
-
defaultModel: "
|
|
6635
|
-
defaultCommand: AI_CONNECT_DEFAULT_CLI_COMMANDS["
|
|
6636
|
-
},
|
|
6637
|
-
{
|
|
6638
|
-
providerId: "qwen",
|
|
6639
|
-
providerLabel: "Qwen",
|
|
6640
|
-
transportKind: "acp",
|
|
6641
|
-
transportId: "qwen-acp",
|
|
6642
|
-
transportLabel: "Qwen ACP",
|
|
6643
|
-
runtime: "local",
|
|
6644
|
-
defaultModel: "default",
|
|
6645
|
-
defaultCommand: AI_CONNECT_DEFAULT_ACP_COMMANDS["qwen:qwen-acp"]
|
|
6665
|
+
defaultModel: "gemini-3.1-pro-low",
|
|
6666
|
+
defaultCommand: AI_CONNECT_DEFAULT_CLI_COMMANDS["pi:pi-cli"]
|
|
6646
6667
|
}
|
|
6647
6668
|
]
|
|
6648
6669
|
},
|
|
@@ -6815,15 +6836,6 @@ function resolveHomeDir(env) {
|
|
|
6815
6836
|
function resolveXdgConfigHome(env) {
|
|
6816
6837
|
return env.XDG_CONFIG_HOME ?? path.join(resolveHomeDir(env), ".config");
|
|
6817
6838
|
}
|
|
6818
|
-
function resolveGeminiCliHome(env) {
|
|
6819
|
-
return env.GEMINI_CLI_HOME ?? resolveHomeDir(env);
|
|
6820
|
-
}
|
|
6821
|
-
function resolveGeminiDir(env) {
|
|
6822
|
-
return path.join(resolveGeminiCliHome(env), ".gemini");
|
|
6823
|
-
}
|
|
6824
|
-
function resolveQwenDir(env) {
|
|
6825
|
-
return path.join(resolveHomeDir(env), ".qwen");
|
|
6826
|
-
}
|
|
6827
6839
|
function resolveCodexHome(env) {
|
|
6828
6840
|
return env.CODEX_HOME ?? path.join(resolveHomeDir(env), ".codex");
|
|
6829
6841
|
}
|
|
@@ -6869,118 +6881,6 @@ async function removeIfExists(targetPath) {
|
|
|
6869
6881
|
}
|
|
6870
6882
|
});
|
|
6871
6883
|
}
|
|
6872
|
-
async function readJsonFile(filePath) {
|
|
6873
|
-
try {
|
|
6874
|
-
const raw = await fs.readFile(filePath, "utf8");
|
|
6875
|
-
const parsed = JSON.parse(raw);
|
|
6876
|
-
return isRecord2(parsed) ? parsed : void 0;
|
|
6877
|
-
} catch (error) {
|
|
6878
|
-
if (error instanceof SyntaxError || error.code === "ENOENT") {
|
|
6879
|
-
return void 0;
|
|
6880
|
-
}
|
|
6881
|
-
throw error;
|
|
6882
|
-
}
|
|
6883
|
-
}
|
|
6884
|
-
function mergeRecordValues(current, next) {
|
|
6885
|
-
const merged = { ...current };
|
|
6886
|
-
for (const [key, value] of Object.entries(next)) {
|
|
6887
|
-
const existing = merged[key];
|
|
6888
|
-
if (isRecord2(existing) && isRecord2(value)) {
|
|
6889
|
-
merged[key] = mergeRecordValues(existing, value);
|
|
6890
|
-
continue;
|
|
6891
|
-
}
|
|
6892
|
-
merged[key] = value;
|
|
6893
|
-
}
|
|
6894
|
-
return merged;
|
|
6895
|
-
}
|
|
6896
|
-
async function writeJsonFile(filePath, value) {
|
|
6897
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
6898
|
-
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
6899
|
-
`, "utf8");
|
|
6900
|
-
}
|
|
6901
|
-
async function prepareGeminiLaunchRuntime(base, launch) {
|
|
6902
|
-
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6903
|
-
return {
|
|
6904
|
-
...base,
|
|
6905
|
-
launch
|
|
6906
|
-
};
|
|
6907
|
-
}
|
|
6908
|
-
const sandbox = await createSandboxPaths("ai-connect-gemini-acp-");
|
|
6909
|
-
const originalGeminiDir = resolveGeminiDir(base.env);
|
|
6910
|
-
const sandboxGeminiDir = path.join(sandbox.root, ".gemini");
|
|
6911
|
-
const settingsPath = path.join(sandboxGeminiDir, "settings.json");
|
|
6912
|
-
const systemSettingsPath = path.join(sandbox.config, "gemini-settings.json");
|
|
6913
|
-
const systemDefaultsPath = path.join(
|
|
6914
|
-
sandbox.config,
|
|
6915
|
-
"gemini-system-defaults.json"
|
|
6916
|
-
);
|
|
6917
|
-
const isolatedCwd = launch.contextMode === "clean" || launch.skillsMode === "disabled" ? sandbox.root : base.cwd;
|
|
6918
|
-
await Promise.all(
|
|
6919
|
-
[
|
|
6920
|
-
"oauth_creds.json",
|
|
6921
|
-
"google_accounts.json",
|
|
6922
|
-
"mcp-oauth-tokens.json",
|
|
6923
|
-
"a2a-oauth-tokens.json",
|
|
6924
|
-
"installation_id"
|
|
6925
|
-
].map(
|
|
6926
|
-
(fileName) => maybeCopyFile(
|
|
6927
|
-
path.join(originalGeminiDir, fileName),
|
|
6928
|
-
path.join(sandboxGeminiDir, fileName)
|
|
6929
|
-
)
|
|
6930
|
-
)
|
|
6931
|
-
);
|
|
6932
|
-
const settings = mergeRecordValues(
|
|
6933
|
-
await readJsonFile(settingsPath) ?? {},
|
|
6934
|
-
{
|
|
6935
|
-
context: {
|
|
6936
|
-
fileName: "AI_CONNECT_CONTEXT_DISABLED.md",
|
|
6937
|
-
includeDirectoryTree: false,
|
|
6938
|
-
memoryBoundaryMarkers: [],
|
|
6939
|
-
includeDirectories: [],
|
|
6940
|
-
loadMemoryFromIncludeDirectories: false,
|
|
6941
|
-
discoveryMaxDirs: 0
|
|
6942
|
-
},
|
|
6943
|
-
admin: {
|
|
6944
|
-
mcp: {
|
|
6945
|
-
enabled: false,
|
|
6946
|
-
config: {},
|
|
6947
|
-
requiredConfig: {}
|
|
6948
|
-
},
|
|
6949
|
-
...launch.skillsMode === "disabled" ? {
|
|
6950
|
-
skills: {
|
|
6951
|
-
enabled: false
|
|
6952
|
-
}
|
|
6953
|
-
} : {}
|
|
6954
|
-
},
|
|
6955
|
-
...launch.skillsMode === "disabled" ? {
|
|
6956
|
-
skills: {
|
|
6957
|
-
enabled: false,
|
|
6958
|
-
disabled: []
|
|
6959
|
-
}
|
|
6960
|
-
} : {}
|
|
6961
|
-
}
|
|
6962
|
-
);
|
|
6963
|
-
await Promise.all([
|
|
6964
|
-
writeJsonFile(settingsPath, settings),
|
|
6965
|
-
writeJsonFile(systemSettingsPath, {}),
|
|
6966
|
-
writeJsonFile(systemDefaultsPath, {})
|
|
6967
|
-
]);
|
|
6968
|
-
return {
|
|
6969
|
-
commandLine: base.commandLine,
|
|
6970
|
-
cwd: isolatedCwd,
|
|
6971
|
-
env: {
|
|
6972
|
-
...base.env,
|
|
6973
|
-
HOME: sandbox.root,
|
|
6974
|
-
GEMINI_CLI_HOME: sandbox.root,
|
|
6975
|
-
GEMINI_CLI_SYSTEM_SETTINGS_PATH: systemSettingsPath,
|
|
6976
|
-
GEMINI_CLI_SYSTEM_DEFAULTS_PATH: systemDefaultsPath
|
|
6977
|
-
},
|
|
6978
|
-
launch,
|
|
6979
|
-
cleanup: async () => {
|
|
6980
|
-
await removeIfExists(sandbox.root);
|
|
6981
|
-
}
|
|
6982
|
-
};
|
|
6983
|
-
}
|
|
6984
6884
|
async function prepareOpenCodeLaunchRuntime(base, launch) {
|
|
6985
6885
|
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6986
6886
|
return {
|
|
@@ -7029,70 +6929,6 @@ async function prepareOpenCodeLaunchRuntime(base, launch) {
|
|
|
7029
6929
|
}
|
|
7030
6930
|
};
|
|
7031
6931
|
}
|
|
7032
|
-
async function prepareQwenLaunchRuntime(base, launch) {
|
|
7033
|
-
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
7034
|
-
return {
|
|
7035
|
-
...base,
|
|
7036
|
-
launch
|
|
7037
|
-
};
|
|
7038
|
-
}
|
|
7039
|
-
const sandbox = await createSandboxPaths("ai-connect-qwen-acp-");
|
|
7040
|
-
const originalQwenDir = resolveQwenDir(base.env);
|
|
7041
|
-
const sandboxQwenDir = path.join(sandbox.home, ".qwen");
|
|
7042
|
-
const settingsPath = path.join(sandboxQwenDir, "settings.json");
|
|
7043
|
-
const systemSettingsPath = path.join(sandbox.config, "qwen-settings.json");
|
|
7044
|
-
const systemDefaultsPath = path.join(
|
|
7045
|
-
sandbox.config,
|
|
7046
|
-
"qwen-system-defaults.json"
|
|
7047
|
-
);
|
|
7048
|
-
await Promise.all(
|
|
7049
|
-
[
|
|
7050
|
-
"oauth_creds.json",
|
|
7051
|
-
"mcp-oauth-tokens.json",
|
|
7052
|
-
"installation_id",
|
|
7053
|
-
".env",
|
|
7054
|
-
"settings.json"
|
|
7055
|
-
].map(
|
|
7056
|
-
(fileName) => maybeCopyFile(
|
|
7057
|
-
path.join(originalQwenDir, fileName),
|
|
7058
|
-
path.join(sandboxQwenDir, fileName)
|
|
7059
|
-
)
|
|
7060
|
-
)
|
|
7061
|
-
);
|
|
7062
|
-
const settings = mergeRecordValues(
|
|
7063
|
-
await readJsonFile(settingsPath) ?? {},
|
|
7064
|
-
{
|
|
7065
|
-
context: {
|
|
7066
|
-
fileName: ["AI_CONNECT_CONTEXT_DISABLED.md"],
|
|
7067
|
-
includeDirectories: [],
|
|
7068
|
-
loadFromIncludeDirectories: false
|
|
7069
|
-
},
|
|
7070
|
-
model: {
|
|
7071
|
-
skipStartupContext: true
|
|
7072
|
-
}
|
|
7073
|
-
}
|
|
7074
|
-
);
|
|
7075
|
-
await Promise.all([
|
|
7076
|
-
writeJsonFile(settingsPath, settings),
|
|
7077
|
-
writeJsonFile(systemSettingsPath, {}),
|
|
7078
|
-
writeJsonFile(systemDefaultsPath, {})
|
|
7079
|
-
]);
|
|
7080
|
-
const isolatedCwd = launch.contextMode === "clean" || launch.skillsMode === "disabled" ? sandbox.root : base.cwd;
|
|
7081
|
-
return {
|
|
7082
|
-
commandLine: base.commandLine,
|
|
7083
|
-
cwd: isolatedCwd,
|
|
7084
|
-
env: {
|
|
7085
|
-
...base.env,
|
|
7086
|
-
HOME: sandbox.home,
|
|
7087
|
-
QWEN_CODE_SYSTEM_SETTINGS_PATH: systemSettingsPath,
|
|
7088
|
-
QWEN_CODE_SYSTEM_DEFAULTS_PATH: systemDefaultsPath
|
|
7089
|
-
},
|
|
7090
|
-
launch,
|
|
7091
|
-
cleanup: async () => {
|
|
7092
|
-
await removeIfExists(sandbox.root);
|
|
7093
|
-
}
|
|
7094
|
-
};
|
|
7095
|
-
}
|
|
7096
6932
|
async function prepareCodexLaunchRuntime(base, launch) {
|
|
7097
6933
|
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
7098
6934
|
return {
|
|
@@ -7210,15 +7046,9 @@ async function prepareAcpLaunchRuntime(route, options, commandLine, cwdOverride)
|
|
|
7210
7046
|
...options?.env ?? {}
|
|
7211
7047
|
}
|
|
7212
7048
|
};
|
|
7213
|
-
if (route.transport.id === "gemini-acp") {
|
|
7214
|
-
return prepareGeminiLaunchRuntime(base, launch);
|
|
7215
|
-
}
|
|
7216
7049
|
if (route.transport.id === "opencode-acp") {
|
|
7217
7050
|
return prepareOpenCodeLaunchRuntime(base, launch);
|
|
7218
7051
|
}
|
|
7219
|
-
if (route.transport.id === "qwen-acp") {
|
|
7220
|
-
return prepareQwenLaunchRuntime(base, launch);
|
|
7221
|
-
}
|
|
7222
7052
|
if (route.transport.id === "codex-acp") {
|
|
7223
7053
|
return prepareCodexLaunchRuntime(base, launch);
|
|
7224
7054
|
}
|
|
@@ -7799,10 +7629,6 @@ function buildAcpLifecycle(route, authRequest, mode) {
|
|
|
7799
7629
|
steps.push("authenticate");
|
|
7800
7630
|
}
|
|
7801
7631
|
steps.push("session/new");
|
|
7802
|
-
if (mode === "prompt" && route.transport.id === "gemini-acp") {
|
|
7803
|
-
steps.push("session/set_model");
|
|
7804
|
-
keys.push("session/set_model.modelId");
|
|
7805
|
-
}
|
|
7806
7632
|
if (mode === "prompt") {
|
|
7807
7633
|
steps.push("session/prompt");
|
|
7808
7634
|
}
|
|
@@ -7938,16 +7764,6 @@ var AcpConnection = class {
|
|
|
7938
7764
|
);
|
|
7939
7765
|
}
|
|
7940
7766
|
const sessionId = session.sessionId;
|
|
7941
|
-
if (context.route.transport.id === "gemini-acp") {
|
|
7942
|
-
await measurePhase(
|
|
7943
|
-
transport.phases ?? [],
|
|
7944
|
-
"session/set_model",
|
|
7945
|
-
async () => this.request("session/set_model", {
|
|
7946
|
-
sessionId,
|
|
7947
|
-
modelId: context.route.model
|
|
7948
|
-
})
|
|
7949
|
-
);
|
|
7950
|
-
}
|
|
7951
7767
|
this.activePrompt = {
|
|
7952
7768
|
sessionId,
|
|
7953
7769
|
text: "",
|
|
@@ -8460,9 +8276,6 @@ var AcpConnection = class {
|
|
|
8460
8276
|
});
|
|
8461
8277
|
}
|
|
8462
8278
|
};
|
|
8463
|
-
function isGeminiAcpFallbackCandidate(route, commandLine) {
|
|
8464
|
-
return route.transport.id === "gemini-acp" && commandLine.includes("--acp") && !commandLine.includes("--experimental-acp");
|
|
8465
|
-
}
|
|
8466
8279
|
function cacheKeyForConnection(route, runtime, options) {
|
|
8467
8280
|
const envEntries = Object.entries(options.env).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}=${value}`).join("");
|
|
8468
8281
|
return [
|
|
@@ -8709,18 +8522,7 @@ function createAcpTransportManager(options) {
|
|
|
8709
8522
|
}
|
|
8710
8523
|
async function drivePrompt(context, onDelta) {
|
|
8711
8524
|
const commandLine = resolveAcpCommand(context.route, options);
|
|
8712
|
-
|
|
8713
|
-
return await runWithCommand(context, commandLine, onDelta);
|
|
8714
|
-
} catch (error) {
|
|
8715
|
-
if (!isGeminiAcpFallbackCandidate(context.route, commandLine)) {
|
|
8716
|
-
throw error;
|
|
8717
|
-
}
|
|
8718
|
-
return await runWithCommand(
|
|
8719
|
-
context,
|
|
8720
|
-
commandLine.replace("--acp", "--experimental-acp"),
|
|
8721
|
-
onDelta
|
|
8722
|
-
);
|
|
8723
|
-
}
|
|
8525
|
+
return await runWithCommand(context, commandLine, onDelta);
|
|
8724
8526
|
}
|
|
8725
8527
|
return {
|
|
8726
8528
|
async runPrompt(context) {
|
|
@@ -8789,17 +8591,7 @@ function createAcpTransportManager(options) {
|
|
|
8789
8591
|
},
|
|
8790
8592
|
async discoverModels(context) {
|
|
8791
8593
|
const commandLine = resolveAcpCommand(context.route, options);
|
|
8792
|
-
|
|
8793
|
-
return await discoverWithCommand(context, commandLine);
|
|
8794
|
-
} catch (error) {
|
|
8795
|
-
if (!isGeminiAcpFallbackCandidate(context.route, commandLine)) {
|
|
8796
|
-
throw error;
|
|
8797
|
-
}
|
|
8798
|
-
return await discoverWithCommand(
|
|
8799
|
-
context,
|
|
8800
|
-
commandLine.replace("--acp", "--experimental-acp")
|
|
8801
|
-
);
|
|
8802
|
-
}
|
|
8594
|
+
return await discoverWithCommand(context, commandLine);
|
|
8803
8595
|
},
|
|
8804
8596
|
async dispose() {
|
|
8805
8597
|
const values = [...connectionPools.values()].flat();
|
|
@@ -8824,14 +8616,6 @@ import fs2 from "node:fs/promises";
|
|
|
8824
8616
|
import os2 from "node:os";
|
|
8825
8617
|
import path2 from "node:path";
|
|
8826
8618
|
var CLI_PRESET_ACP_DISCOVERY_DEFAULTS = {
|
|
8827
|
-
gemini: {
|
|
8828
|
-
transportId: "gemini-acp",
|
|
8829
|
-
providerId: "gemini"
|
|
8830
|
-
},
|
|
8831
|
-
qwen: {
|
|
8832
|
-
transportId: "qwen-acp",
|
|
8833
|
-
providerId: "qwen"
|
|
8834
|
-
},
|
|
8835
8619
|
claude: {
|
|
8836
8620
|
transportId: "claude-code-acp",
|
|
8837
8621
|
providerId: "anthropic"
|
|
@@ -8861,25 +8645,26 @@ function splitCommandLine2(commandLine) {
|
|
|
8861
8645
|
function quoteArg(value) {
|
|
8862
8646
|
return /\s/.test(value) ? JSON.stringify(value) : value;
|
|
8863
8647
|
}
|
|
8864
|
-
function buildCliPrompt(context) {
|
|
8648
|
+
function buildCliPrompt(context, fileInputEnabled) {
|
|
8865
8649
|
if (context.request.operation !== "text") {
|
|
8866
8650
|
throw new AiConnectError(
|
|
8867
8651
|
"not_supported",
|
|
8868
8652
|
`CLI transport "${context.route.transport.id}" only supports text requests.`
|
|
8869
8653
|
);
|
|
8870
8654
|
}
|
|
8871
|
-
if (context.request.image
|
|
8872
|
-
(file) => portableFileCategory(file) === "image" || portableFileCategory(file) === "document"
|
|
8873
|
-
)) {
|
|
8655
|
+
if (context.request.image) {
|
|
8874
8656
|
throw new AiConnectError(
|
|
8875
8657
|
"unsupported_capability",
|
|
8876
|
-
`CLI transport "${context.route.transport.id}" does not support image
|
|
8658
|
+
`CLI transport "${context.route.transport.id}" does not support image output.`
|
|
8877
8659
|
);
|
|
8878
8660
|
}
|
|
8879
|
-
if (context.request.attachments.length > 0) {
|
|
8661
|
+
if (!fileInputEnabled && context.request.attachments.length > 0) {
|
|
8662
|
+
const hasBinary = context.request.attachments.some(
|
|
8663
|
+
(file) => portableFileCategory(file) === "image" || portableFileCategory(file) === "document"
|
|
8664
|
+
);
|
|
8880
8665
|
throw new AiConnectError(
|
|
8881
|
-
"not_supported",
|
|
8882
|
-
`CLI transport "${context.route.transport.id}" does not support attachments.`
|
|
8666
|
+
hasBinary ? "unsupported_capability" : "not_supported",
|
|
8667
|
+
hasBinary ? `CLI transport "${context.route.transport.id}" does not support image or document inputs.` : `CLI transport "${context.route.transport.id}" does not support attachments.`
|
|
8883
8668
|
);
|
|
8884
8669
|
}
|
|
8885
8670
|
return context.request.messages.map((message) => {
|
|
@@ -8938,10 +8723,8 @@ function buildCliCwd(context, options) {
|
|
|
8938
8723
|
}
|
|
8939
8724
|
function defaultCliPresetIdForRoute(route) {
|
|
8940
8725
|
switch (route.transport.id) {
|
|
8941
|
-
case "
|
|
8942
|
-
return "
|
|
8943
|
-
case "qwen-cli":
|
|
8944
|
-
return "qwen";
|
|
8726
|
+
case "pi-cli":
|
|
8727
|
+
return "pi";
|
|
8945
8728
|
case "claude-cli":
|
|
8946
8729
|
return "claude";
|
|
8947
8730
|
case "openclaude-cli":
|
|
@@ -9026,11 +8809,16 @@ function createCliDiscoveryAcpRoute(route) {
|
|
|
9026
8809
|
function resolveCliTransportOptions(route) {
|
|
9027
8810
|
const presetId = route.transport.cli?.preset ?? defaultCliPresetIdForRoute(route);
|
|
9028
8811
|
const preset = presetId ? getCliTransportPreset(presetId) : void 0;
|
|
8812
|
+
const mergedFileInput = preset?.options.fileInput || route.transport.cli?.fileInput ? {
|
|
8813
|
+
...preset?.options.fileInput ?? {},
|
|
8814
|
+
...route.transport.cli?.fileInput ?? {}
|
|
8815
|
+
} : void 0;
|
|
9029
8816
|
const merged = {
|
|
9030
8817
|
...preset?.options ?? {},
|
|
9031
8818
|
...route.transport.cli ?? {},
|
|
9032
8819
|
...route.transport.cli?.argsTemplate ? { argsTemplate: route.transport.cli.argsTemplate } : {},
|
|
9033
8820
|
...route.transport.cli?.parser ? { parser: route.transport.cli.parser } : {},
|
|
8821
|
+
...mergedFileInput ? { fileInput: mergedFileInput } : {},
|
|
9034
8822
|
...presetId ? { preset: presetId } : {}
|
|
9035
8823
|
};
|
|
9036
8824
|
if (!merged.argsTemplate?.length) {
|
|
@@ -9041,15 +8829,73 @@ function resolveCliTransportOptions(route) {
|
|
|
9041
8829
|
}
|
|
9042
8830
|
return merged;
|
|
9043
8831
|
}
|
|
9044
|
-
async function
|
|
8832
|
+
async function createCliTempDir(baseDir, prefix) {
|
|
9045
8833
|
const tempDir = await fs2.mkdtemp(
|
|
9046
|
-
path2.join(os2.tmpdir(), "ai-connect-cli-
|
|
8834
|
+
path2.join(baseDir ?? os2.tmpdir(), prefix ?? "ai-connect-cli-")
|
|
9047
8835
|
);
|
|
9048
8836
|
return {
|
|
9049
8837
|
tempDir,
|
|
9050
8838
|
outputFile: path2.join(tempDir, "last-message.txt")
|
|
9051
8839
|
};
|
|
9052
8840
|
}
|
|
8841
|
+
function safeStagedBasename(name, index, mimeType) {
|
|
8842
|
+
const base = path2.basename(name.replace(/\\/g, "/"));
|
|
8843
|
+
if (base === "" || base === "." || base === "..") {
|
|
8844
|
+
return `attachment-${index}.${extensionForMime(mimeType)}`;
|
|
8845
|
+
}
|
|
8846
|
+
return base;
|
|
8847
|
+
}
|
|
8848
|
+
async function stageCliAttachments(attachments, attachDir, fileInput) {
|
|
8849
|
+
const categories = new Set(
|
|
8850
|
+
fileInput.categories ?? ["image", "document", "text", "other"]
|
|
8851
|
+
);
|
|
8852
|
+
await fs2.mkdir(attachDir, { recursive: true });
|
|
8853
|
+
const refs = [];
|
|
8854
|
+
const usedNames = /* @__PURE__ */ new Map();
|
|
8855
|
+
for (let i = 0; i < attachments.length; i += 1) {
|
|
8856
|
+
const file = preparePortableFile(attachments[i]);
|
|
8857
|
+
const payload = await materializePortableFile(file);
|
|
8858
|
+
if (!categories.has(payload.category)) {
|
|
8859
|
+
continue;
|
|
8860
|
+
}
|
|
8861
|
+
let destName = safeStagedBasename(payload.name, i, payload.mimeType);
|
|
8862
|
+
const seen = usedNames.get(destName);
|
|
8863
|
+
if (seen !== void 0) {
|
|
8864
|
+
destName = `${i}-${destName}`;
|
|
8865
|
+
}
|
|
8866
|
+
usedNames.set(destName, i);
|
|
8867
|
+
const dest = path2.join(attachDir, destName);
|
|
8868
|
+
if (payload.category === "text") {
|
|
8869
|
+
if (payload.text !== void 0) {
|
|
8870
|
+
await fs2.writeFile(dest, payload.text, "utf8");
|
|
8871
|
+
refs.push(dest);
|
|
8872
|
+
continue;
|
|
8873
|
+
}
|
|
8874
|
+
} else {
|
|
8875
|
+
const bytes = payload.base64 ? Buffer.from(payload.base64, "base64") : await portableFileToBytes(file);
|
|
8876
|
+
if (bytes !== void 0) {
|
|
8877
|
+
await fs2.writeFile(dest, bytes);
|
|
8878
|
+
refs.push(dest);
|
|
8879
|
+
continue;
|
|
8880
|
+
}
|
|
8881
|
+
}
|
|
8882
|
+
if (payload.uri) {
|
|
8883
|
+
refs.push(payload.uri);
|
|
8884
|
+
}
|
|
8885
|
+
}
|
|
8886
|
+
return refs;
|
|
8887
|
+
}
|
|
8888
|
+
function expandFileArgs(staged, fileInput) {
|
|
8889
|
+
const perFileArgs = fileInput?.perFileArgs ?? ["@{path}"];
|
|
8890
|
+
if (fileInput?.placement === "prompt") {
|
|
8891
|
+
return [];
|
|
8892
|
+
}
|
|
8893
|
+
return staged.flatMap(
|
|
8894
|
+
(ref) => perFileArgs.map(
|
|
8895
|
+
(tpl) => tpl.replace("{path}", ref).replace("{name}", path2.basename(ref))
|
|
8896
|
+
)
|
|
8897
|
+
);
|
|
8898
|
+
}
|
|
9053
8899
|
function materializeTemplateArg(templateArg, values, parameterKeys) {
|
|
9054
8900
|
switch (templateArg) {
|
|
9055
8901
|
case "{prompt}":
|
|
@@ -9076,31 +8922,62 @@ function materializeTemplateArg(templateArg, values, parameterKeys) {
|
|
|
9076
8922
|
}
|
|
9077
8923
|
async function buildCliInvocation(context, options) {
|
|
9078
8924
|
const route = context.route;
|
|
9079
|
-
const prompt = buildCliPrompt(context);
|
|
9080
8925
|
const cliOptions = resolveCliTransportOptions(route);
|
|
8926
|
+
const fileInput = cliOptions.fileInput;
|
|
8927
|
+
const attachments = context.request.attachments;
|
|
8928
|
+
const hasStageable = Boolean(fileInput) && attachments.length > 0;
|
|
8929
|
+
let prompt = buildCliPrompt(context, Boolean(fileInput));
|
|
9081
8930
|
const commandLine = resolveCliCommand(route, options);
|
|
9082
8931
|
const resolved = splitCommandLine2(commandLine);
|
|
9083
8932
|
const parameterKeys = [];
|
|
9084
8933
|
let tempDir;
|
|
9085
8934
|
let outputFile;
|
|
9086
|
-
|
|
9087
|
-
|
|
8935
|
+
const needsOutputFile = cliOptions.argsTemplate.includes("{output_file}");
|
|
8936
|
+
if (needsOutputFile || hasStageable) {
|
|
8937
|
+
const created = await createCliTempDir(
|
|
8938
|
+
fileInput?.stagingDir ?? options?.staging?.dir,
|
|
8939
|
+
options?.staging?.prefix
|
|
8940
|
+
);
|
|
9088
8941
|
tempDir = created.tempDir;
|
|
9089
8942
|
outputFile = created.outputFile;
|
|
9090
8943
|
}
|
|
8944
|
+
let staged = [];
|
|
8945
|
+
if (hasStageable && tempDir && fileInput) {
|
|
8946
|
+
staged = await stageCliAttachments(
|
|
8947
|
+
attachments,
|
|
8948
|
+
path2.join(tempDir, "attachments"),
|
|
8949
|
+
fileInput
|
|
8950
|
+
);
|
|
8951
|
+
if (fileInput.placement === "prompt" && staged.length > 0) {
|
|
8952
|
+
const separator = fileInput.separator ?? " ";
|
|
8953
|
+
const mentionTemplate = fileInput.mentionTemplate ?? "@{path}";
|
|
8954
|
+
const mentions = staged.map(
|
|
8955
|
+
(ref) => mentionTemplate.replace("{path}", ref).replace("{name}", path2.basename(ref))
|
|
8956
|
+
).join(separator);
|
|
8957
|
+
prompt = `${prompt}${separator}${mentions}`;
|
|
8958
|
+
}
|
|
8959
|
+
}
|
|
9091
8960
|
const args = [
|
|
9092
8961
|
...resolved.args,
|
|
9093
|
-
...cliOptions.argsTemplate.
|
|
9094
|
-
(part)
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
8962
|
+
...cliOptions.argsTemplate.flatMap((part) => {
|
|
8963
|
+
if (part === "{files}") {
|
|
8964
|
+
if (staged.length > 0) {
|
|
8965
|
+
parameterKeys.push("{files}");
|
|
8966
|
+
}
|
|
8967
|
+
return expandFileArgs(staged, fileInput);
|
|
8968
|
+
}
|
|
8969
|
+
return [
|
|
8970
|
+
materializeTemplateArg(
|
|
8971
|
+
part,
|
|
8972
|
+
{
|
|
8973
|
+
prompt,
|
|
8974
|
+
model: route.model,
|
|
8975
|
+
...outputFile ? { outputFile } : {}
|
|
8976
|
+
},
|
|
8977
|
+
parameterKeys
|
|
8978
|
+
)
|
|
8979
|
+
];
|
|
8980
|
+
})
|
|
9104
8981
|
];
|
|
9105
8982
|
return {
|
|
9106
8983
|
command: resolved.command,
|
|
@@ -9109,6 +8986,7 @@ async function buildCliInvocation(context, options) {
|
|
|
9109
8986
|
env: buildCliEnvironment(options),
|
|
9110
8987
|
...tempDir ? { tempDir } : {},
|
|
9111
8988
|
...outputFile ? { outputFile } : {},
|
|
8989
|
+
...options?.staging?.keep ? { keepStaging: true } : {},
|
|
9112
8990
|
parameterKeys
|
|
9113
8991
|
};
|
|
9114
8992
|
}
|
|
@@ -9320,60 +9198,6 @@ function parseCliModelList(stdout, parser, selector) {
|
|
|
9320
9198
|
}
|
|
9321
9199
|
return models;
|
|
9322
9200
|
}
|
|
9323
|
-
function parseGeminiCli(stdout) {
|
|
9324
|
-
const payload = JSON.parse(stdout);
|
|
9325
|
-
if (typeof payload.error === "string") {
|
|
9326
|
-
throw new AiConnectError("temporary_unavailable", payload.error);
|
|
9327
|
-
}
|
|
9328
|
-
if (typeof payload.response !== "string") {
|
|
9329
|
-
throw new AiConnectError(
|
|
9330
|
-
"temporary_unavailable",
|
|
9331
|
-
"Gemini CLI JSON output did not include response text."
|
|
9332
|
-
);
|
|
9333
|
-
}
|
|
9334
|
-
const usage = payload.stats ? statsToUsage(payload.stats) : void 0;
|
|
9335
|
-
return {
|
|
9336
|
-
text: payload.response,
|
|
9337
|
-
...usage ? { usage } : {},
|
|
9338
|
-
data: payload
|
|
9339
|
-
};
|
|
9340
|
-
}
|
|
9341
|
-
function parseQwenCli(stdout) {
|
|
9342
|
-
const payload = JSON.parse(stdout);
|
|
9343
|
-
if (!Array.isArray(payload)) {
|
|
9344
|
-
throw new AiConnectError(
|
|
9345
|
-
"temporary_unavailable",
|
|
9346
|
-
"Qwen CLI JSON output did not return a message array."
|
|
9347
|
-
);
|
|
9348
|
-
}
|
|
9349
|
-
const resultMessage = payload.find(
|
|
9350
|
-
(entry) => entry && typeof entry === "object" && entry.type === "result"
|
|
9351
|
-
);
|
|
9352
|
-
if (!resultMessage) {
|
|
9353
|
-
throw new AiConnectError(
|
|
9354
|
-
"temporary_unavailable",
|
|
9355
|
-
"Qwen CLI JSON output did not contain a result message."
|
|
9356
|
-
);
|
|
9357
|
-
}
|
|
9358
|
-
if (resultMessage.is_error === true) {
|
|
9359
|
-
throw new AiConnectError(
|
|
9360
|
-
"temporary_unavailable",
|
|
9361
|
-
typeof resultMessage.result === "string" ? resultMessage.result : "Qwen CLI returned an error result."
|
|
9362
|
-
);
|
|
9363
|
-
}
|
|
9364
|
-
if (typeof resultMessage.result !== "string") {
|
|
9365
|
-
throw new AiConnectError(
|
|
9366
|
-
"temporary_unavailable",
|
|
9367
|
-
"Qwen CLI result message did not contain text output."
|
|
9368
|
-
);
|
|
9369
|
-
}
|
|
9370
|
-
const usage = resultMessage.stats ? statsToUsage(resultMessage.stats) : void 0;
|
|
9371
|
-
return {
|
|
9372
|
-
text: resultMessage.result,
|
|
9373
|
-
...usage ? { usage } : {},
|
|
9374
|
-
data: payload
|
|
9375
|
-
};
|
|
9376
|
-
}
|
|
9377
9201
|
function parseClaudeCli(stdout) {
|
|
9378
9202
|
const payload = JSON.parse(stdout);
|
|
9379
9203
|
const text = typeof payload.result === "string" ? payload.result : typeof payload.response === "string" ? payload.response : typeof payload.text === "string" ? payload.text : void 0;
|
|
@@ -9459,10 +9283,8 @@ function parseCliResult(route, result, outputFileContent) {
|
|
|
9459
9283
|
}
|
|
9460
9284
|
}
|
|
9461
9285
|
switch (cliOptions.preset) {
|
|
9462
|
-
case "
|
|
9463
|
-
return
|
|
9464
|
-
case "qwen":
|
|
9465
|
-
return parseQwenCli(stdout);
|
|
9286
|
+
case "pi":
|
|
9287
|
+
return parseTextCli(stdout, { kind: "text" });
|
|
9466
9288
|
case "claude":
|
|
9467
9289
|
case "openclaude":
|
|
9468
9290
|
return parseClaudeCli(stdout);
|
|
@@ -9553,7 +9375,7 @@ async function executeCliInvocation(invocation, timeoutMs, phases, signal) {
|
|
|
9553
9375
|
});
|
|
9554
9376
|
}
|
|
9555
9377
|
async function cleanupCliInvocation(invocation) {
|
|
9556
|
-
if (invocation.tempDir) {
|
|
9378
|
+
if (invocation.tempDir && !invocation.keepStaging) {
|
|
9557
9379
|
await fs2.rm(invocation.tempDir, { recursive: true, force: true });
|
|
9558
9380
|
}
|
|
9559
9381
|
}
|