@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/node/local.js
CHANGED
|
@@ -2,6 +2,128 @@
|
|
|
2
2
|
import { accessSync, constants } from "node:fs";
|
|
3
3
|
import { delimiter, join } from "node:path";
|
|
4
4
|
|
|
5
|
+
// src/cli-presets.ts
|
|
6
|
+
var AI_CONNECT_DEFAULT_CLI_PRESETS = {
|
|
7
|
+
pi: {
|
|
8
|
+
id: "pi",
|
|
9
|
+
label: "pi (coding agent)",
|
|
10
|
+
command: "pi",
|
|
11
|
+
transportId: "pi-cli",
|
|
12
|
+
options: {
|
|
13
|
+
preset: "pi",
|
|
14
|
+
// pi print mode emits the answer as plain text on stdout (no JSON wrapper),
|
|
15
|
+
// and has no ACP mode — so it uses the raw `text` parser and no discovery sidecar.
|
|
16
|
+
// pi takes file/image attachments as separate `@<path>` argv tokens (REQ-024);
|
|
17
|
+
// {files} expands one `@<staged-path>` token per attachment. pi stages all
|
|
18
|
+
// categories (PDFs as-is, no extraction guarantee).
|
|
19
|
+
argsTemplate: ["--print", "--model", "{model}", "{files}", "{prompt}"],
|
|
20
|
+
discovery: {
|
|
21
|
+
via: "none"
|
|
22
|
+
},
|
|
23
|
+
parser: {
|
|
24
|
+
kind: "text"
|
|
25
|
+
},
|
|
26
|
+
fileInput: {
|
|
27
|
+
placement: "args",
|
|
28
|
+
perFileArgs: ["@{path}"]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
claude: {
|
|
33
|
+
id: "claude",
|
|
34
|
+
label: "Claude CLI",
|
|
35
|
+
command: "claude",
|
|
36
|
+
transportId: "claude-cli",
|
|
37
|
+
options: {
|
|
38
|
+
preset: "claude",
|
|
39
|
+
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
40
|
+
discovery: {
|
|
41
|
+
via: "acp",
|
|
42
|
+
acp: {
|
|
43
|
+
transportId: "claude-code-acp"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
parser: {
|
|
47
|
+
kind: "json",
|
|
48
|
+
textPath: "__preset__:claude.result",
|
|
49
|
+
usagePath: "usage",
|
|
50
|
+
errorPath: "__preset__:claude.error"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
openclaude: {
|
|
55
|
+
id: "openclaude",
|
|
56
|
+
label: "OpenClaude CLI",
|
|
57
|
+
command: "openclaude",
|
|
58
|
+
transportId: "openclaude-cli",
|
|
59
|
+
options: {
|
|
60
|
+
preset: "openclaude",
|
|
61
|
+
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
62
|
+
parser: {
|
|
63
|
+
kind: "json",
|
|
64
|
+
textPath: "__preset__:claude.result",
|
|
65
|
+
usagePath: "usage",
|
|
66
|
+
errorPath: "__preset__:claude.error"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
codex: {
|
|
71
|
+
id: "codex",
|
|
72
|
+
label: "Codex CLI",
|
|
73
|
+
command: "codex",
|
|
74
|
+
transportId: "codex-cli",
|
|
75
|
+
options: {
|
|
76
|
+
preset: "codex",
|
|
77
|
+
// codex exec takes image attachments via `-i/--image <FILE>` flags
|
|
78
|
+
// (REQ-024); {files} expands a `--image <staged-path>` pair per image.
|
|
79
|
+
// Images only — a non-image attachment cleanly rejects pre-spawn.
|
|
80
|
+
argsTemplate: [
|
|
81
|
+
"exec",
|
|
82
|
+
"--json",
|
|
83
|
+
"--output-last-message",
|
|
84
|
+
"{output_file}",
|
|
85
|
+
"--model",
|
|
86
|
+
"{model}",
|
|
87
|
+
"{files}",
|
|
88
|
+
"{prompt}"
|
|
89
|
+
],
|
|
90
|
+
discovery: {
|
|
91
|
+
via: "acp",
|
|
92
|
+
acp: {
|
|
93
|
+
transportId: "codex-acp"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
fileInput: {
|
|
97
|
+
placement: "args",
|
|
98
|
+
perFileArgs: ["--image", "{path}"],
|
|
99
|
+
categories: ["image"]
|
|
100
|
+
},
|
|
101
|
+
parser: {
|
|
102
|
+
kind: "jsonl",
|
|
103
|
+
text: {
|
|
104
|
+
path: "__preset__:codex.text"
|
|
105
|
+
},
|
|
106
|
+
usage: {
|
|
107
|
+
path: "__preset__:codex.usage"
|
|
108
|
+
},
|
|
109
|
+
error: {
|
|
110
|
+
path: "__preset__:codex.error"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var AI_CONNECT_DEFAULT_CLI_COMMANDS = {
|
|
117
|
+
"anthropic:claude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.claude.command,
|
|
118
|
+
"anthropic:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
119
|
+
"openclaude:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
120
|
+
"openai:codex-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.codex.command,
|
|
121
|
+
"pi:pi-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.pi.command
|
|
122
|
+
};
|
|
123
|
+
function getCliTransportPreset(presetId) {
|
|
124
|
+
return AI_CONNECT_DEFAULT_CLI_PRESETS[presetId];
|
|
125
|
+
}
|
|
126
|
+
|
|
5
127
|
// src/errors.ts
|
|
6
128
|
var AiConnectError = class extends Error {
|
|
7
129
|
code;
|
|
@@ -346,9 +468,38 @@ function normalizeTransport(providerId, input) {
|
|
|
346
468
|
...fallback !== void 0 ? { fallback } : {}
|
|
347
469
|
};
|
|
348
470
|
})();
|
|
471
|
+
const fileInput = (() => {
|
|
472
|
+
const fi = descriptor.cli?.fileInput;
|
|
473
|
+
if (!fi) {
|
|
474
|
+
return void 0;
|
|
475
|
+
}
|
|
476
|
+
const validCategories = ["image", "document", "text", "other"];
|
|
477
|
+
if (fi.categories) {
|
|
478
|
+
for (const c of fi.categories) {
|
|
479
|
+
assert(
|
|
480
|
+
validCategories.includes(c),
|
|
481
|
+
`Unsupported CLI fileInput category "${String(c)}" for provider "${providerId}".`
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
const placement = fi.placement ?? "args";
|
|
486
|
+
assert(
|
|
487
|
+
placement === "args" || placement === "prompt",
|
|
488
|
+
`Unsupported CLI fileInput placement "${String(placement)}" for provider "${providerId}".`
|
|
489
|
+
);
|
|
490
|
+
return {
|
|
491
|
+
placement,
|
|
492
|
+
...fi.perFileArgs ? { perFileArgs: fi.perFileArgs.map((part) => String(part)) } : {},
|
|
493
|
+
...fi.mentionTemplate?.trim() ? { mentionTemplate: fi.mentionTemplate.trim() } : {},
|
|
494
|
+
...fi.separator !== void 0 ? { separator: fi.separator } : {},
|
|
495
|
+
...fi.categories ? { categories: [...fi.categories] } : {},
|
|
496
|
+
...fi.stagingDir?.trim() ? { stagingDir: fi.stagingDir.trim() } : {}
|
|
497
|
+
};
|
|
498
|
+
})();
|
|
349
499
|
return {
|
|
350
500
|
...normalized,
|
|
351
|
-
...discovery ? { discovery } : {}
|
|
501
|
+
...discovery ? { discovery } : {},
|
|
502
|
+
...fileInput ? { fileInput } : {}
|
|
352
503
|
};
|
|
353
504
|
})();
|
|
354
505
|
const normalizedAuth = descriptor.auth?.methodId?.trim() ? {
|
|
@@ -384,7 +535,7 @@ function normalizeTransport(providerId, input) {
|
|
|
384
535
|
};
|
|
385
536
|
}
|
|
386
537
|
if (descriptor.kind === "cli") {
|
|
387
|
-
const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "
|
|
538
|
+
const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "pi" ? "pi-cli" : "cli";
|
|
388
539
|
return {
|
|
389
540
|
kind: "cli",
|
|
390
541
|
id: descriptor.id?.trim() || inferredId2,
|
|
@@ -401,7 +552,7 @@ function normalizeTransport(providerId, input) {
|
|
|
401
552
|
...descriptor.baseUrl?.trim() ? { baseUrl: descriptor.baseUrl.trim() } : {}
|
|
402
553
|
};
|
|
403
554
|
}
|
|
404
|
-
const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" :
|
|
555
|
+
const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" : "acp";
|
|
405
556
|
const normalizedLaunch = descriptor.launch ? (() => {
|
|
406
557
|
const contextMode = descriptor.launch?.contextMode ?? "workspace";
|
|
407
558
|
const skillsMode = descriptor.launch?.skillsMode ?? "default";
|
|
@@ -436,7 +587,40 @@ function normalizeRuntime(transport, runtime) {
|
|
|
436
587
|
}
|
|
437
588
|
return "universal";
|
|
438
589
|
}
|
|
590
|
+
function cliPresetIdForTransportId(transportId) {
|
|
591
|
+
switch (transportId) {
|
|
592
|
+
case "pi-cli":
|
|
593
|
+
return "pi";
|
|
594
|
+
case "claude-cli":
|
|
595
|
+
return "claude";
|
|
596
|
+
case "openclaude-cli":
|
|
597
|
+
return "openclaude";
|
|
598
|
+
case "codex-cli":
|
|
599
|
+
return "codex";
|
|
600
|
+
default:
|
|
601
|
+
return void 0;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function cliFileInputCategories(transport) {
|
|
605
|
+
if (transport.kind !== "cli") {
|
|
606
|
+
return /* @__PURE__ */ new Set();
|
|
607
|
+
}
|
|
608
|
+
const presetId = transport.cli?.preset ?? cliPresetIdForTransportId(transport.id);
|
|
609
|
+
const presetFileInput = presetId ? AI_CONNECT_DEFAULT_CLI_PRESETS[presetId]?.options.fileInput : void 0;
|
|
610
|
+
const routeFileInput = transport.cli?.fileInput;
|
|
611
|
+
if (!presetFileInput && !routeFileInput) {
|
|
612
|
+
return /* @__PURE__ */ new Set();
|
|
613
|
+
}
|
|
614
|
+
const merged = {
|
|
615
|
+
...presetFileInput ?? {},
|
|
616
|
+
...routeFileInput ?? {}
|
|
617
|
+
};
|
|
618
|
+
return new Set(
|
|
619
|
+
merged.categories ?? ["image", "document", "text", "other"]
|
|
620
|
+
);
|
|
621
|
+
}
|
|
439
622
|
function defaultCapabilities(transport, runtime) {
|
|
623
|
+
const cliFileCategories = cliFileInputCategories(transport);
|
|
440
624
|
const localOnly = runtime === "local" || transport.kind === "acp" || transport.kind === "cli" || transport.kind === "server";
|
|
441
625
|
const browserSafe = !localOnly;
|
|
442
626
|
return {
|
|
@@ -451,10 +635,12 @@ function defaultCapabilities(transport, runtime) {
|
|
|
451
635
|
supportsClientToolExecution: transport.kind === "api",
|
|
452
636
|
// C6 / F-P-4: api routes now advertise document + image input parity with acp.
|
|
453
637
|
// The per-account `capabilities` override remains the escape hatch for
|
|
454
|
-
// text-only api proxies that must opt out.
|
|
455
|
-
|
|
638
|
+
// text-only api proxies that must opt out. REQ-024: a cli route advertises
|
|
639
|
+
// document/image input ONLY for the categories its fileInput actually stages
|
|
640
|
+
// (category-aware so e.g. an image-only cli cleanly rejects a PDF pre-spawn).
|
|
641
|
+
supportsFileUpload: transport.kind === "acp" || transport.kind === "api" || cliFileCategories.has("document"),
|
|
456
642
|
supportsFileOutput: transport.kind === "acp",
|
|
457
|
-
supportsImageInput: transport.kind === "acp" || transport.kind === "api",
|
|
643
|
+
supportsImageInput: transport.kind === "acp" || transport.kind === "api" || cliFileCategories.has("image"),
|
|
458
644
|
supportsImageOutput: transport.kind === "acp",
|
|
459
645
|
supportsProfileVerification: true,
|
|
460
646
|
supportsCredentialRotation: false,
|
|
@@ -1005,6 +1191,10 @@ function extension(value) {
|
|
|
1005
1191
|
}
|
|
1006
1192
|
return name.slice(index + 1).toLowerCase();
|
|
1007
1193
|
}
|
|
1194
|
+
function extensionForMime(mimeType) {
|
|
1195
|
+
const subtype = mimeType.split("/")[1]?.toLowerCase().replace(/[^a-z0-9.+-]/g, "");
|
|
1196
|
+
return subtype && subtype.length > 0 ? subtype : "bin";
|
|
1197
|
+
}
|
|
1008
1198
|
function isRemoteUrl(value) {
|
|
1009
1199
|
try {
|
|
1010
1200
|
const url = new URL(value);
|
|
@@ -1629,18 +1819,14 @@ var MODEL_REFERENCE = {
|
|
|
1629
1819
|
key: "gemini-3.1-flash-lite",
|
|
1630
1820
|
contextLength: 1048576
|
|
1631
1821
|
},
|
|
1822
|
+
"gemini-3.1-pro": { key: "gemini-3.1-pro", contextLength: 1048576 },
|
|
1632
1823
|
"gemini-3-pro": { key: "gemini-3-pro", contextLength: 2097152 },
|
|
1633
1824
|
"auto-gemini-3": { key: "auto-gemini-3", contextLength: 1048576 },
|
|
1634
1825
|
"gemini-2.5-flash": { key: "gemini-2.5-flash", contextLength: 1048576 },
|
|
1635
1826
|
"gemini-2.5-pro": { key: "gemini-2.5-pro", contextLength: 2097152 },
|
|
1636
1827
|
"gemini-2.0-flash": { key: "gemini-2.0-flash", contextLength: 1048576 },
|
|
1637
1828
|
"gemini-1.5-flash": { key: "gemini-1.5-flash", contextLength: 1048576 },
|
|
1638
|
-
"gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
|
|
1639
|
-
// --- Qwen (catalog: qwen3-coder-plus) ---
|
|
1640
|
-
"qwen3-coder-plus": { key: "qwen3-coder-plus", contextLength: 1048576 },
|
|
1641
|
-
"qwen3-coder": { key: "qwen3-coder", contextLength: 262144 },
|
|
1642
|
-
"qwen-max": { key: "qwen-max", contextLength: 32768 },
|
|
1643
|
-
"qwen-plus": { key: "qwen-plus", contextLength: 131072 }
|
|
1829
|
+
"gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
|
|
1644
1830
|
};
|
|
1645
1831
|
function normalizeModelKey(model) {
|
|
1646
1832
|
let key = model.trim().toLowerCase();
|
|
@@ -6320,8 +6506,6 @@ import { pathToFileURL } from "node:url";
|
|
|
6320
6506
|
var AI_CONNECT_DEFAULT_ACP_COMMANDS = {
|
|
6321
6507
|
"anthropic:claude-code-acp": "npx -y @agentclientprotocol/claude-agent-acp@^0.25.0",
|
|
6322
6508
|
"openai:codex-acp": "npx @zed-industries/codex-acp@^0.11.1",
|
|
6323
|
-
"gemini:gemini-acp": "gemini --acp",
|
|
6324
|
-
"qwen:qwen-acp": "qwen --acp",
|
|
6325
6509
|
"opencode:opencode-acp": "opencode acp"
|
|
6326
6510
|
};
|
|
6327
6511
|
|
|
@@ -6402,15 +6586,6 @@ function resolveHomeDir(env) {
|
|
|
6402
6586
|
function resolveXdgConfigHome(env) {
|
|
6403
6587
|
return env.XDG_CONFIG_HOME ?? path.join(resolveHomeDir(env), ".config");
|
|
6404
6588
|
}
|
|
6405
|
-
function resolveGeminiCliHome(env) {
|
|
6406
|
-
return env.GEMINI_CLI_HOME ?? resolveHomeDir(env);
|
|
6407
|
-
}
|
|
6408
|
-
function resolveGeminiDir(env) {
|
|
6409
|
-
return path.join(resolveGeminiCliHome(env), ".gemini");
|
|
6410
|
-
}
|
|
6411
|
-
function resolveQwenDir(env) {
|
|
6412
|
-
return path.join(resolveHomeDir(env), ".qwen");
|
|
6413
|
-
}
|
|
6414
6589
|
function resolveCodexHome(env) {
|
|
6415
6590
|
return env.CODEX_HOME ?? path.join(resolveHomeDir(env), ".codex");
|
|
6416
6591
|
}
|
|
@@ -6456,118 +6631,6 @@ async function removeIfExists(targetPath) {
|
|
|
6456
6631
|
}
|
|
6457
6632
|
});
|
|
6458
6633
|
}
|
|
6459
|
-
async function readJsonFile(filePath) {
|
|
6460
|
-
try {
|
|
6461
|
-
const raw = await fs.readFile(filePath, "utf8");
|
|
6462
|
-
const parsed = JSON.parse(raw);
|
|
6463
|
-
return isRecord2(parsed) ? parsed : void 0;
|
|
6464
|
-
} catch (error) {
|
|
6465
|
-
if (error instanceof SyntaxError || error.code === "ENOENT") {
|
|
6466
|
-
return void 0;
|
|
6467
|
-
}
|
|
6468
|
-
throw error;
|
|
6469
|
-
}
|
|
6470
|
-
}
|
|
6471
|
-
function mergeRecordValues(current, next) {
|
|
6472
|
-
const merged = { ...current };
|
|
6473
|
-
for (const [key, value] of Object.entries(next)) {
|
|
6474
|
-
const existing = merged[key];
|
|
6475
|
-
if (isRecord2(existing) && isRecord2(value)) {
|
|
6476
|
-
merged[key] = mergeRecordValues(existing, value);
|
|
6477
|
-
continue;
|
|
6478
|
-
}
|
|
6479
|
-
merged[key] = value;
|
|
6480
|
-
}
|
|
6481
|
-
return merged;
|
|
6482
|
-
}
|
|
6483
|
-
async function writeJsonFile(filePath, value) {
|
|
6484
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
6485
|
-
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
6486
|
-
`, "utf8");
|
|
6487
|
-
}
|
|
6488
|
-
async function prepareGeminiLaunchRuntime(base, launch) {
|
|
6489
|
-
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6490
|
-
return {
|
|
6491
|
-
...base,
|
|
6492
|
-
launch
|
|
6493
|
-
};
|
|
6494
|
-
}
|
|
6495
|
-
const sandbox = await createSandboxPaths("ai-connect-gemini-acp-");
|
|
6496
|
-
const originalGeminiDir = resolveGeminiDir(base.env);
|
|
6497
|
-
const sandboxGeminiDir = path.join(sandbox.root, ".gemini");
|
|
6498
|
-
const settingsPath = path.join(sandboxGeminiDir, "settings.json");
|
|
6499
|
-
const systemSettingsPath = path.join(sandbox.config, "gemini-settings.json");
|
|
6500
|
-
const systemDefaultsPath = path.join(
|
|
6501
|
-
sandbox.config,
|
|
6502
|
-
"gemini-system-defaults.json"
|
|
6503
|
-
);
|
|
6504
|
-
const isolatedCwd = launch.contextMode === "clean" || launch.skillsMode === "disabled" ? sandbox.root : base.cwd;
|
|
6505
|
-
await Promise.all(
|
|
6506
|
-
[
|
|
6507
|
-
"oauth_creds.json",
|
|
6508
|
-
"google_accounts.json",
|
|
6509
|
-
"mcp-oauth-tokens.json",
|
|
6510
|
-
"a2a-oauth-tokens.json",
|
|
6511
|
-
"installation_id"
|
|
6512
|
-
].map(
|
|
6513
|
-
(fileName) => maybeCopyFile(
|
|
6514
|
-
path.join(originalGeminiDir, fileName),
|
|
6515
|
-
path.join(sandboxGeminiDir, fileName)
|
|
6516
|
-
)
|
|
6517
|
-
)
|
|
6518
|
-
);
|
|
6519
|
-
const settings = mergeRecordValues(
|
|
6520
|
-
await readJsonFile(settingsPath) ?? {},
|
|
6521
|
-
{
|
|
6522
|
-
context: {
|
|
6523
|
-
fileName: "AI_CONNECT_CONTEXT_DISABLED.md",
|
|
6524
|
-
includeDirectoryTree: false,
|
|
6525
|
-
memoryBoundaryMarkers: [],
|
|
6526
|
-
includeDirectories: [],
|
|
6527
|
-
loadMemoryFromIncludeDirectories: false,
|
|
6528
|
-
discoveryMaxDirs: 0
|
|
6529
|
-
},
|
|
6530
|
-
admin: {
|
|
6531
|
-
mcp: {
|
|
6532
|
-
enabled: false,
|
|
6533
|
-
config: {},
|
|
6534
|
-
requiredConfig: {}
|
|
6535
|
-
},
|
|
6536
|
-
...launch.skillsMode === "disabled" ? {
|
|
6537
|
-
skills: {
|
|
6538
|
-
enabled: false
|
|
6539
|
-
}
|
|
6540
|
-
} : {}
|
|
6541
|
-
},
|
|
6542
|
-
...launch.skillsMode === "disabled" ? {
|
|
6543
|
-
skills: {
|
|
6544
|
-
enabled: false,
|
|
6545
|
-
disabled: []
|
|
6546
|
-
}
|
|
6547
|
-
} : {}
|
|
6548
|
-
}
|
|
6549
|
-
);
|
|
6550
|
-
await Promise.all([
|
|
6551
|
-
writeJsonFile(settingsPath, settings),
|
|
6552
|
-
writeJsonFile(systemSettingsPath, {}),
|
|
6553
|
-
writeJsonFile(systemDefaultsPath, {})
|
|
6554
|
-
]);
|
|
6555
|
-
return {
|
|
6556
|
-
commandLine: base.commandLine,
|
|
6557
|
-
cwd: isolatedCwd,
|
|
6558
|
-
env: {
|
|
6559
|
-
...base.env,
|
|
6560
|
-
HOME: sandbox.root,
|
|
6561
|
-
GEMINI_CLI_HOME: sandbox.root,
|
|
6562
|
-
GEMINI_CLI_SYSTEM_SETTINGS_PATH: systemSettingsPath,
|
|
6563
|
-
GEMINI_CLI_SYSTEM_DEFAULTS_PATH: systemDefaultsPath
|
|
6564
|
-
},
|
|
6565
|
-
launch,
|
|
6566
|
-
cleanup: async () => {
|
|
6567
|
-
await removeIfExists(sandbox.root);
|
|
6568
|
-
}
|
|
6569
|
-
};
|
|
6570
|
-
}
|
|
6571
6634
|
async function prepareOpenCodeLaunchRuntime(base, launch) {
|
|
6572
6635
|
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6573
6636
|
return {
|
|
@@ -6616,70 +6679,6 @@ async function prepareOpenCodeLaunchRuntime(base, launch) {
|
|
|
6616
6679
|
}
|
|
6617
6680
|
};
|
|
6618
6681
|
}
|
|
6619
|
-
async function prepareQwenLaunchRuntime(base, launch) {
|
|
6620
|
-
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6621
|
-
return {
|
|
6622
|
-
...base,
|
|
6623
|
-
launch
|
|
6624
|
-
};
|
|
6625
|
-
}
|
|
6626
|
-
const sandbox = await createSandboxPaths("ai-connect-qwen-acp-");
|
|
6627
|
-
const originalQwenDir = resolveQwenDir(base.env);
|
|
6628
|
-
const sandboxQwenDir = path.join(sandbox.home, ".qwen");
|
|
6629
|
-
const settingsPath = path.join(sandboxQwenDir, "settings.json");
|
|
6630
|
-
const systemSettingsPath = path.join(sandbox.config, "qwen-settings.json");
|
|
6631
|
-
const systemDefaultsPath = path.join(
|
|
6632
|
-
sandbox.config,
|
|
6633
|
-
"qwen-system-defaults.json"
|
|
6634
|
-
);
|
|
6635
|
-
await Promise.all(
|
|
6636
|
-
[
|
|
6637
|
-
"oauth_creds.json",
|
|
6638
|
-
"mcp-oauth-tokens.json",
|
|
6639
|
-
"installation_id",
|
|
6640
|
-
".env",
|
|
6641
|
-
"settings.json"
|
|
6642
|
-
].map(
|
|
6643
|
-
(fileName) => maybeCopyFile(
|
|
6644
|
-
path.join(originalQwenDir, fileName),
|
|
6645
|
-
path.join(sandboxQwenDir, fileName)
|
|
6646
|
-
)
|
|
6647
|
-
)
|
|
6648
|
-
);
|
|
6649
|
-
const settings = mergeRecordValues(
|
|
6650
|
-
await readJsonFile(settingsPath) ?? {},
|
|
6651
|
-
{
|
|
6652
|
-
context: {
|
|
6653
|
-
fileName: ["AI_CONNECT_CONTEXT_DISABLED.md"],
|
|
6654
|
-
includeDirectories: [],
|
|
6655
|
-
loadFromIncludeDirectories: false
|
|
6656
|
-
},
|
|
6657
|
-
model: {
|
|
6658
|
-
skipStartupContext: true
|
|
6659
|
-
}
|
|
6660
|
-
}
|
|
6661
|
-
);
|
|
6662
|
-
await Promise.all([
|
|
6663
|
-
writeJsonFile(settingsPath, settings),
|
|
6664
|
-
writeJsonFile(systemSettingsPath, {}),
|
|
6665
|
-
writeJsonFile(systemDefaultsPath, {})
|
|
6666
|
-
]);
|
|
6667
|
-
const isolatedCwd = launch.contextMode === "clean" || launch.skillsMode === "disabled" ? sandbox.root : base.cwd;
|
|
6668
|
-
return {
|
|
6669
|
-
commandLine: base.commandLine,
|
|
6670
|
-
cwd: isolatedCwd,
|
|
6671
|
-
env: {
|
|
6672
|
-
...base.env,
|
|
6673
|
-
HOME: sandbox.home,
|
|
6674
|
-
QWEN_CODE_SYSTEM_SETTINGS_PATH: systemSettingsPath,
|
|
6675
|
-
QWEN_CODE_SYSTEM_DEFAULTS_PATH: systemDefaultsPath
|
|
6676
|
-
},
|
|
6677
|
-
launch,
|
|
6678
|
-
cleanup: async () => {
|
|
6679
|
-
await removeIfExists(sandbox.root);
|
|
6680
|
-
}
|
|
6681
|
-
};
|
|
6682
|
-
}
|
|
6683
6682
|
async function prepareCodexLaunchRuntime(base, launch) {
|
|
6684
6683
|
if (launch.contextMode === "workspace" && launch.skillsMode === "default") {
|
|
6685
6684
|
return {
|
|
@@ -6797,15 +6796,9 @@ async function prepareAcpLaunchRuntime(route, options, commandLine, cwdOverride)
|
|
|
6797
6796
|
...options?.env ?? {}
|
|
6798
6797
|
}
|
|
6799
6798
|
};
|
|
6800
|
-
if (route.transport.id === "gemini-acp") {
|
|
6801
|
-
return prepareGeminiLaunchRuntime(base, launch);
|
|
6802
|
-
}
|
|
6803
6799
|
if (route.transport.id === "opencode-acp") {
|
|
6804
6800
|
return prepareOpenCodeLaunchRuntime(base, launch);
|
|
6805
6801
|
}
|
|
6806
|
-
if (route.transport.id === "qwen-acp") {
|
|
6807
|
-
return prepareQwenLaunchRuntime(base, launch);
|
|
6808
|
-
}
|
|
6809
6802
|
if (route.transport.id === "codex-acp") {
|
|
6810
6803
|
return prepareCodexLaunchRuntime(base, launch);
|
|
6811
6804
|
}
|
|
@@ -7386,10 +7379,6 @@ function buildAcpLifecycle(route, authRequest, mode) {
|
|
|
7386
7379
|
steps.push("authenticate");
|
|
7387
7380
|
}
|
|
7388
7381
|
steps.push("session/new");
|
|
7389
|
-
if (mode === "prompt" && route.transport.id === "gemini-acp") {
|
|
7390
|
-
steps.push("session/set_model");
|
|
7391
|
-
keys.push("session/set_model.modelId");
|
|
7392
|
-
}
|
|
7393
7382
|
if (mode === "prompt") {
|
|
7394
7383
|
steps.push("session/prompt");
|
|
7395
7384
|
}
|
|
@@ -7525,16 +7514,6 @@ var AcpConnection = class {
|
|
|
7525
7514
|
);
|
|
7526
7515
|
}
|
|
7527
7516
|
const sessionId = session.sessionId;
|
|
7528
|
-
if (context.route.transport.id === "gemini-acp") {
|
|
7529
|
-
await measurePhase(
|
|
7530
|
-
transport.phases ?? [],
|
|
7531
|
-
"session/set_model",
|
|
7532
|
-
async () => this.request("session/set_model", {
|
|
7533
|
-
sessionId,
|
|
7534
|
-
modelId: context.route.model
|
|
7535
|
-
})
|
|
7536
|
-
);
|
|
7537
|
-
}
|
|
7538
7517
|
this.activePrompt = {
|
|
7539
7518
|
sessionId,
|
|
7540
7519
|
text: "",
|
|
@@ -8047,9 +8026,6 @@ var AcpConnection = class {
|
|
|
8047
8026
|
});
|
|
8048
8027
|
}
|
|
8049
8028
|
};
|
|
8050
|
-
function isGeminiAcpFallbackCandidate(route, commandLine) {
|
|
8051
|
-
return route.transport.id === "gemini-acp" && commandLine.includes("--acp") && !commandLine.includes("--experimental-acp");
|
|
8052
|
-
}
|
|
8053
8029
|
function cacheKeyForConnection(route, runtime, options) {
|
|
8054
8030
|
const envEntries = Object.entries(options.env).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}=${value}`).join("");
|
|
8055
8031
|
return [
|
|
@@ -8296,18 +8272,7 @@ function createAcpTransportManager(options) {
|
|
|
8296
8272
|
}
|
|
8297
8273
|
async function drivePrompt(context, onDelta) {
|
|
8298
8274
|
const commandLine = resolveAcpCommand(context.route, options);
|
|
8299
|
-
|
|
8300
|
-
return await runWithCommand(context, commandLine, onDelta);
|
|
8301
|
-
} catch (error) {
|
|
8302
|
-
if (!isGeminiAcpFallbackCandidate(context.route, commandLine)) {
|
|
8303
|
-
throw error;
|
|
8304
|
-
}
|
|
8305
|
-
return await runWithCommand(
|
|
8306
|
-
context,
|
|
8307
|
-
commandLine.replace("--acp", "--experimental-acp"),
|
|
8308
|
-
onDelta
|
|
8309
|
-
);
|
|
8310
|
-
}
|
|
8275
|
+
return await runWithCommand(context, commandLine, onDelta);
|
|
8311
8276
|
}
|
|
8312
8277
|
return {
|
|
8313
8278
|
async runPrompt(context) {
|
|
@@ -8376,17 +8341,7 @@ function createAcpTransportManager(options) {
|
|
|
8376
8341
|
},
|
|
8377
8342
|
async discoverModels(context) {
|
|
8378
8343
|
const commandLine = resolveAcpCommand(context.route, options);
|
|
8379
|
-
|
|
8380
|
-
return await discoverWithCommand(context, commandLine);
|
|
8381
|
-
} catch (error) {
|
|
8382
|
-
if (!isGeminiAcpFallbackCandidate(context.route, commandLine)) {
|
|
8383
|
-
throw error;
|
|
8384
|
-
}
|
|
8385
|
-
return await discoverWithCommand(
|
|
8386
|
-
context,
|
|
8387
|
-
commandLine.replace("--acp", "--experimental-acp")
|
|
8388
|
-
);
|
|
8389
|
-
}
|
|
8344
|
+
return await discoverWithCommand(context, commandLine);
|
|
8390
8345
|
},
|
|
8391
8346
|
async dispose() {
|
|
8392
8347
|
const values = [...connectionPools.values()].flat();
|
|
@@ -8410,150 +8365,7 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
8410
8365
|
import fs2 from "node:fs/promises";
|
|
8411
8366
|
import os2 from "node:os";
|
|
8412
8367
|
import path2 from "node:path";
|
|
8413
|
-
|
|
8414
|
-
// src/cli-presets.ts
|
|
8415
|
-
var AI_CONNECT_DEFAULT_CLI_PRESETS = {
|
|
8416
|
-
gemini: {
|
|
8417
|
-
id: "gemini",
|
|
8418
|
-
label: "Gemini CLI",
|
|
8419
|
-
command: "gemini",
|
|
8420
|
-
transportId: "gemini-cli",
|
|
8421
|
-
options: {
|
|
8422
|
-
preset: "gemini",
|
|
8423
|
-
argsTemplate: ["-p", "{prompt}", "--output-format", "json", "--model", "{model}"],
|
|
8424
|
-
discovery: {
|
|
8425
|
-
via: "acp",
|
|
8426
|
-
acp: {
|
|
8427
|
-
transportId: "gemini-acp"
|
|
8428
|
-
}
|
|
8429
|
-
},
|
|
8430
|
-
parser: {
|
|
8431
|
-
kind: "json",
|
|
8432
|
-
textPath: "response",
|
|
8433
|
-
usagePath: "stats",
|
|
8434
|
-
errorPath: "error"
|
|
8435
|
-
}
|
|
8436
|
-
}
|
|
8437
|
-
},
|
|
8438
|
-
qwen: {
|
|
8439
|
-
id: "qwen",
|
|
8440
|
-
label: "Qwen CLI",
|
|
8441
|
-
command: "qwen",
|
|
8442
|
-
transportId: "qwen-cli",
|
|
8443
|
-
options: {
|
|
8444
|
-
preset: "qwen",
|
|
8445
|
-
argsTemplate: ["-p", "{prompt}", "--output-format", "json", "--model", "{model}"],
|
|
8446
|
-
discovery: {
|
|
8447
|
-
via: "acp",
|
|
8448
|
-
acp: {
|
|
8449
|
-
transportId: "qwen-acp"
|
|
8450
|
-
}
|
|
8451
|
-
},
|
|
8452
|
-
parser: {
|
|
8453
|
-
kind: "json",
|
|
8454
|
-
textPath: "__preset__:qwen.result",
|
|
8455
|
-
usagePath: "__preset__:qwen.stats",
|
|
8456
|
-
errorPath: "__preset__:qwen.error"
|
|
8457
|
-
}
|
|
8458
|
-
}
|
|
8459
|
-
},
|
|
8460
|
-
claude: {
|
|
8461
|
-
id: "claude",
|
|
8462
|
-
label: "Claude CLI",
|
|
8463
|
-
command: "claude",
|
|
8464
|
-
transportId: "claude-cli",
|
|
8465
|
-
options: {
|
|
8466
|
-
preset: "claude",
|
|
8467
|
-
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
8468
|
-
discovery: {
|
|
8469
|
-
via: "acp",
|
|
8470
|
-
acp: {
|
|
8471
|
-
transportId: "claude-code-acp"
|
|
8472
|
-
}
|
|
8473
|
-
},
|
|
8474
|
-
parser: {
|
|
8475
|
-
kind: "json",
|
|
8476
|
-
textPath: "__preset__:claude.result",
|
|
8477
|
-
usagePath: "usage",
|
|
8478
|
-
errorPath: "__preset__:claude.error"
|
|
8479
|
-
}
|
|
8480
|
-
}
|
|
8481
|
-
},
|
|
8482
|
-
openclaude: {
|
|
8483
|
-
id: "openclaude",
|
|
8484
|
-
label: "OpenClaude CLI",
|
|
8485
|
-
command: "openclaude",
|
|
8486
|
-
transportId: "openclaude-cli",
|
|
8487
|
-
options: {
|
|
8488
|
-
preset: "openclaude",
|
|
8489
|
-
argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
|
|
8490
|
-
parser: {
|
|
8491
|
-
kind: "json",
|
|
8492
|
-
textPath: "__preset__:claude.result",
|
|
8493
|
-
usagePath: "usage",
|
|
8494
|
-
errorPath: "__preset__:claude.error"
|
|
8495
|
-
}
|
|
8496
|
-
}
|
|
8497
|
-
},
|
|
8498
|
-
codex: {
|
|
8499
|
-
id: "codex",
|
|
8500
|
-
label: "Codex CLI",
|
|
8501
|
-
command: "codex",
|
|
8502
|
-
transportId: "codex-cli",
|
|
8503
|
-
options: {
|
|
8504
|
-
preset: "codex",
|
|
8505
|
-
argsTemplate: [
|
|
8506
|
-
"exec",
|
|
8507
|
-
"--json",
|
|
8508
|
-
"--output-last-message",
|
|
8509
|
-
"{output_file}",
|
|
8510
|
-
"--model",
|
|
8511
|
-
"{model}",
|
|
8512
|
-
"{prompt}"
|
|
8513
|
-
],
|
|
8514
|
-
discovery: {
|
|
8515
|
-
via: "acp",
|
|
8516
|
-
acp: {
|
|
8517
|
-
transportId: "codex-acp"
|
|
8518
|
-
}
|
|
8519
|
-
},
|
|
8520
|
-
parser: {
|
|
8521
|
-
kind: "jsonl",
|
|
8522
|
-
text: {
|
|
8523
|
-
path: "__preset__:codex.text"
|
|
8524
|
-
},
|
|
8525
|
-
usage: {
|
|
8526
|
-
path: "__preset__:codex.usage"
|
|
8527
|
-
},
|
|
8528
|
-
error: {
|
|
8529
|
-
path: "__preset__:codex.error"
|
|
8530
|
-
}
|
|
8531
|
-
}
|
|
8532
|
-
}
|
|
8533
|
-
}
|
|
8534
|
-
};
|
|
8535
|
-
var AI_CONNECT_DEFAULT_CLI_COMMANDS = {
|
|
8536
|
-
"anthropic:claude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.claude.command,
|
|
8537
|
-
"anthropic:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
8538
|
-
"openclaude:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
|
|
8539
|
-
"openai:codex-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.codex.command,
|
|
8540
|
-
"gemini:gemini-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.gemini.command,
|
|
8541
|
-
"qwen:qwen-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.qwen.command
|
|
8542
|
-
};
|
|
8543
|
-
function getCliTransportPreset(presetId) {
|
|
8544
|
-
return AI_CONNECT_DEFAULT_CLI_PRESETS[presetId];
|
|
8545
|
-
}
|
|
8546
|
-
|
|
8547
|
-
// src/cli.ts
|
|
8548
8368
|
var CLI_PRESET_ACP_DISCOVERY_DEFAULTS = {
|
|
8549
|
-
gemini: {
|
|
8550
|
-
transportId: "gemini-acp",
|
|
8551
|
-
providerId: "gemini"
|
|
8552
|
-
},
|
|
8553
|
-
qwen: {
|
|
8554
|
-
transportId: "qwen-acp",
|
|
8555
|
-
providerId: "qwen"
|
|
8556
|
-
},
|
|
8557
8369
|
claude: {
|
|
8558
8370
|
transportId: "claude-code-acp",
|
|
8559
8371
|
providerId: "anthropic"
|
|
@@ -8583,25 +8395,26 @@ function splitCommandLine2(commandLine) {
|
|
|
8583
8395
|
function quoteArg(value) {
|
|
8584
8396
|
return /\s/.test(value) ? JSON.stringify(value) : value;
|
|
8585
8397
|
}
|
|
8586
|
-
function buildCliPrompt(context) {
|
|
8398
|
+
function buildCliPrompt(context, fileInputEnabled) {
|
|
8587
8399
|
if (context.request.operation !== "text") {
|
|
8588
8400
|
throw new AiConnectError(
|
|
8589
8401
|
"not_supported",
|
|
8590
8402
|
`CLI transport "${context.route.transport.id}" only supports text requests.`
|
|
8591
8403
|
);
|
|
8592
8404
|
}
|
|
8593
|
-
if (context.request.image
|
|
8594
|
-
(file) => portableFileCategory(file) === "image" || portableFileCategory(file) === "document"
|
|
8595
|
-
)) {
|
|
8405
|
+
if (context.request.image) {
|
|
8596
8406
|
throw new AiConnectError(
|
|
8597
8407
|
"unsupported_capability",
|
|
8598
|
-
`CLI transport "${context.route.transport.id}" does not support image
|
|
8408
|
+
`CLI transport "${context.route.transport.id}" does not support image output.`
|
|
8599
8409
|
);
|
|
8600
8410
|
}
|
|
8601
|
-
if (context.request.attachments.length > 0) {
|
|
8411
|
+
if (!fileInputEnabled && context.request.attachments.length > 0) {
|
|
8412
|
+
const hasBinary = context.request.attachments.some(
|
|
8413
|
+
(file) => portableFileCategory(file) === "image" || portableFileCategory(file) === "document"
|
|
8414
|
+
);
|
|
8602
8415
|
throw new AiConnectError(
|
|
8603
|
-
"not_supported",
|
|
8604
|
-
`CLI transport "${context.route.transport.id}" does not support attachments.`
|
|
8416
|
+
hasBinary ? "unsupported_capability" : "not_supported",
|
|
8417
|
+
hasBinary ? `CLI transport "${context.route.transport.id}" does not support image or document inputs.` : `CLI transport "${context.route.transport.id}" does not support attachments.`
|
|
8605
8418
|
);
|
|
8606
8419
|
}
|
|
8607
8420
|
return context.request.messages.map((message) => {
|
|
@@ -8660,10 +8473,8 @@ function buildCliCwd(context, options) {
|
|
|
8660
8473
|
}
|
|
8661
8474
|
function defaultCliPresetIdForRoute(route) {
|
|
8662
8475
|
switch (route.transport.id) {
|
|
8663
|
-
case "
|
|
8664
|
-
return "
|
|
8665
|
-
case "qwen-cli":
|
|
8666
|
-
return "qwen";
|
|
8476
|
+
case "pi-cli":
|
|
8477
|
+
return "pi";
|
|
8667
8478
|
case "claude-cli":
|
|
8668
8479
|
return "claude";
|
|
8669
8480
|
case "openclaude-cli":
|
|
@@ -8748,11 +8559,16 @@ function createCliDiscoveryAcpRoute(route) {
|
|
|
8748
8559
|
function resolveCliTransportOptions(route) {
|
|
8749
8560
|
const presetId = route.transport.cli?.preset ?? defaultCliPresetIdForRoute(route);
|
|
8750
8561
|
const preset = presetId ? getCliTransportPreset(presetId) : void 0;
|
|
8562
|
+
const mergedFileInput = preset?.options.fileInput || route.transport.cli?.fileInput ? {
|
|
8563
|
+
...preset?.options.fileInput ?? {},
|
|
8564
|
+
...route.transport.cli?.fileInput ?? {}
|
|
8565
|
+
} : void 0;
|
|
8751
8566
|
const merged = {
|
|
8752
8567
|
...preset?.options ?? {},
|
|
8753
8568
|
...route.transport.cli ?? {},
|
|
8754
8569
|
...route.transport.cli?.argsTemplate ? { argsTemplate: route.transport.cli.argsTemplate } : {},
|
|
8755
8570
|
...route.transport.cli?.parser ? { parser: route.transport.cli.parser } : {},
|
|
8571
|
+
...mergedFileInput ? { fileInput: mergedFileInput } : {},
|
|
8756
8572
|
...presetId ? { preset: presetId } : {}
|
|
8757
8573
|
};
|
|
8758
8574
|
if (!merged.argsTemplate?.length) {
|
|
@@ -8763,15 +8579,73 @@ function resolveCliTransportOptions(route) {
|
|
|
8763
8579
|
}
|
|
8764
8580
|
return merged;
|
|
8765
8581
|
}
|
|
8766
|
-
async function
|
|
8582
|
+
async function createCliTempDir(baseDir, prefix) {
|
|
8767
8583
|
const tempDir = await fs2.mkdtemp(
|
|
8768
|
-
path2.join(os2.tmpdir(), "ai-connect-cli-
|
|
8584
|
+
path2.join(baseDir ?? os2.tmpdir(), prefix ?? "ai-connect-cli-")
|
|
8769
8585
|
);
|
|
8770
8586
|
return {
|
|
8771
8587
|
tempDir,
|
|
8772
8588
|
outputFile: path2.join(tempDir, "last-message.txt")
|
|
8773
8589
|
};
|
|
8774
8590
|
}
|
|
8591
|
+
function safeStagedBasename(name, index, mimeType) {
|
|
8592
|
+
const base = path2.basename(name.replace(/\\/g, "/"));
|
|
8593
|
+
if (base === "" || base === "." || base === "..") {
|
|
8594
|
+
return `attachment-${index}.${extensionForMime(mimeType)}`;
|
|
8595
|
+
}
|
|
8596
|
+
return base;
|
|
8597
|
+
}
|
|
8598
|
+
async function stageCliAttachments(attachments, attachDir, fileInput) {
|
|
8599
|
+
const categories = new Set(
|
|
8600
|
+
fileInput.categories ?? ["image", "document", "text", "other"]
|
|
8601
|
+
);
|
|
8602
|
+
await fs2.mkdir(attachDir, { recursive: true });
|
|
8603
|
+
const refs = [];
|
|
8604
|
+
const usedNames = /* @__PURE__ */ new Map();
|
|
8605
|
+
for (let i = 0; i < attachments.length; i += 1) {
|
|
8606
|
+
const file = preparePortableFile(attachments[i]);
|
|
8607
|
+
const payload = await materializePortableFile(file);
|
|
8608
|
+
if (!categories.has(payload.category)) {
|
|
8609
|
+
continue;
|
|
8610
|
+
}
|
|
8611
|
+
let destName = safeStagedBasename(payload.name, i, payload.mimeType);
|
|
8612
|
+
const seen = usedNames.get(destName);
|
|
8613
|
+
if (seen !== void 0) {
|
|
8614
|
+
destName = `${i}-${destName}`;
|
|
8615
|
+
}
|
|
8616
|
+
usedNames.set(destName, i);
|
|
8617
|
+
const dest = path2.join(attachDir, destName);
|
|
8618
|
+
if (payload.category === "text") {
|
|
8619
|
+
if (payload.text !== void 0) {
|
|
8620
|
+
await fs2.writeFile(dest, payload.text, "utf8");
|
|
8621
|
+
refs.push(dest);
|
|
8622
|
+
continue;
|
|
8623
|
+
}
|
|
8624
|
+
} else {
|
|
8625
|
+
const bytes = payload.base64 ? Buffer.from(payload.base64, "base64") : await portableFileToBytes(file);
|
|
8626
|
+
if (bytes !== void 0) {
|
|
8627
|
+
await fs2.writeFile(dest, bytes);
|
|
8628
|
+
refs.push(dest);
|
|
8629
|
+
continue;
|
|
8630
|
+
}
|
|
8631
|
+
}
|
|
8632
|
+
if (payload.uri) {
|
|
8633
|
+
refs.push(payload.uri);
|
|
8634
|
+
}
|
|
8635
|
+
}
|
|
8636
|
+
return refs;
|
|
8637
|
+
}
|
|
8638
|
+
function expandFileArgs(staged, fileInput) {
|
|
8639
|
+
const perFileArgs = fileInput?.perFileArgs ?? ["@{path}"];
|
|
8640
|
+
if (fileInput?.placement === "prompt") {
|
|
8641
|
+
return [];
|
|
8642
|
+
}
|
|
8643
|
+
return staged.flatMap(
|
|
8644
|
+
(ref) => perFileArgs.map(
|
|
8645
|
+
(tpl) => tpl.replace("{path}", ref).replace("{name}", path2.basename(ref))
|
|
8646
|
+
)
|
|
8647
|
+
);
|
|
8648
|
+
}
|
|
8775
8649
|
function materializeTemplateArg(templateArg, values, parameterKeys) {
|
|
8776
8650
|
switch (templateArg) {
|
|
8777
8651
|
case "{prompt}":
|
|
@@ -8798,31 +8672,62 @@ function materializeTemplateArg(templateArg, values, parameterKeys) {
|
|
|
8798
8672
|
}
|
|
8799
8673
|
async function buildCliInvocation(context, options) {
|
|
8800
8674
|
const route = context.route;
|
|
8801
|
-
const prompt = buildCliPrompt(context);
|
|
8802
8675
|
const cliOptions = resolveCliTransportOptions(route);
|
|
8676
|
+
const fileInput = cliOptions.fileInput;
|
|
8677
|
+
const attachments = context.request.attachments;
|
|
8678
|
+
const hasStageable = Boolean(fileInput) && attachments.length > 0;
|
|
8679
|
+
let prompt = buildCliPrompt(context, Boolean(fileInput));
|
|
8803
8680
|
const commandLine = resolveCliCommand(route, options);
|
|
8804
8681
|
const resolved = splitCommandLine2(commandLine);
|
|
8805
8682
|
const parameterKeys = [];
|
|
8806
8683
|
let tempDir;
|
|
8807
8684
|
let outputFile;
|
|
8808
|
-
|
|
8809
|
-
|
|
8685
|
+
const needsOutputFile = cliOptions.argsTemplate.includes("{output_file}");
|
|
8686
|
+
if (needsOutputFile || hasStageable) {
|
|
8687
|
+
const created = await createCliTempDir(
|
|
8688
|
+
fileInput?.stagingDir ?? options?.staging?.dir,
|
|
8689
|
+
options?.staging?.prefix
|
|
8690
|
+
);
|
|
8810
8691
|
tempDir = created.tempDir;
|
|
8811
8692
|
outputFile = created.outputFile;
|
|
8812
8693
|
}
|
|
8694
|
+
let staged = [];
|
|
8695
|
+
if (hasStageable && tempDir && fileInput) {
|
|
8696
|
+
staged = await stageCliAttachments(
|
|
8697
|
+
attachments,
|
|
8698
|
+
path2.join(tempDir, "attachments"),
|
|
8699
|
+
fileInput
|
|
8700
|
+
);
|
|
8701
|
+
if (fileInput.placement === "prompt" && staged.length > 0) {
|
|
8702
|
+
const separator = fileInput.separator ?? " ";
|
|
8703
|
+
const mentionTemplate = fileInput.mentionTemplate ?? "@{path}";
|
|
8704
|
+
const mentions = staged.map(
|
|
8705
|
+
(ref) => mentionTemplate.replace("{path}", ref).replace("{name}", path2.basename(ref))
|
|
8706
|
+
).join(separator);
|
|
8707
|
+
prompt = `${prompt}${separator}${mentions}`;
|
|
8708
|
+
}
|
|
8709
|
+
}
|
|
8813
8710
|
const args = [
|
|
8814
8711
|
...resolved.args,
|
|
8815
|
-
...cliOptions.argsTemplate.
|
|
8816
|
-
(part)
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8712
|
+
...cliOptions.argsTemplate.flatMap((part) => {
|
|
8713
|
+
if (part === "{files}") {
|
|
8714
|
+
if (staged.length > 0) {
|
|
8715
|
+
parameterKeys.push("{files}");
|
|
8716
|
+
}
|
|
8717
|
+
return expandFileArgs(staged, fileInput);
|
|
8718
|
+
}
|
|
8719
|
+
return [
|
|
8720
|
+
materializeTemplateArg(
|
|
8721
|
+
part,
|
|
8722
|
+
{
|
|
8723
|
+
prompt,
|
|
8724
|
+
model: route.model,
|
|
8725
|
+
...outputFile ? { outputFile } : {}
|
|
8726
|
+
},
|
|
8727
|
+
parameterKeys
|
|
8728
|
+
)
|
|
8729
|
+
];
|
|
8730
|
+
})
|
|
8826
8731
|
];
|
|
8827
8732
|
return {
|
|
8828
8733
|
command: resolved.command,
|
|
@@ -8831,6 +8736,7 @@ async function buildCliInvocation(context, options) {
|
|
|
8831
8736
|
env: buildCliEnvironment(options),
|
|
8832
8737
|
...tempDir ? { tempDir } : {},
|
|
8833
8738
|
...outputFile ? { outputFile } : {},
|
|
8739
|
+
...options?.staging?.keep ? { keepStaging: true } : {},
|
|
8834
8740
|
parameterKeys
|
|
8835
8741
|
};
|
|
8836
8742
|
}
|
|
@@ -9042,60 +8948,6 @@ function parseCliModelList(stdout, parser, selector) {
|
|
|
9042
8948
|
}
|
|
9043
8949
|
return models;
|
|
9044
8950
|
}
|
|
9045
|
-
function parseGeminiCli(stdout) {
|
|
9046
|
-
const payload = JSON.parse(stdout);
|
|
9047
|
-
if (typeof payload.error === "string") {
|
|
9048
|
-
throw new AiConnectError("temporary_unavailable", payload.error);
|
|
9049
|
-
}
|
|
9050
|
-
if (typeof payload.response !== "string") {
|
|
9051
|
-
throw new AiConnectError(
|
|
9052
|
-
"temporary_unavailable",
|
|
9053
|
-
"Gemini CLI JSON output did not include response text."
|
|
9054
|
-
);
|
|
9055
|
-
}
|
|
9056
|
-
const usage = payload.stats ? statsToUsage(payload.stats) : void 0;
|
|
9057
|
-
return {
|
|
9058
|
-
text: payload.response,
|
|
9059
|
-
...usage ? { usage } : {},
|
|
9060
|
-
data: payload
|
|
9061
|
-
};
|
|
9062
|
-
}
|
|
9063
|
-
function parseQwenCli(stdout) {
|
|
9064
|
-
const payload = JSON.parse(stdout);
|
|
9065
|
-
if (!Array.isArray(payload)) {
|
|
9066
|
-
throw new AiConnectError(
|
|
9067
|
-
"temporary_unavailable",
|
|
9068
|
-
"Qwen CLI JSON output did not return a message array."
|
|
9069
|
-
);
|
|
9070
|
-
}
|
|
9071
|
-
const resultMessage = payload.find(
|
|
9072
|
-
(entry) => entry && typeof entry === "object" && entry.type === "result"
|
|
9073
|
-
);
|
|
9074
|
-
if (!resultMessage) {
|
|
9075
|
-
throw new AiConnectError(
|
|
9076
|
-
"temporary_unavailable",
|
|
9077
|
-
"Qwen CLI JSON output did not contain a result message."
|
|
9078
|
-
);
|
|
9079
|
-
}
|
|
9080
|
-
if (resultMessage.is_error === true) {
|
|
9081
|
-
throw new AiConnectError(
|
|
9082
|
-
"temporary_unavailable",
|
|
9083
|
-
typeof resultMessage.result === "string" ? resultMessage.result : "Qwen CLI returned an error result."
|
|
9084
|
-
);
|
|
9085
|
-
}
|
|
9086
|
-
if (typeof resultMessage.result !== "string") {
|
|
9087
|
-
throw new AiConnectError(
|
|
9088
|
-
"temporary_unavailable",
|
|
9089
|
-
"Qwen CLI result message did not contain text output."
|
|
9090
|
-
);
|
|
9091
|
-
}
|
|
9092
|
-
const usage = resultMessage.stats ? statsToUsage(resultMessage.stats) : void 0;
|
|
9093
|
-
return {
|
|
9094
|
-
text: resultMessage.result,
|
|
9095
|
-
...usage ? { usage } : {},
|
|
9096
|
-
data: payload
|
|
9097
|
-
};
|
|
9098
|
-
}
|
|
9099
8951
|
function parseClaudeCli(stdout) {
|
|
9100
8952
|
const payload = JSON.parse(stdout);
|
|
9101
8953
|
const text = typeof payload.result === "string" ? payload.result : typeof payload.response === "string" ? payload.response : typeof payload.text === "string" ? payload.text : void 0;
|
|
@@ -9181,10 +9033,8 @@ function parseCliResult(route, result, outputFileContent) {
|
|
|
9181
9033
|
}
|
|
9182
9034
|
}
|
|
9183
9035
|
switch (cliOptions.preset) {
|
|
9184
|
-
case "
|
|
9185
|
-
return
|
|
9186
|
-
case "qwen":
|
|
9187
|
-
return parseQwenCli(stdout);
|
|
9036
|
+
case "pi":
|
|
9037
|
+
return parseTextCli(stdout, { kind: "text" });
|
|
9188
9038
|
case "claude":
|
|
9189
9039
|
case "openclaude":
|
|
9190
9040
|
return parseClaudeCli(stdout);
|
|
@@ -9275,7 +9125,7 @@ async function executeCliInvocation(invocation, timeoutMs, phases, signal) {
|
|
|
9275
9125
|
});
|
|
9276
9126
|
}
|
|
9277
9127
|
async function cleanupCliInvocation(invocation) {
|
|
9278
|
-
if (invocation.tempDir) {
|
|
9128
|
+
if (invocation.tempDir && !invocation.keepStaging) {
|
|
9279
9129
|
await fs2.rm(invocation.tempDir, { recursive: true, force: true });
|
|
9280
9130
|
}
|
|
9281
9131
|
}
|