zidane 5.13.3 → 5.13.5
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 +3 -3
- package/dist/{acp-BqIU2mo-.js → acp-CJ1yHdpK.js} +467 -16
- package/dist/acp-CJ1yHdpK.js.map +1 -0
- package/dist/acp-cli.js +86 -346
- package/dist/acp-cli.js.map +1 -1
- package/dist/acp.d.ts +10 -1
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +1 -1
- package/dist/{xdg-zlSeVBhQ.js → auth-QA4WuQK5.js} +52 -2
- package/dist/auth-QA4WuQK5.js.map +1 -0
- package/dist/chat.js +2 -2
- package/dist/{transcript-anchors-Cq-8gx8u.js → transcript-anchors-C_ecFvJ2.js} +3 -53
- package/dist/transcript-anchors-C_ecFvJ2.js.map +1 -0
- package/dist/tui.js +2 -2
- package/docs/ACP.md +47 -25
- package/package.json +1 -1
- package/dist/acp-BqIU2mo-.js.map +0 -1
- package/dist/transcript-anchors-Cq-8gx8u.js.map +0 -1
- package/dist/xdg-zlSeVBhQ.js.map +0 -1
package/dist/acp-cli.js
CHANGED
|
@@ -4,15 +4,14 @@ import { n as createSandboxContext, r as createProcessContext } from "./contexts
|
|
|
4
4
|
import { i as basic_default } from "./presets-HDIxliiq.js";
|
|
5
5
|
import "./session-BDWZZaYa.js";
|
|
6
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-
|
|
8
|
-
import {
|
|
7
|
+
import { d as buildStartLocalOptions, f as parseStartArgValues, m as startProviderNames, p as startLocalArgOptions, t as runAcpStdioServer, u as StartUsageError } from "./acp-CJ1yHdpK.js";
|
|
8
|
+
import { a as readCredentials, b as modelsForDescriptor, i as credentialsPath, n as resolveStorageDirs, p as credKeyOf, t as detectAuth, u as BUILTIN_PROVIDERS } from "./auth-QA4WuQK5.js";
|
|
9
9
|
import { createDaytonaProvider } from "./contexts/daytona.js";
|
|
10
10
|
import { createDockerContext } from "./contexts/docker.js";
|
|
11
11
|
import { createE2BProvider } from "./contexts/e2b.js";
|
|
12
12
|
import "./session/sqlite.js";
|
|
13
|
-
import {
|
|
14
|
-
import { existsSync
|
|
15
|
-
import { parseArgs } from "node:util";
|
|
13
|
+
import { resolve } from "node:path";
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
16
15
|
//#region src/acp/credential-bridge.ts
|
|
17
16
|
/**
|
|
18
17
|
* Bridge the user's existing zidane credential store into the process
|
|
@@ -61,342 +60,6 @@ function bridgeStoredCredentials(options = {}) {
|
|
|
61
60
|
return path;
|
|
62
61
|
}
|
|
63
62
|
//#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
63
|
//#region src/cli-shared.ts
|
|
401
64
|
/**
|
|
402
65
|
* Build the execution context for a CLI run: `process` (default), `docker`,
|
|
@@ -565,8 +228,9 @@ async function main() {
|
|
|
565
228
|
const bridged = bridgeStoredCredentials();
|
|
566
229
|
if (bridged) process.stderr.write(`[zidane-acp] using stored credentials from ${bridged}\n`);
|
|
567
230
|
}
|
|
568
|
-
const registry = buildModelRegistry(options, values);
|
|
569
|
-
const
|
|
231
|
+
const registry = buildModelRegistry(options, values) ?? buildAutoModelRegistry(options, argv);
|
|
232
|
+
const sessionDb = options.sessionDb ?? defaultAcpSessionDbPath();
|
|
233
|
+
const store = (await import("./session/sqlite.js")).createTuiStore(sessionDb);
|
|
570
234
|
const handle = runAcpStdioServer({
|
|
571
235
|
...registry ? {
|
|
572
236
|
models: registry.models,
|
|
@@ -617,12 +281,13 @@ function usage() {
|
|
|
617
281
|
" --model-option provider:model[:label] add a cross-provider selectable model (repeatable)",
|
|
618
282
|
" --context process|docker|e2b|daytona",
|
|
619
283
|
" --cwd <path>",
|
|
620
|
-
|
|
284
|
+
` --session-db <path> default: ${defaultAcpSessionDbPath()}`,
|
|
621
285
|
" --mcp <json>",
|
|
622
286
|
" --no-credential-store ignore ~/.zidane credentials; use only env",
|
|
623
287
|
"",
|
|
624
|
-
"
|
|
625
|
-
"
|
|
288
|
+
"Without --provider / --model / --models / --model-option, zidane-acp",
|
|
289
|
+
"auto-advertises models for every configured provider. Explicit flags keep",
|
|
290
|
+
"the old deterministic single-provider or curated-picker behavior.",
|
|
626
291
|
"",
|
|
627
292
|
"Credentials: by default zidane-acp reads Zidane's app credential store",
|
|
628
293
|
"(~/.zidane/credentials.json), so no API keys are needed in the editor config.",
|
|
@@ -670,6 +335,7 @@ function buildModelRegistry(options, values) {
|
|
|
670
335
|
id: model,
|
|
671
336
|
name: model,
|
|
672
337
|
model,
|
|
338
|
+
...modelChoiceMetadata(options.provider, model),
|
|
673
339
|
provider: baseProvider
|
|
674
340
|
});
|
|
675
341
|
}
|
|
@@ -683,6 +349,66 @@ function buildModelRegistry(options, values) {
|
|
|
683
349
|
defaultModelId: options.model && choices.find((choice) => choice.id === options.model || choice.model === options.model)?.id || choices[0].id
|
|
684
350
|
};
|
|
685
351
|
}
|
|
352
|
+
function buildAutoModelRegistry(options, argv) {
|
|
353
|
+
if (hasExplicitProviderTarget(argv)) return void 0;
|
|
354
|
+
const dataDir = resolveStorageDirs({ env: process.env }).configDir;
|
|
355
|
+
const available = detectAuth(dataDir, BUILTIN_PROVIDERS, process.env).filter((provider) => provider.available);
|
|
356
|
+
if (available.length === 0) return void 0;
|
|
357
|
+
const choices = [];
|
|
358
|
+
const providers = /* @__PURE__ */ new Map();
|
|
359
|
+
const providerFor = (key) => {
|
|
360
|
+
let provider = providers.get(key);
|
|
361
|
+
if (!provider) {
|
|
362
|
+
const descriptor = BUILTIN_PROVIDERS[key];
|
|
363
|
+
if (!descriptor) throw new StartUsageError(`Unknown configured provider: ${key}`);
|
|
364
|
+
provider = descriptor.factory();
|
|
365
|
+
providers.set(key, provider);
|
|
366
|
+
}
|
|
367
|
+
return provider;
|
|
368
|
+
};
|
|
369
|
+
for (const auth of available) {
|
|
370
|
+
const descriptor = BUILTIN_PROVIDERS[auth.key];
|
|
371
|
+
if (!descriptor) continue;
|
|
372
|
+
const models = promoteDefaultModel(modelsForDescriptor(descriptor), descriptor.defaultModel);
|
|
373
|
+
for (const model of models) choices.push({
|
|
374
|
+
id: `${descriptor.key}:${model.id}`,
|
|
375
|
+
name: `${model.name ?? model.id} (${descriptor.label})`,
|
|
376
|
+
model: model.id,
|
|
377
|
+
...model.reasoning === true ? { reasoning: true } : {},
|
|
378
|
+
...model.options?.length ? { options: model.options.map((option) => ({
|
|
379
|
+
id: option.id,
|
|
380
|
+
label: option.label,
|
|
381
|
+
...option.description ? { description: option.description } : {}
|
|
382
|
+
})) } : {},
|
|
383
|
+
provider: () => providerFor(descriptor.key)
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
if (choices.length === 0) return void 0;
|
|
387
|
+
return {
|
|
388
|
+
models: choices,
|
|
389
|
+
defaultModelId: choices.find((choice) => choice.id === `${options.provider}:${options.model}` || choice.model === options.model)?.id ?? choices[0].id
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function promoteDefaultModel(models, defaultModel) {
|
|
393
|
+
if (!defaultModel) return models;
|
|
394
|
+
const index = models.findIndex((model) => model.id === defaultModel);
|
|
395
|
+
if (index <= 0) return models;
|
|
396
|
+
const next = models.slice();
|
|
397
|
+
const [selected] = next.splice(index, 1);
|
|
398
|
+
next.unshift(selected);
|
|
399
|
+
return next;
|
|
400
|
+
}
|
|
401
|
+
function hasExplicitProviderTarget(argv) {
|
|
402
|
+
return hasArg(argv, "provider") || hasArg(argv, "model", "m") || hasArg(argv, "models") || hasArg(argv, "model-option");
|
|
403
|
+
}
|
|
404
|
+
function hasArg(argv, long, short) {
|
|
405
|
+
const longFlag = `--${long}`;
|
|
406
|
+
const shortFlag = short ? `-${short}` : void 0;
|
|
407
|
+
return argv.some((arg) => arg === longFlag || arg.startsWith(`${longFlag}=`) || shortFlag !== void 0 && (arg === shortFlag || arg.startsWith(`${shortFlag}=`)));
|
|
408
|
+
}
|
|
409
|
+
function defaultAcpSessionDbPath() {
|
|
410
|
+
return resolve(resolveStorageDirs({ env: process.env }).dataDir, "acp-sessions.db");
|
|
411
|
+
}
|
|
686
412
|
function parseModelOption(raw, options) {
|
|
687
413
|
const parts = raw.split(":");
|
|
688
414
|
if (parts.length < 2 || !parts[0] || !parts[1]) throw new StartUsageError(`--model-option must be "provider:model[:label]", got '${raw}'.`);
|
|
@@ -699,9 +425,23 @@ function parseModelOption(raw, options) {
|
|
|
699
425
|
id: `${providerName}:${model}`,
|
|
700
426
|
name: label ?? model,
|
|
701
427
|
model,
|
|
428
|
+
...modelChoiceMetadata(providerName, model),
|
|
702
429
|
provider
|
|
703
430
|
};
|
|
704
431
|
}
|
|
432
|
+
function modelChoiceMetadata(providerName, modelId) {
|
|
433
|
+
const descriptor = BUILTIN_PROVIDERS[providerName];
|
|
434
|
+
if (!descriptor) return {};
|
|
435
|
+
const model = modelsForDescriptor(descriptor).find((candidate) => candidate.id === modelId);
|
|
436
|
+
return {
|
|
437
|
+
...model?.reasoning === true ? { reasoning: true } : {},
|
|
438
|
+
...model?.options?.length ? { options: model.options.map((option) => ({
|
|
439
|
+
id: option.id,
|
|
440
|
+
label: option.label,
|
|
441
|
+
...option.description ? { description: option.description } : {}
|
|
442
|
+
})) } : {}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
705
445
|
main().catch((err) => {
|
|
706
446
|
if (err instanceof StartUsageError) process.stderr.write(`${err.message}\n`);
|
|
707
447
|
else process.stderr.write(`ACP server error: ${err instanceof Error ? err.message : String(err)}\n`);
|