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.
Files changed (96) hide show
  1. package/README.md +15 -0
  2. package/dist/acp-BqIU2mo-.js +1410 -0
  3. package/dist/acp-BqIU2mo-.js.map +1 -0
  4. package/dist/acp-cli.d.ts +1 -0
  5. package/dist/acp-cli.js +713 -0
  6. package/dist/acp-cli.js.map +1 -0
  7. package/dist/acp.d.ts +655 -0
  8. package/dist/acp.d.ts.map +1 -0
  9. package/dist/acp.js +2 -0
  10. package/dist/{agent-Db4ojCSV.d.ts → agent-D7ZL8B2X.d.ts} +2 -2
  11. package/dist/{agent-Db4ojCSV.d.ts.map → agent-D7ZL8B2X.d.ts.map} +1 -1
  12. package/dist/chat/pure.d.ts +3 -3
  13. package/dist/chat.d.ts +6 -6
  14. package/dist/chat.js +3 -2
  15. package/dist/chat.js.map +1 -1
  16. package/dist/contexts/daytona.d.ts +3 -3
  17. package/dist/contexts/docker.d.ts +1 -1
  18. package/dist/contexts/docker.d.ts.map +1 -1
  19. package/dist/contexts/docker.js +4 -1
  20. package/dist/contexts/docker.js.map +1 -1
  21. package/dist/contexts/e2b.d.ts +2 -2
  22. package/dist/{contexts-VhV4Af8x.js → contexts-DHi8LPCp.js} +25 -9
  23. package/dist/contexts-DHi8LPCp.js.map +1 -0
  24. package/dist/contexts.d.ts +3 -3
  25. package/dist/contexts.js +1 -1
  26. package/dist/eval.d.ts +1 -1
  27. package/dist/eval.js +3 -3
  28. package/dist/glob-DCWXy_tr.js +128 -0
  29. package/dist/glob-DCWXy_tr.js.map +1 -0
  30. package/dist/{headless-tVN-g6IR.js → headless-0O6HMNBQ.js} +6 -6
  31. package/dist/{headless-tVN-g6IR.js.map → headless-0O6HMNBQ.js.map} +1 -1
  32. package/dist/headless.d.ts +1 -1
  33. package/dist/headless.js +1 -1
  34. package/dist/{index-BEblm0Hu.d.ts → index-BsyPeCSL.d.ts} +3 -3
  35. package/dist/{index-BEblm0Hu.d.ts.map → index-BsyPeCSL.d.ts.map} +1 -1
  36. package/dist/{index-CJ-2g7bY.d.ts → index-CDcQW-2S.d.ts} +3 -3
  37. package/dist/index-CDcQW-2S.d.ts.map +1 -0
  38. package/dist/{index-CrMb8jCE.d.ts → index-CF15aqlk.d.ts} +3 -3
  39. package/dist/{index-CrMb8jCE.d.ts.map → index-CF15aqlk.d.ts.map} +1 -1
  40. package/dist/index.d.ts +7 -7
  41. package/dist/index.js +7 -7
  42. package/dist/lazy-DLOurOC_.js +20 -0
  43. package/dist/lazy-DLOurOC_.js.map +1 -0
  44. package/dist/{logger-Dcrj48qY.d.ts → logger-DItaCwPw.d.ts} +2 -2
  45. package/dist/{logger-Dcrj48qY.d.ts.map → logger-DItaCwPw.d.ts.map} +1 -1
  46. package/dist/mcp.d.ts +1 -1
  47. package/dist/{messages-CGazSyTL.js → messages-DEsLGBB9.js} +2 -2
  48. package/dist/{messages-CGazSyTL.js.map → messages-DEsLGBB9.js.map} +1 -1
  49. package/dist/output/stream-json.d.ts +2 -2
  50. package/dist/output/stream-json.js +1 -1
  51. package/dist/output/terminal.d.ts +2 -2
  52. package/dist/{presets-kPEMOCmE.js → presets-HDIxliiq.js} +2 -2
  53. package/dist/{presets-kPEMOCmE.js.map → presets-HDIxliiq.js.map} +1 -1
  54. package/dist/presets.d.ts +2 -2
  55. package/dist/presets.js +1 -1
  56. package/dist/{providers-Bo2biCyT.js → providers-Cz-RNYZO.js} +7 -13
  57. package/dist/providers-Cz-RNYZO.js.map +1 -0
  58. package/dist/providers.d.ts +1 -1
  59. package/dist/providers.js +2 -2
  60. package/dist/restate.d.ts +2 -2
  61. package/dist/session/sqlite.d.ts +1 -1
  62. package/dist/{session-B69BQSn1.js → session-BDWZZaYa.js} +2 -2
  63. package/dist/{session-B69BQSn1.js.map → session-BDWZZaYa.js.map} +1 -1
  64. package/dist/session.d.ts +1 -1
  65. package/dist/session.js +2 -2
  66. package/dist/skills.d.ts +2 -2
  67. package/dist/{tool-formatters-CkqBgPH4.d.ts → tool-formatters-CNSMadtp.d.ts} +2 -2
  68. package/dist/{tool-formatters-CkqBgPH4.d.ts.map → tool-formatters-CNSMadtp.d.ts.map} +1 -1
  69. package/dist/tools/fetch-url.d.ts +1 -1
  70. package/dist/tools/web-search.d.ts +1 -1
  71. package/dist/{tools-5Bnlq68O.js → tools-DhzKzB1y.js} +39 -56
  72. package/dist/tools-DhzKzB1y.js.map +1 -0
  73. package/dist/tools.d.ts +2 -2
  74. package/dist/tools.js +1 -1
  75. package/dist/{transcript-anchors-D4PwUMyO.js → transcript-anchors-Cq-8gx8u.js} +9 -1417
  76. package/dist/transcript-anchors-Cq-8gx8u.js.map +1 -0
  77. package/dist/{transcript-anchors-BnLZmASt.d.ts → transcript-anchors-EG-SmZRu.d.ts} +4 -4
  78. package/dist/{transcript-anchors-BnLZmASt.d.ts.map → transcript-anchors-EG-SmZRu.d.ts.map} +1 -1
  79. package/dist/tui.d.ts +3 -3
  80. package/dist/tui.js +7 -6
  81. package/dist/tui.js.map +1 -1
  82. package/dist/{turn-operations-B6FaQAZN.d.ts → turn-operations-DwtWRYr1.d.ts} +3 -3
  83. package/dist/{turn-operations-B6FaQAZN.d.ts.map → turn-operations-DwtWRYr1.d.ts.map} +1 -1
  84. package/dist/{types-B39tBba1.d.ts → types-Bs2oY7Ux.d.ts} +27 -4
  85. package/dist/types-Bs2oY7Ux.d.ts.map +1 -0
  86. package/dist/types.d.ts +4 -4
  87. package/dist/xdg-zlSeVBhQ.js +1417 -0
  88. package/dist/xdg-zlSeVBhQ.js.map +1 -0
  89. package/docs/ACP.md +221 -0
  90. package/package.json +11 -1
  91. package/dist/contexts-VhV4Af8x.js.map +0 -1
  92. package/dist/index-CJ-2g7bY.d.ts.map +0 -1
  93. package/dist/providers-Bo2biCyT.js.map +0 -1
  94. package/dist/tools-5Bnlq68O.js.map +0 -1
  95. package/dist/transcript-anchors-D4PwUMyO.js.map +0 -1
  96. package/dist/types-B39tBba1.d.ts.map +0 -1
@@ -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