zidane 5.13.0 → 5.13.2
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 +15 -0
- package/dist/acp-BqIU2mo-.js +1410 -0
- package/dist/acp-BqIU2mo-.js.map +1 -0
- package/dist/acp-cli.d.ts +1 -0
- package/dist/acp-cli.js +713 -0
- package/dist/acp-cli.js.map +1 -0
- package/dist/acp.d.ts +655 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +2 -0
- package/dist/{agent-Db4ojCSV.d.ts → agent-D7ZL8B2X.d.ts} +2 -2
- package/dist/{agent-Db4ojCSV.d.ts.map → agent-D7ZL8B2X.d.ts.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +6 -6
- package/dist/chat.js +3 -2
- package/dist/chat.js.map +1 -1
- package/dist/contexts/daytona.d.ts +3 -3
- package/dist/contexts/docker.d.ts +1 -1
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +4 -1
- package/dist/contexts/docker.js.map +1 -1
- package/dist/contexts/e2b.d.ts +2 -2
- package/dist/{contexts-VhV4Af8x.js → contexts-DHi8LPCp.js} +25 -9
- package/dist/contexts-DHi8LPCp.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +3 -3
- package/dist/glob-DCWXy_tr.js +128 -0
- package/dist/glob-DCWXy_tr.js.map +1 -0
- package/dist/{headless-tVN-g6IR.js → headless-0O6HMNBQ.js} +6 -6
- package/dist/{headless-tVN-g6IR.js.map → headless-0O6HMNBQ.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-BEblm0Hu.d.ts → index-BsyPeCSL.d.ts} +3 -3
- package/dist/{index-BEblm0Hu.d.ts.map → index-BsyPeCSL.d.ts.map} +1 -1
- package/dist/{index-CJ-2g7bY.d.ts → index-CDcQW-2S.d.ts} +3 -3
- package/dist/index-CDcQW-2S.d.ts.map +1 -0
- package/dist/{index-CrMb8jCE.d.ts → index-CF15aqlk.d.ts} +3 -3
- package/dist/{index-CrMb8jCE.d.ts.map → index-CF15aqlk.d.ts.map} +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -7
- package/dist/lazy-DLOurOC_.js +20 -0
- package/dist/lazy-DLOurOC_.js.map +1 -0
- package/dist/{logger-Dcrj48qY.d.ts → logger-DItaCwPw.d.ts} +2 -2
- package/dist/{logger-Dcrj48qY.d.ts.map → logger-DItaCwPw.d.ts.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/{messages-CGazSyTL.js → messages-DEsLGBB9.js} +2 -2
- package/dist/{messages-CGazSyTL.js.map → messages-DEsLGBB9.js.map} +1 -1
- package/dist/output/stream-json.d.ts +2 -2
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +2 -2
- package/dist/{presets-kPEMOCmE.js → presets-HDIxliiq.js} +2 -2
- package/dist/{presets-kPEMOCmE.js.map → presets-HDIxliiq.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-Bo2biCyT.js → providers-Cz-RNYZO.js} +7 -13
- package/dist/providers-Cz-RNYZO.js.map +1 -0
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/restate.d.ts +2 -2
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-B69BQSn1.js → session-BDWZZaYa.js} +2 -2
- package/dist/{session-B69BQSn1.js.map → session-BDWZZaYa.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/{tool-formatters-CkqBgPH4.d.ts → tool-formatters-CNSMadtp.d.ts} +2 -2
- package/dist/{tool-formatters-CkqBgPH4.d.ts.map → tool-formatters-CNSMadtp.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/{tools-5Bnlq68O.js → tools-DhzKzB1y.js} +39 -56
- package/dist/tools-DhzKzB1y.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-D4PwUMyO.js → transcript-anchors-Cq-8gx8u.js} +9 -1417
- package/dist/transcript-anchors-Cq-8gx8u.js.map +1 -0
- package/dist/{transcript-anchors-BnLZmASt.d.ts → transcript-anchors-EG-SmZRu.d.ts} +4 -4
- package/dist/{transcript-anchors-BnLZmASt.d.ts.map → transcript-anchors-EG-SmZRu.d.ts.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.js +7 -6
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-B6FaQAZN.d.ts → turn-operations-DwtWRYr1.d.ts} +3 -3
- package/dist/{turn-operations-B6FaQAZN.d.ts.map → turn-operations-DwtWRYr1.d.ts.map} +1 -1
- package/dist/{types-B39tBba1.d.ts → types-Bs2oY7Ux.d.ts} +27 -4
- package/dist/types-Bs2oY7Ux.d.ts.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/xdg-zlSeVBhQ.js +1417 -0
- package/dist/xdg-zlSeVBhQ.js.map +1 -0
- package/docs/ACP.md +221 -0
- package/package.json +11 -1
- package/dist/contexts-VhV4Af8x.js.map +0 -1
- package/dist/index-CJ-2g7bY.d.ts.map +0 -1
- package/dist/providers-Bo2biCyT.js.map +0 -1
- package/dist/tools-5Bnlq68O.js.map +0 -1
- package/dist/transcript-anchors-D4PwUMyO.js.map +0 -1
- package/dist/types-B39tBba1.d.ts.map +0 -1
package/dist/acp-cli.js
ADDED
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { b as openaiCompat } from "./messages-DEsLGBB9.js";
|
|
3
|
+
import { n as createSandboxContext, r as createProcessContext } from "./contexts-DHi8LPCp.js";
|
|
4
|
+
import { i as basic_default } from "./presets-HDIxliiq.js";
|
|
5
|
+
import "./session-BDWZZaYa.js";
|
|
6
|
+
import { f as arcee, i as openai, l as cerebras, p as anthropic, r as openrouter, t as xai, u as baseten } from "./providers-Cz-RNYZO.js";
|
|
7
|
+
import { t as runAcpStdioServer } from "./acp-BqIU2mo-.js";
|
|
8
|
+
import { f as credKeyOf, i as readCredentials, l as BUILTIN_PROVIDERS, r as credentialsPath, t as resolveStorageDirs } from "./xdg-zlSeVBhQ.js";
|
|
9
|
+
import { createDaytonaProvider } from "./contexts/daytona.js";
|
|
10
|
+
import { createDockerContext } from "./contexts/docker.js";
|
|
11
|
+
import { createE2BProvider } from "./contexts/e2b.js";
|
|
12
|
+
import "./session/sqlite.js";
|
|
13
|
+
import { basename } from "node:path";
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { parseArgs } from "node:util";
|
|
16
|
+
//#region src/acp/credential-bridge.ts
|
|
17
|
+
/**
|
|
18
|
+
* Bridge the user's existing zidane credential store into the process
|
|
19
|
+
* environment so the ACP agent authenticates with the SAME login the TUI /
|
|
20
|
+
* `zidane auth` already set up — no secrets in the editor's `settings.json`.
|
|
21
|
+
*
|
|
22
|
+
* ACP gives the client (e.g. Zed) no way to forward its own provider API keys
|
|
23
|
+
* to an external agent: provider/auth is agent-owned. The only client-driven
|
|
24
|
+
* channel is the `agent_servers.<name>.env` block, i.e. literal secrets in
|
|
25
|
+
* settings. This bridge removes that need by reading the on-disk store the rest
|
|
26
|
+
* of zidane manages:
|
|
27
|
+
*
|
|
28
|
+
* - OAuth providers (Anthropic / OpenAI Codex / xAI): point
|
|
29
|
+
* `ZIDANE_CREDENTIALS_PATH` at the store so the harness providers read and
|
|
30
|
+
* refresh from it.
|
|
31
|
+
* - API-key providers (OpenRouter / Cerebras / Baseten / Arcee / …): inject
|
|
32
|
+
* the stored key into the provider's env var.
|
|
33
|
+
*
|
|
34
|
+
* Precedence is "explicit env wins": an env var already set (e.g. by the
|
|
35
|
+
* editor's `env` block) is never overwritten — the store only FILLS GAPS. The
|
|
36
|
+
* whole bridge no-ops when `ZIDANE_CREDENTIALS_PATH` is already set (the caller
|
|
37
|
+
* is managing credentials explicitly) or when no store file exists (so
|
|
38
|
+
* `~/.credentials.json` from a legacy `bun run auth` keeps working untouched).
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Apply the stored credentials to `env`. Returns the bridged file path, or
|
|
42
|
+
* `null` when nothing was applied (no store, or an explicit path override).
|
|
43
|
+
*/
|
|
44
|
+
function bridgeStoredCredentials(options = {}) {
|
|
45
|
+
const env = options.env ?? process.env;
|
|
46
|
+
if (env.ZIDANE_CREDENTIALS_PATH) return null;
|
|
47
|
+
const dataDir = options.dataDir ?? resolveStorageDirs({ env }).configDir;
|
|
48
|
+
const path = credentialsPath(dataDir);
|
|
49
|
+
if (!existsSync(path)) return null;
|
|
50
|
+
env.ZIDANE_CREDENTIALS_PATH = path;
|
|
51
|
+
const creds = readCredentials(dataDir);
|
|
52
|
+
for (const descriptor of Object.values(BUILTIN_PROVIDERS)) {
|
|
53
|
+
const cred = creds[credKeyOf(descriptor)];
|
|
54
|
+
if (cred?.kind !== "apikey") continue;
|
|
55
|
+
if (descriptor.envKey && cred.value && !env[descriptor.envKey]) env[descriptor.envKey] = cred.value;
|
|
56
|
+
for (const field of descriptor.customFields ?? []) {
|
|
57
|
+
const value = cred.customFields?.[field.key];
|
|
58
|
+
if (value && !env[field.envVar]) env[field.envVar] = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return path;
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/start/shared-options.ts
|
|
65
|
+
const validThinkingLevels = [
|
|
66
|
+
"off",
|
|
67
|
+
"minimal",
|
|
68
|
+
"low",
|
|
69
|
+
"medium",
|
|
70
|
+
"high",
|
|
71
|
+
"xhigh",
|
|
72
|
+
"max",
|
|
73
|
+
"adaptive"
|
|
74
|
+
];
|
|
75
|
+
const startProviderNames = [
|
|
76
|
+
"anthropic",
|
|
77
|
+
"openai",
|
|
78
|
+
"openrouter",
|
|
79
|
+
"cerebras",
|
|
80
|
+
"xai",
|
|
81
|
+
"arcee",
|
|
82
|
+
"baseten",
|
|
83
|
+
"local",
|
|
84
|
+
"openai-compat"
|
|
85
|
+
];
|
|
86
|
+
const startPresetNames = ["basic"];
|
|
87
|
+
const startFormatNames = ["zidane", "provider"];
|
|
88
|
+
var StartUsageError = class extends Error {
|
|
89
|
+
constructor(message) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.name = "StartUsageError";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The shared `bun start` / `zidane run` option table. Exported so
|
|
96
|
+
* `parseStartArgs` (start CLI) can compose it with the Restate-only options
|
|
97
|
+
* and parse argv ONCE — two diverging tables would let new flags silently
|
|
98
|
+
* drift between the two parsers.
|
|
99
|
+
*/
|
|
100
|
+
const startLocalArgOptions = {
|
|
101
|
+
"help": {
|
|
102
|
+
type: "boolean",
|
|
103
|
+
short: "h",
|
|
104
|
+
default: false
|
|
105
|
+
},
|
|
106
|
+
"prompt": {
|
|
107
|
+
type: "string",
|
|
108
|
+
short: "p"
|
|
109
|
+
},
|
|
110
|
+
"model": {
|
|
111
|
+
type: "string",
|
|
112
|
+
short: "m"
|
|
113
|
+
},
|
|
114
|
+
"preset": {
|
|
115
|
+
type: "string",
|
|
116
|
+
short: "t",
|
|
117
|
+
default: "basic"
|
|
118
|
+
},
|
|
119
|
+
"system": {
|
|
120
|
+
type: "string",
|
|
121
|
+
short: "s"
|
|
122
|
+
},
|
|
123
|
+
"thinking": {
|
|
124
|
+
type: "string",
|
|
125
|
+
default: "off"
|
|
126
|
+
},
|
|
127
|
+
"provider": {
|
|
128
|
+
type: "string",
|
|
129
|
+
default: "anthropic"
|
|
130
|
+
},
|
|
131
|
+
"base-url": { type: "string" },
|
|
132
|
+
"api-key-env": { type: "string" },
|
|
133
|
+
"headers-env": { type: "string" },
|
|
134
|
+
"header": {
|
|
135
|
+
type: "string",
|
|
136
|
+
multiple: true
|
|
137
|
+
},
|
|
138
|
+
"vision": { type: "boolean" },
|
|
139
|
+
"image-in-tool-result": { type: "boolean" },
|
|
140
|
+
"temperature": { type: "string" },
|
|
141
|
+
"seed": { type: "string" },
|
|
142
|
+
"context": {
|
|
143
|
+
type: "string",
|
|
144
|
+
short: "c",
|
|
145
|
+
default: "process"
|
|
146
|
+
},
|
|
147
|
+
"image": { type: "string" },
|
|
148
|
+
"cwd": { type: "string" },
|
|
149
|
+
"env": {
|
|
150
|
+
type: "string",
|
|
151
|
+
multiple: true
|
|
152
|
+
},
|
|
153
|
+
"pass-env": {
|
|
154
|
+
type: "string",
|
|
155
|
+
multiple: true
|
|
156
|
+
},
|
|
157
|
+
"pregame": { type: "string" },
|
|
158
|
+
"sandbox": { type: "string" },
|
|
159
|
+
"sandbox-on-destroy": { type: "string" },
|
|
160
|
+
"mcp": { type: "string" },
|
|
161
|
+
"session-db": { type: "string" },
|
|
162
|
+
"session-id": { type: "string" },
|
|
163
|
+
"json": {
|
|
164
|
+
type: "boolean",
|
|
165
|
+
default: false
|
|
166
|
+
},
|
|
167
|
+
"stream-json": {
|
|
168
|
+
type: "boolean",
|
|
169
|
+
default: false
|
|
170
|
+
},
|
|
171
|
+
"format": {
|
|
172
|
+
type: "string",
|
|
173
|
+
default: "zidane"
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Strict argv parse shared by the start CLIs. `strict: true` rejects unknown
|
|
178
|
+
* flags and stray positionals loudly — without it, a typo'd flag's value
|
|
179
|
+
* silently leaks into the run (e.g. as a bogus prompt). Parse failures are
|
|
180
|
+
* rethrown as {@link StartUsageError}.
|
|
181
|
+
*/
|
|
182
|
+
function parseStartArgValues(argv, options) {
|
|
183
|
+
try {
|
|
184
|
+
const { values } = parseArgs({
|
|
185
|
+
args: [...argv],
|
|
186
|
+
options,
|
|
187
|
+
strict: true
|
|
188
|
+
});
|
|
189
|
+
return values;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
throw new StartUsageError(`${err instanceof Error ? err.message : String(err)}\n\nRun with --help for usage.`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** Assemble {@link StartLocalOptions} from already-parsed argv values. */
|
|
195
|
+
function buildStartLocalOptions(values, config = {}) {
|
|
196
|
+
const { requirePrompt = true, usage = startLocalUsage } = config;
|
|
197
|
+
if (values.help === true) throw new StartUsageError(usage());
|
|
198
|
+
const prompt = values.prompt;
|
|
199
|
+
if (requirePrompt && (!prompt || prompt.trim() === "")) throw new StartUsageError(usage());
|
|
200
|
+
const thinkingRaw = values.thinking || "off";
|
|
201
|
+
if (!validThinkingLevels.includes(thinkingRaw)) throw new Error(`Unknown thinking level: ${thinkingRaw}. Available: ${validThinkingLevels.join(", ")}`);
|
|
202
|
+
const provider = values.provider || "anthropic";
|
|
203
|
+
if (!startProviderNames.includes(provider)) throw new Error(`Unknown provider: ${provider}. Available: ${startProviderNames.join(", ")}`);
|
|
204
|
+
const preset = values.preset || "basic";
|
|
205
|
+
if (!startPresetNames.includes(preset)) throw new Error(`Unknown preset: ${preset}. Available: ${startPresetNames.join(", ")}`);
|
|
206
|
+
if (values.json === true && values["stream-json"] === true) throw new StartUsageError("Use either `--json` or `--stream-json`, not both.");
|
|
207
|
+
const format = values.format || "zidane";
|
|
208
|
+
if (!startFormatNames.includes(format)) throw new Error(`Unknown format: ${format}. Available: ${startFormatNames.join(", ")}`);
|
|
209
|
+
const baseURL = values["base-url"];
|
|
210
|
+
const apiKeyEnv = values["api-key-env"];
|
|
211
|
+
const headersEnv = values["headers-env"];
|
|
212
|
+
const extraHeaders = parseHeaderArgs(values.header, "--header");
|
|
213
|
+
const temperature = parseOptionalNumber(values.temperature, "--temperature");
|
|
214
|
+
const seed = parseOptionalInteger(values.seed, "--seed");
|
|
215
|
+
const env = parseEnvArgs(values.env, values["pass-env"]);
|
|
216
|
+
const context = values.context || "process";
|
|
217
|
+
const pregame = loadPregame(values.pregame, context);
|
|
218
|
+
const sandboxId = resolveSandboxId(values.sandbox, context);
|
|
219
|
+
const sandboxOnDestroy = resolveSandboxOnDestroy(values["sandbox-on-destroy"], context);
|
|
220
|
+
return {
|
|
221
|
+
system: values.system,
|
|
222
|
+
prompt: prompt ?? "",
|
|
223
|
+
model: values.model,
|
|
224
|
+
...baseURL !== void 0 ? { baseURL } : {},
|
|
225
|
+
...apiKeyEnv !== void 0 ? { apiKeyEnv } : {},
|
|
226
|
+
...headersEnv !== void 0 ? { headersEnv } : {},
|
|
227
|
+
...Object.keys(extraHeaders).length > 0 ? { extraHeaders } : {},
|
|
228
|
+
...values.vision === true ? { vision: true } : {},
|
|
229
|
+
...values["image-in-tool-result"] === true ? { imageInToolResult: true } : {},
|
|
230
|
+
...temperature !== void 0 ? { temperature } : {},
|
|
231
|
+
...seed !== void 0 ? { seed } : {},
|
|
232
|
+
preset,
|
|
233
|
+
thinking: thinkingRaw,
|
|
234
|
+
provider,
|
|
235
|
+
context,
|
|
236
|
+
image: values.image,
|
|
237
|
+
cwd: values.cwd,
|
|
238
|
+
...Object.keys(env).length > 0 ? { env } : {},
|
|
239
|
+
...pregame ? { pregame } : {},
|
|
240
|
+
...sandboxId ? { sandboxId } : {},
|
|
241
|
+
...sandboxOnDestroy ? { sandboxOnDestroy } : {},
|
|
242
|
+
mcp: parseMcpConfig(values.mcp),
|
|
243
|
+
sessionDb: values["session-db"],
|
|
244
|
+
sessionId: values["session-id"],
|
|
245
|
+
json: values.json === true,
|
|
246
|
+
streamJson: values["stream-json"] === true,
|
|
247
|
+
format
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function startLocalUsage() {
|
|
251
|
+
return [
|
|
252
|
+
`Usage: bun start --prompt "your message" \\`,
|
|
253
|
+
` [--provider ${startProviderNames.join("|")}] [--model <id>] [--thinking ${validThinkingLevels.join("|")}]`,
|
|
254
|
+
` [--base-url <url>] [--api-key-env <NAME>] [--headers-env <NAME>] [--header 'Name: value']`,
|
|
255
|
+
` [--vision] [--image-in-tool-result] [--temperature <f>] [--seed <n>]`,
|
|
256
|
+
` [--context process|docker|e2b|daytona] [--image node:22] [--cwd /workspace]`,
|
|
257
|
+
` [--env KEY=VALUE ...] [--pass-env VARNAME ...] [--pregame setup.sh] [--sandbox <id>] [--sandbox-on-destroy kill|pause|leave]`,
|
|
258
|
+
` [--mcp '[...]'] [--session-db ./sessions.db] [--session-id <id>] [--json|--stream-json] [--format zidane|provider]`,
|
|
259
|
+
``,
|
|
260
|
+
` --stream-json streams COMPLETE TURNS as JSONL (one line per turn), then the result.`,
|
|
261
|
+
` For the full event stream (text deltas, tool calls, …) use the headless CLI's`,
|
|
262
|
+
` --output-format stream-json instead.`
|
|
263
|
+
].join("\n");
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Parse the `--mcp` argument: inline JSON (object or array) or a path to a JSON
|
|
267
|
+
* file. Validates that each server declares the fields its transport requires.
|
|
268
|
+
*/
|
|
269
|
+
function parseMcpConfig(raw) {
|
|
270
|
+
if (!raw) return void 0;
|
|
271
|
+
let text = raw.trim();
|
|
272
|
+
if (!text.startsWith("[") && !text.startsWith("{")) text = readFileSync(text, "utf8");
|
|
273
|
+
const parsed = JSON.parse(text);
|
|
274
|
+
const configs = Array.isArray(parsed) ? parsed : [parsed];
|
|
275
|
+
for (const config of configs) {
|
|
276
|
+
if (!config.name || !config.transport) throw new Error(`Invalid MCP config: each server needs "name" and "transport". Got: ${JSON.stringify(config)}`);
|
|
277
|
+
if (config.transport === "stdio" && !config.command) throw new Error(`MCP server "${config.name}": stdio transport requires "command"`);
|
|
278
|
+
if ((config.transport === "sse" || config.transport === "streamable-http") && !config.url) throw new Error(`MCP server "${config.name}": ${config.transport} transport requires "url"`);
|
|
279
|
+
}
|
|
280
|
+
return configs;
|
|
281
|
+
}
|
|
282
|
+
function parseOptionalNumber(raw, flag) {
|
|
283
|
+
if (raw === void 0) return void 0;
|
|
284
|
+
const value = Number(raw);
|
|
285
|
+
if (Number.isNaN(value)) throw new Error(`${flag} must be a number, got '${raw}'.`);
|
|
286
|
+
return value;
|
|
287
|
+
}
|
|
288
|
+
function parseOptionalInteger(raw, flag) {
|
|
289
|
+
if (raw === void 0) return void 0;
|
|
290
|
+
const value = Number(raw);
|
|
291
|
+
if (!Number.isInteger(value)) throw new Error(`${flag} must be an integer, got '${raw}'.`);
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Build the execution-context environment from `--pass-env VARNAME` (forward
|
|
296
|
+
* the named variable from the current shell) and `--env KEY=VALUE` (set a
|
|
297
|
+
* value explicitly). `--pass-env` is applied first so an explicit `--env`
|
|
298
|
+
* wins when both name the same key. An unset `--pass-env` variable is a hard
|
|
299
|
+
* error rather than a silently-empty value.
|
|
300
|
+
*/
|
|
301
|
+
function parseEnvArgs(envArgs, passEnvArgs, source = process.env) {
|
|
302
|
+
const env = {};
|
|
303
|
+
for (const name of passEnvArgs ?? []) {
|
|
304
|
+
const key = name.trim();
|
|
305
|
+
if (!key) throw new Error("--pass-env requires a variable name.");
|
|
306
|
+
const value = source[key];
|
|
307
|
+
if (value === void 0) throw new Error(`--pass-env ${key}: environment variable ${key} is not set.`);
|
|
308
|
+
env[key] = value;
|
|
309
|
+
}
|
|
310
|
+
for (const item of envArgs ?? []) {
|
|
311
|
+
const eq = item.indexOf("=");
|
|
312
|
+
const key = eq === -1 ? "" : item.slice(0, eq).trim();
|
|
313
|
+
if (!key) throw new Error(`--env must be formatted as KEY=VALUE, got '${item}'.`);
|
|
314
|
+
env[key] = item.slice(eq + 1);
|
|
315
|
+
}
|
|
316
|
+
return env;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Resolve the `--pregame <file>` setup script. Returns `undefined` when no path
|
|
320
|
+
* is given. The script only runs in remote sandbox contexts (it's uploaded into
|
|
321
|
+
* the ready sandbox before prompting); for any other context this warns and
|
|
322
|
+
* ignores it rather than failing. For supported contexts the file is read
|
|
323
|
+
* eagerly — consistent with `--prompt-file`/`--schema` — so a missing or
|
|
324
|
+
* unreadable file surfaces as a usage error up front rather than mid-spawn.
|
|
325
|
+
* The returned `name` is the basename used for the uploaded file.
|
|
326
|
+
*/
|
|
327
|
+
function loadPregame(path, context) {
|
|
328
|
+
if (!path) return void 0;
|
|
329
|
+
if (!supportsRemoteSandboxFlags(context)) {
|
|
330
|
+
console.error(`--pregame is only supported with --context e2b or daytona; ignoring it for context '${context}'.`);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
let content;
|
|
334
|
+
try {
|
|
335
|
+
content = readFileSync(path, "utf8");
|
|
336
|
+
} catch (err) {
|
|
337
|
+
throw new Error(`--pregame: failed to read '${path}': ${err.message}`);
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
name: basename(path),
|
|
341
|
+
content
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Resolve the `--sandbox <id>` argument. Returns `undefined` when no id is
|
|
346
|
+
* given. Connecting to a pre-existing sandbox is supported by the remote
|
|
347
|
+
* sandbox providers; for any other context this warns and ignores it rather
|
|
348
|
+
* than failing, mirroring {@link loadPregame}. A blank value (e.g.
|
|
349
|
+
* `--sandbox ''`) is treated as absent.
|
|
350
|
+
*/
|
|
351
|
+
function resolveSandboxId(raw, context) {
|
|
352
|
+
const id = raw?.trim();
|
|
353
|
+
if (!id) return void 0;
|
|
354
|
+
if (!supportsRemoteSandboxFlags(context)) {
|
|
355
|
+
console.error(`--sandbox is only supported with --context e2b or daytona; ignoring it for context '${context}'.`);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
return id;
|
|
359
|
+
}
|
|
360
|
+
const validSandboxOnDestroyValues = [
|
|
361
|
+
"kill",
|
|
362
|
+
"pause",
|
|
363
|
+
"leave"
|
|
364
|
+
];
|
|
365
|
+
/**
|
|
366
|
+
* Resolve the `--sandbox-on-destroy kill|pause|leave` argument. Returns
|
|
367
|
+
* `undefined` when not set (the provider defaults to `'kill'` / delete).
|
|
368
|
+
* Applies to remote sandbox contexts; warns and ignores for any other context,
|
|
369
|
+
* mirroring {@link resolveSandboxId} and {@link loadPregame}.
|
|
370
|
+
*/
|
|
371
|
+
function resolveSandboxOnDestroy(raw, context) {
|
|
372
|
+
const value = raw?.trim();
|
|
373
|
+
if (!value) return void 0;
|
|
374
|
+
if (!supportsRemoteSandboxFlags(context)) {
|
|
375
|
+
console.error(`--sandbox-on-destroy is only supported with --context e2b or daytona; ignoring it for context '${context}'.`);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (!validSandboxOnDestroyValues.includes(value)) throw new StartUsageError(`--sandbox-on-destroy must be kill|pause|leave, got '${value}'.`);
|
|
379
|
+
return value;
|
|
380
|
+
}
|
|
381
|
+
function supportsRemoteSandboxFlags(context) {
|
|
382
|
+
return context === "e2b" || context === "daytona";
|
|
383
|
+
}
|
|
384
|
+
function parseHeaderArgs(raw, flag) {
|
|
385
|
+
if (!raw || raw.length === 0) return {};
|
|
386
|
+
const headers = {};
|
|
387
|
+
for (const item of raw) {
|
|
388
|
+
const colon = item.indexOf(":");
|
|
389
|
+
const equals = item.indexOf("=");
|
|
390
|
+
const separator = colon === -1 ? equals : equals === -1 ? colon : Math.min(colon, equals);
|
|
391
|
+
if (separator <= 0) throw new Error(`${flag} must be formatted as "Name: value" or "Name=value".`);
|
|
392
|
+
const name = item.slice(0, separator).trim();
|
|
393
|
+
const value = item.slice(separator + 1).trim();
|
|
394
|
+
if (!name || !value) throw new Error(`${flag} must include both a header name and value.`);
|
|
395
|
+
headers[name] = value;
|
|
396
|
+
}
|
|
397
|
+
return headers;
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/cli-shared.ts
|
|
401
|
+
/**
|
|
402
|
+
* Build the execution context for a CLI run: `process` (default), `docker`,
|
|
403
|
+
* `e2b`, or `daytona`.
|
|
404
|
+
*
|
|
405
|
+
* The `e2b` backend reads its `E2B_API_KEY` / `E2B_DOMAIN` / `E2B_TEMPLATE`
|
|
406
|
+
* from the environment (point `E2B_DOMAIN` at a self-hosted cluster, and set
|
|
407
|
+
* `E2B_TEMPLATE` to a template that exists there — the default `base` does
|
|
408
|
+
* not); `--cwd` becomes the sandbox working directory.
|
|
409
|
+
*
|
|
410
|
+
* The Daytona backend reads `DAYTONA_API_KEY` / `DAYTONA_API_URL` /
|
|
411
|
+
* `DAYTONA_TARGET` from the environment. `--cwd` becomes the sandbox working
|
|
412
|
+
* directory.
|
|
413
|
+
*
|
|
414
|
+
* `env` (from `--env` / `--pass-env`) is forwarded to whichever context is
|
|
415
|
+
* selected: baked into remote sandboxes at create time, set on the Docker
|
|
416
|
+
* container, or merged into the in-process command environment.
|
|
417
|
+
*
|
|
418
|
+
* `pregame` (from `--pregame`) is supported by the remote sandbox providers:
|
|
419
|
+
* a setup script uploaded and run in the sandbox once it's ready, before
|
|
420
|
+
* prompting begins.
|
|
421
|
+
*
|
|
422
|
+
* `sandboxId` (from `--sandbox`) attaches to a pre-existing remote sandbox
|
|
423
|
+
* instead of creating a fresh one.
|
|
424
|
+
*/
|
|
425
|
+
function createCliExecution(type, opts) {
|
|
426
|
+
switch (type) {
|
|
427
|
+
case "docker": return createDockerContext({
|
|
428
|
+
image: opts.image,
|
|
429
|
+
cwd: opts.cwd,
|
|
430
|
+
env: opts.env
|
|
431
|
+
});
|
|
432
|
+
case "e2b": return createSandboxContext(createE2BProvider({
|
|
433
|
+
cwd: opts.cwd,
|
|
434
|
+
env: opts.env,
|
|
435
|
+
...opts.pregame ? { pregame: opts.pregame } : {},
|
|
436
|
+
...opts.sandboxId ? { sandboxId: opts.sandboxId } : {},
|
|
437
|
+
...opts.sandboxOnDestroy ? { onDestroy: opts.sandboxOnDestroy } : {}
|
|
438
|
+
}));
|
|
439
|
+
case "daytona": return createSandboxContext(createDaytonaProvider({
|
|
440
|
+
cwd: opts.cwd,
|
|
441
|
+
env: opts.env,
|
|
442
|
+
...opts.pregame ? { pregame: opts.pregame } : {},
|
|
443
|
+
...opts.sandboxId ? { sandboxId: opts.sandboxId } : {},
|
|
444
|
+
...opts.sandboxOnDestroy ? { onDestroy: opts.sandboxOnDestroy } : {}
|
|
445
|
+
}));
|
|
446
|
+
default: return createProcessContext({
|
|
447
|
+
cwd: opts.cwd,
|
|
448
|
+
env: opts.env
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
//#endregion
|
|
453
|
+
//#region src/start/options.ts
|
|
454
|
+
const startProviders = {
|
|
455
|
+
anthropic,
|
|
456
|
+
openai,
|
|
457
|
+
openrouter,
|
|
458
|
+
cerebras,
|
|
459
|
+
xai,
|
|
460
|
+
arcee,
|
|
461
|
+
baseten
|
|
462
|
+
};
|
|
463
|
+
const startPresets = { basic: basic_default };
|
|
464
|
+
function resolvePreset(name) {
|
|
465
|
+
const preset = startPresets[name];
|
|
466
|
+
if (!preset) throw new Error(`Unknown preset: ${name}. Available: ${Object.keys(startPresets).join(", ")}`);
|
|
467
|
+
return preset;
|
|
468
|
+
}
|
|
469
|
+
function createStartProvider(input) {
|
|
470
|
+
const options = typeof input === "string" ? { provider: input } : input;
|
|
471
|
+
const name = options.provider;
|
|
472
|
+
if (name === "local" || name === "openai-compat") return createLocalCompatProvider(options, name);
|
|
473
|
+
const providerFactory = startProviders[name];
|
|
474
|
+
if (!providerFactory) throw new Error(`Unknown provider: ${name}. Available: ${Object.keys(startProviders).join(", ")}, local, openai-compat`);
|
|
475
|
+
warnIgnoredLocalProviderOptions(options, name);
|
|
476
|
+
return providerFactory();
|
|
477
|
+
}
|
|
478
|
+
function createLocalCompatProvider(options, name) {
|
|
479
|
+
const baseURL = options.baseURL ?? process.env.LOCAL_LLM_BASE_URL;
|
|
480
|
+
if (!baseURL) throw new Error("No base URL for the local provider. Pass --base-url or set LOCAL_LLM_BASE_URL (e.g. http://localhost:11434/v1 for Ollama, http://localhost:8000/v1 for vLLM).");
|
|
481
|
+
const apiKey = (options.apiKeyEnv ? process.env[options.apiKeyEnv] : void 0) ?? process.env.LOCAL_LLM_API_KEY ?? "no-key";
|
|
482
|
+
const capabilities = {
|
|
483
|
+
vision: options.vision ?? false,
|
|
484
|
+
imageInToolResult: options.imageInToolResult ?? false
|
|
485
|
+
};
|
|
486
|
+
if (capabilities.imageInToolResult) capabilities.vision = true;
|
|
487
|
+
const extraBodyParams = { stream_options: { include_usage: true } };
|
|
488
|
+
if (options.temperature !== void 0) extraBodyParams.temperature = options.temperature;
|
|
489
|
+
if (options.seed !== void 0) extraBodyParams.seed = options.seed;
|
|
490
|
+
return openaiCompat({
|
|
491
|
+
name,
|
|
492
|
+
baseURL,
|
|
493
|
+
apiKey,
|
|
494
|
+
extraHeaders: resolveLocalExtraHeaders(options),
|
|
495
|
+
capabilities,
|
|
496
|
+
...options.model ? { defaultModel: options.model } : process.env.LOCAL_LLM_DEFAULT_MODEL ? { defaultModel: process.env.LOCAL_LLM_DEFAULT_MODEL } : {},
|
|
497
|
+
extraBodyParams
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
function warnIgnoredLocalProviderOptions(options, provider) {
|
|
501
|
+
const ignored = [
|
|
502
|
+
options.baseURL !== void 0 ? "--base-url" : null,
|
|
503
|
+
options.apiKeyEnv !== void 0 ? "--api-key-env" : null,
|
|
504
|
+
options.headersEnv !== void 0 ? "--headers-env" : null,
|
|
505
|
+
options.extraHeaders !== void 0 ? "--header" : null,
|
|
506
|
+
options.vision !== void 0 ? "--vision" : null,
|
|
507
|
+
options.imageInToolResult !== void 0 ? "--image-in-tool-result" : null,
|
|
508
|
+
options.temperature !== void 0 ? "--temperature" : null,
|
|
509
|
+
options.seed !== void 0 ? "--seed" : null
|
|
510
|
+
].filter((flag) => flag !== null);
|
|
511
|
+
if (ignored.length > 0) console.error(`[start] ${ignored.join(", ")} only apply to --provider local|openai-compat; ignored for '${provider}'.`);
|
|
512
|
+
}
|
|
513
|
+
function resolveLocalExtraHeaders(options) {
|
|
514
|
+
return {
|
|
515
|
+
...process.env.LOCAL_LLM_AUTHORIZATION ? { Authorization: process.env.LOCAL_LLM_AUTHORIZATION } : {},
|
|
516
|
+
...readHeadersEnv(options.headersEnv ?? "LOCAL_LLM_HEADERS"),
|
|
517
|
+
...options.extraHeaders ?? {}
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function readHeadersEnv(envName) {
|
|
521
|
+
const raw = process.env[envName];
|
|
522
|
+
if (!raw) return {};
|
|
523
|
+
let parsed;
|
|
524
|
+
try {
|
|
525
|
+
parsed = JSON.parse(raw);
|
|
526
|
+
} catch {
|
|
527
|
+
throw new Error(`${envName} must contain a JSON object of string headers.`);
|
|
528
|
+
}
|
|
529
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`${envName} must contain a JSON object of string headers.`);
|
|
530
|
+
const headers = {};
|
|
531
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
532
|
+
if (typeof value !== "string") throw new Error(`${envName} header "${name}" must be a string.`);
|
|
533
|
+
headers[name] = value;
|
|
534
|
+
}
|
|
535
|
+
return headers;
|
|
536
|
+
}
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region src/acp-cli.ts
|
|
539
|
+
const acpArgOptions = {
|
|
540
|
+
"models": { type: "string" },
|
|
541
|
+
"model-option": {
|
|
542
|
+
type: "string",
|
|
543
|
+
multiple: true
|
|
544
|
+
},
|
|
545
|
+
"no-credential-store": {
|
|
546
|
+
type: "boolean",
|
|
547
|
+
default: false
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
async function main() {
|
|
551
|
+
const argv = Bun.argv.slice(2);
|
|
552
|
+
if (isHelp(argv)) {
|
|
553
|
+
process.stdout.write(`${usage()}\n`);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const values = parseStartArgValues(argv, {
|
|
557
|
+
...startLocalArgOptions,
|
|
558
|
+
...acpArgOptions
|
|
559
|
+
});
|
|
560
|
+
const options = buildStartLocalOptions(values, {
|
|
561
|
+
requirePrompt: false,
|
|
562
|
+
usage
|
|
563
|
+
});
|
|
564
|
+
if (values["no-credential-store"] !== true) {
|
|
565
|
+
const bridged = bridgeStoredCredentials();
|
|
566
|
+
if (bridged) process.stderr.write(`[zidane-acp] using stored credentials from ${bridged}\n`);
|
|
567
|
+
}
|
|
568
|
+
const registry = buildModelRegistry(options, values);
|
|
569
|
+
const store = options.sessionDb ? (await import("./session/sqlite.js")).createSqliteStore({ path: options.sessionDb }) : void 0;
|
|
570
|
+
const handle = runAcpStdioServer({
|
|
571
|
+
...registry ? {
|
|
572
|
+
models: registry.models,
|
|
573
|
+
...registry.defaultModelId ? { defaultModelId: registry.defaultModelId } : {}
|
|
574
|
+
} : { provider: buildBaseProvider(options) },
|
|
575
|
+
model: options.model,
|
|
576
|
+
preset: resolvePreset(options.preset),
|
|
577
|
+
system: options.system,
|
|
578
|
+
store,
|
|
579
|
+
framing: "newline",
|
|
580
|
+
agentOptions: { ...options.mcp ? { mcpServers: options.mcp } : {} },
|
|
581
|
+
execution: (request) => createCliExecution(options.context, {
|
|
582
|
+
image: options.image,
|
|
583
|
+
cwd: request.cwd || options.cwd,
|
|
584
|
+
env: options.env,
|
|
585
|
+
pregame: options.pregame,
|
|
586
|
+
sandboxId: options.sandboxId,
|
|
587
|
+
sandboxOnDestroy: options.sandboxOnDestroy
|
|
588
|
+
}),
|
|
589
|
+
agentInfo: {
|
|
590
|
+
name: "zidane",
|
|
591
|
+
title: "Zidane",
|
|
592
|
+
version: process.env.npm_package_version ?? "1"
|
|
593
|
+
},
|
|
594
|
+
permission: { enabled: true }
|
|
595
|
+
});
|
|
596
|
+
const shutdown = async () => {
|
|
597
|
+
await handle.close();
|
|
598
|
+
store?.close?.();
|
|
599
|
+
};
|
|
600
|
+
process.once("SIGINT", () => {
|
|
601
|
+
shutdown().finally(() => process.exit(130));
|
|
602
|
+
});
|
|
603
|
+
process.once("SIGTERM", () => {
|
|
604
|
+
shutdown().finally(() => process.exit(143));
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
function usage() {
|
|
608
|
+
return [
|
|
609
|
+
"Usage: zidane-acp [provider/context options]",
|
|
610
|
+
"",
|
|
611
|
+
"Runs Zidane as an ACP v1 agent over stdio. Prompts are received through ACP session/prompt.",
|
|
612
|
+
"",
|
|
613
|
+
"Common options:",
|
|
614
|
+
" --provider anthropic|openai|openrouter|cerebras|xai|arcee|baseten|local|openai-compat",
|
|
615
|
+
" --model <id>",
|
|
616
|
+
" --models <id,id,...> advertise a model selector (all on --provider)",
|
|
617
|
+
" --model-option provider:model[:label] add a cross-provider selectable model (repeatable)",
|
|
618
|
+
" --context process|docker|e2b|daytona",
|
|
619
|
+
" --cwd <path>",
|
|
620
|
+
" --session-db <path>",
|
|
621
|
+
" --mcp <json>",
|
|
622
|
+
" --no-credential-store ignore ~/.zidane credentials; use only env",
|
|
623
|
+
"",
|
|
624
|
+
"With --models / --model-option, ACP clients (e.g. Zed) can switch models at",
|
|
625
|
+
"runtime; each model resolves its own provider credentials when first selected.",
|
|
626
|
+
"",
|
|
627
|
+
"Credentials: by default zidane-acp reads your existing zidane login from",
|
|
628
|
+
"~/.zidane/credentials.json (run `zidane auth` once), so no API keys are needed",
|
|
629
|
+
"in the editor config. Explicit env vars always win."
|
|
630
|
+
].join("\n");
|
|
631
|
+
}
|
|
632
|
+
function isHelp(argv) {
|
|
633
|
+
return argv.includes("--help") || argv.includes("-h");
|
|
634
|
+
}
|
|
635
|
+
/** Build a `createStartProvider` config from the parsed CLI options. */
|
|
636
|
+
function providerConfig(options, provider, model) {
|
|
637
|
+
return {
|
|
638
|
+
provider,
|
|
639
|
+
model,
|
|
640
|
+
baseURL: options.baseURL,
|
|
641
|
+
apiKeyEnv: options.apiKeyEnv,
|
|
642
|
+
headersEnv: options.headersEnv,
|
|
643
|
+
extraHeaders: options.extraHeaders,
|
|
644
|
+
vision: options.vision,
|
|
645
|
+
imageInToolResult: options.imageInToolResult,
|
|
646
|
+
temperature: options.temperature,
|
|
647
|
+
seed: options.seed
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function buildBaseProvider(options) {
|
|
651
|
+
return createStartProvider(providerConfig(options, options.provider, options.model));
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Assemble the selectable-model registry from `--model-option` (cross-provider,
|
|
655
|
+
* each lazily building its own provider) and `--models` (same `--provider`,
|
|
656
|
+
* sharing one provider instance). Returns `undefined` when neither flag is set
|
|
657
|
+
* so the caller falls back to a single provider.
|
|
658
|
+
*/
|
|
659
|
+
function buildModelRegistry(options, values) {
|
|
660
|
+
const modelsFlag = values.models?.trim();
|
|
661
|
+
const modelOptionFlags = values["model-option"] ?? [];
|
|
662
|
+
if (!modelsFlag && modelOptionFlags.length === 0) return void 0;
|
|
663
|
+
const choices = [];
|
|
664
|
+
for (const raw of modelOptionFlags) choices.push(parseModelOption(raw, options));
|
|
665
|
+
if (modelsFlag) {
|
|
666
|
+
const models = modelsFlag.split(",").map((model) => model.trim()).filter(Boolean);
|
|
667
|
+
if (models.length === 0) throw new StartUsageError("--models must list at least one model id.");
|
|
668
|
+
const baseProvider = buildBaseProvider(options);
|
|
669
|
+
for (const model of models) choices.push({
|
|
670
|
+
id: model,
|
|
671
|
+
name: model,
|
|
672
|
+
model,
|
|
673
|
+
provider: baseProvider
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
const seen = /* @__PURE__ */ new Set();
|
|
677
|
+
for (const choice of choices) {
|
|
678
|
+
if (seen.has(choice.id)) throw new StartUsageError(`Duplicate model id "${choice.id}" across --models / --model-option.`);
|
|
679
|
+
seen.add(choice.id);
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
models: choices,
|
|
683
|
+
defaultModelId: options.model && choices.find((choice) => choice.id === options.model || choice.model === options.model)?.id || choices[0].id
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function parseModelOption(raw, options) {
|
|
687
|
+
const parts = raw.split(":");
|
|
688
|
+
if (parts.length < 2 || !parts[0] || !parts[1]) throw new StartUsageError(`--model-option must be "provider:model[:label]", got '${raw}'.`);
|
|
689
|
+
const providerName = parts[0];
|
|
690
|
+
if (!startProviderNames.includes(providerName)) throw new StartUsageError(`--model-option provider must be one of ${startProviderNames.join("|")}, got '${providerName}'.`);
|
|
691
|
+
const model = parts[1];
|
|
692
|
+
const label = parts.slice(2).join(":").trim() || void 0;
|
|
693
|
+
const isLocal = providerName === "local" || providerName === "openai-compat";
|
|
694
|
+
const provider = () => createStartProvider(isLocal ? providerConfig(options, providerName, model) : {
|
|
695
|
+
provider: providerName,
|
|
696
|
+
model
|
|
697
|
+
});
|
|
698
|
+
return {
|
|
699
|
+
id: `${providerName}:${model}`,
|
|
700
|
+
name: label ?? model,
|
|
701
|
+
model,
|
|
702
|
+
provider
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
main().catch((err) => {
|
|
706
|
+
if (err instanceof StartUsageError) process.stderr.write(`${err.message}\n`);
|
|
707
|
+
else process.stderr.write(`ACP server error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
708
|
+
process.exit(1);
|
|
709
|
+
});
|
|
710
|
+
//#endregion
|
|
711
|
+
export {};
|
|
712
|
+
|
|
713
|
+
//# sourceMappingURL=acp-cli.js.map
|