@datalabrotterdam/nova-ai-cli 1.1.0 → 1.2.0-alpha.10

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 (52) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +211 -100
  3. package/dist/browser-setup-7SWHUQEL.js +57 -0
  4. package/dist/chunk-FMHWW3E6.js +11 -0
  5. package/dist/chunk-KDAUUQBE.js +643 -0
  6. package/dist/chunk-L77KQRGZ.js +170 -0
  7. package/dist/headless-BQXK7TNC.js +550 -0
  8. package/dist/index.js +193 -17
  9. package/dist/tui-TJFV6NC6.js +5185 -0
  10. package/dist/update-check-G3PYCRWM.js +23 -0
  11. package/package.json +35 -33
  12. package/dist/acp/agent.d.ts +0 -13
  13. package/dist/acp/agent.js +0 -242
  14. package/dist/acp/auth-server.d.ts +0 -10
  15. package/dist/acp/auth-server.js +0 -67
  16. package/dist/acp/credentials.d.ts +0 -11
  17. package/dist/acp/credentials.js +0 -30
  18. package/dist/acp/index.d.ts +0 -2
  19. package/dist/acp/index.js +0 -21
  20. package/dist/acp/mcp.d.ts +0 -11
  21. package/dist/acp/mcp.js +0 -90
  22. package/dist/acp/sessions.d.ts +0 -20
  23. package/dist/acp/sessions.js +0 -91
  24. package/dist/acp/tools/index.d.ts +0 -6
  25. package/dist/acp/tools/index.js +0 -5
  26. package/dist/acp/tools/marker.d.ts +0 -17
  27. package/dist/acp/tools/marker.js +0 -43
  28. package/dist/acp/tools/permission.d.ts +0 -2
  29. package/dist/acp/tools/permission.js +0 -20
  30. package/dist/acp/tools/read-file.d.ts +0 -2
  31. package/dist/acp/tools/read-file.js +0 -20
  32. package/dist/acp/tools/registry.d.ts +0 -4
  33. package/dist/acp/tools/registry.js +0 -10
  34. package/dist/acp/tools/reporting.d.ts +0 -3
  35. package/dist/acp/tools/reporting.js +0 -26
  36. package/dist/acp/tools/run-command.d.ts +0 -2
  37. package/dist/acp/tools/run-command.js +0 -29
  38. package/dist/acp/tools/system-prompt.d.ts +0 -2
  39. package/dist/acp/tools/system-prompt.js +0 -23
  40. package/dist/acp/tools/types.d.ts +0 -18
  41. package/dist/acp/tools/types.js +0 -1
  42. package/dist/acp/tools/write-file.d.ts +0 -2
  43. package/dist/acp/tools/write-file.js +0 -20
  44. package/dist/index.d.ts +0 -2
  45. package/dist/tui/index.d.ts +0 -7
  46. package/dist/tui/index.js +0 -11
  47. package/dist/webui/index.d.ts +0 -6
  48. package/dist/webui/index.js +0 -9
  49. package/src/acp/web/dist/assets/index-CSccI7lJ.css +0 -1
  50. package/src/acp/web/dist/assets/index-D2Z0sfLR.js +0 -1
  51. package/src/acp/web/dist/favicon.svg +0 -46
  52. package/src/acp/web/dist/index.html +0 -32
@@ -0,0 +1,550 @@
1
+ import {
2
+ TuiAcpClient,
3
+ choosePermissionOption,
4
+ createStore
5
+ } from "./chunk-KDAUUQBE.js";
6
+
7
+ // src/headless/index.ts
8
+ import { resolve } from "path";
9
+ import { NOVA_NOTIFICATIONS } from "@datalabrotterdam/nova-ai-agent/acp/extensions.js";
10
+ import * as acp from "@agentclientprotocol/sdk";
11
+ import { NovaAgent } from "@datalabrotterdam/nova-ai-agent/acp/agent.js";
12
+ import { clientFromContext } from "@datalabrotterdam/nova-ai-agent/client/context-client.js";
13
+ import { startInProcessAgent } from "@datalabrotterdam/nova-ai-agent/client/in-process.js";
14
+ import { listStoredSessions, loadStoredSession } from "@datalabrotterdam/nova-ai-agent/core/sessions.js";
15
+
16
+ // src/headless/client.ts
17
+ var HeadlessAcpClient = class {
18
+ constructor(cwd, permissionMode, emit) {
19
+ this.permissionMode = permissionMode;
20
+ this.emit = emit;
21
+ const store = createStore({
22
+ messages: [],
23
+ plan: [],
24
+ pendingPermission: null,
25
+ pendingQuestion: null,
26
+ inputHistory: [],
27
+ busy: false,
28
+ mode: "chat",
29
+ permissionMode: "ask",
30
+ interactionMode: "agent",
31
+ sessionId: "headless",
32
+ cwd,
33
+ statusLine: null,
34
+ queuedCount: 0,
35
+ contextUsage: null,
36
+ updateAvailable: null
37
+ });
38
+ this.base = new TuiAcpClient(store, cwd);
39
+ this.baseContext = this.base.context();
40
+ }
41
+ permissionMode;
42
+ emit;
43
+ capabilities = {
44
+ fs: { readTextFile: true, writeTextFile: true },
45
+ terminal: true
46
+ };
47
+ base;
48
+ baseContext;
49
+ toolNames = /* @__PURE__ */ new Map();
50
+ context() {
51
+ return {
52
+ request: (method, params, options) => this.handleRequest(method, params, options),
53
+ notify: (method, params) => this.handleNotification(method, params)
54
+ };
55
+ }
56
+ async close() {
57
+ await this.base.releaseAllTerminals();
58
+ }
59
+ async handleRequest(method, params, options) {
60
+ options?.cancellationSignal?.throwIfAborted();
61
+ if (method === "session/request_permission") {
62
+ return this.requestPermission(params);
63
+ }
64
+ if (method === "elicitation/create") {
65
+ return { action: "decline" };
66
+ }
67
+ return this.baseContext.request(method, params, options);
68
+ }
69
+ async handleNotification(method, params) {
70
+ await this.emit({ type: "notification", method, params });
71
+ if (method === "session/update") await this.reportAgentDecision(params);
72
+ await this.baseContext.notify(method, params);
73
+ }
74
+ /**
75
+ * The agent's policy already applied rules and the mode (set from
76
+ * --permission-mode); what still reaches us needs a person, and there is
77
+ * none: only bypass-all approves it.
78
+ */
79
+ async requestPermission(params) {
80
+ const toolName = this.toolName(params.toolCall);
81
+ const allow = this.permissionMode === "bypass-all";
82
+ const optionId = choosePermissionOption(params.options, allow, "once") ?? (allow ? "allow_once" : "reject_once");
83
+ await this.emit({
84
+ type: "permission",
85
+ toolCallId: params.toolCall.toolCallId,
86
+ toolName,
87
+ decision: allow ? "allow" : "reject",
88
+ source: this.permissionMode
89
+ });
90
+ return { outcome: { outcome: "selected", optionId } };
91
+ }
92
+ /** Reports decisions the agent's policy made without asking. */
93
+ async reportAgentDecision(params) {
94
+ const update = params?.update;
95
+ if (!update) return;
96
+ const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : null;
97
+ if (!toolCallId) return;
98
+ if (update.sessionUpdate === "tool_call") {
99
+ this.toolNames.set(toolCallId, this.toolName(update));
100
+ return;
101
+ }
102
+ const permission = update._meta?.["nova-ai-cli/permission"];
103
+ if (update.sessionUpdate !== "tool_call_update" || !permission) return;
104
+ await this.emit({
105
+ type: "permission",
106
+ toolCallId,
107
+ toolName: this.toolNames.get(toolCallId) ?? "tool",
108
+ decision: permission.decision === "allow" ? "allow" : "reject",
109
+ source: permission.reason === "mode" ? this.permissionMode : "rule"
110
+ });
111
+ }
112
+ toolName(call) {
113
+ const fromMeta = call._meta?.["nova-ai-cli/tool"];
114
+ return typeof fromMeta === "string" ? fromMeta : call.title ?? "tool";
115
+ }
116
+ };
117
+
118
+ // src/headless/index.ts
119
+ var AGENT_PERMISSION_MODE = {
120
+ "read-only": "default",
121
+ "accept-edits": "acceptEdits",
122
+ "bypass-all": "bypassPermissions"
123
+ };
124
+ async function runHeadless(args, dependencies = {}) {
125
+ const stdout = dependencies.stdout ?? process.stdout;
126
+ const stderr = dependencies.stderr ?? process.stderr;
127
+ let options;
128
+ try {
129
+ options = parseHeadlessArgs(args, process.cwd());
130
+ } catch (error) {
131
+ const message = errorMessage(error);
132
+ if (args.includes("--json") || args.some((arg) => /^--output-format(=|$)/.test(arg) && !arg.endsWith("=text"))) {
133
+ return writeFailure(true, stdout, stderr, message);
134
+ }
135
+ stderr.write(`${message}
136
+
137
+ ${HEADLESS_HELP}
138
+ `);
139
+ return 1;
140
+ }
141
+ if (options.help) {
142
+ stdout.write(`${HEADLESS_HELP}
143
+ `);
144
+ return 0;
145
+ }
146
+ let prompt = options.prompt;
147
+ if (!prompt) {
148
+ const canReadStdin = dependencies.readStdin || !process.stdin.isTTY;
149
+ if (canReadStdin) {
150
+ prompt = (await (dependencies.readStdin ?? readStdin)()).trim();
151
+ }
152
+ }
153
+ if (!prompt) {
154
+ return writeFailure(
155
+ options.outputFormat !== "text",
156
+ stdout,
157
+ stderr,
158
+ "Headless mode requires a prompt argument or piped stdin."
159
+ );
160
+ }
161
+ if (options.continueLast) {
162
+ const latest = listStoredSessions(options.cwd)[0];
163
+ if (!latest) {
164
+ return writeFailure(
165
+ options.outputFormat !== "text",
166
+ stdout,
167
+ stderr,
168
+ `No earlier session in ${options.cwd} to continue.`
169
+ );
170
+ }
171
+ options.resume = latest.sessionId;
172
+ }
173
+ if (options.resume) {
174
+ const stored = loadStoredSession(options.resume);
175
+ if (!stored) {
176
+ return writeFailure(
177
+ options.outputFormat !== "text",
178
+ stdout,
179
+ stderr,
180
+ `Session ${options.resume} not found.`
181
+ );
182
+ }
183
+ options.cwd = stored.cwd;
184
+ }
185
+ const output = createOutput(options.outputFormat, stdout, stderr);
186
+ const client = (dependencies.clientFactory ?? ((cwd, permissionMode, emit) => new HeadlessAcpClient(cwd, permissionMode, emit)))(options.cwd, options.permissionMode, output.event);
187
+ const connection = startInProcessAgent(
188
+ () => clientFromContext(client.context()),
189
+ dependencies.agent ?? new NovaAgent()
190
+ );
191
+ const agent = connection.client;
192
+ const sessionMeta = { "nova-ai-cli/workspaceMcp": options.mcp };
193
+ const reportMcpFailures = (meta) => {
194
+ const failures = toRecord(toRecord(meta)?.["nova-ai-cli/mcp"])?.failures;
195
+ for (const failure of Array.isArray(failures) ? failures : []) {
196
+ const item = toRecord(failure);
197
+ if (typeof item?.serverName !== "string" || typeof item.message !== "string") continue;
198
+ output.write({
199
+ type: "mcp.configuration_error",
200
+ serverName: item.serverName,
201
+ message: item.message
202
+ });
203
+ }
204
+ };
205
+ let sessionId = null;
206
+ let interrupted = false;
207
+ const onInterrupt = () => {
208
+ interrupted = true;
209
+ if (sessionId) void agent.cancel({ sessionId }).catch(() => {
210
+ });
211
+ };
212
+ if (dependencies.registerSignals !== false)
213
+ process.once("SIGINT", onInterrupt);
214
+ try {
215
+ await agent.initialize({
216
+ protocolVersion: acp.PROTOCOL_VERSION,
217
+ clientCapabilities: client.capabilities,
218
+ clientInfo: { name: "nova-ai-cli-headless", version: "1" }
219
+ });
220
+ if (options.trustWorkspace) await agent.workspaceTrust({ cwd: options.cwd, trusted: true });
221
+ if (options.resume) {
222
+ const resumed = await agent.resumeSession({
223
+ sessionId: options.resume,
224
+ cwd: options.cwd,
225
+ mcpServers: [],
226
+ _meta: sessionMeta
227
+ });
228
+ reportMcpFailures(resumed._meta);
229
+ sessionId = options.resume;
230
+ } else {
231
+ const created = await agent.newSession({
232
+ cwd: options.cwd,
233
+ mcpServers: [],
234
+ _meta: sessionMeta
235
+ });
236
+ reportMcpFailures(created._meta);
237
+ sessionId = created.sessionId;
238
+ }
239
+ await agent.setSessionConfigOption({
240
+ sessionId,
241
+ configId: "permission_mode",
242
+ value: AGENT_PERMISSION_MODE[options.permissionMode]
243
+ });
244
+ output.write({
245
+ type: "session.started",
246
+ sessionId,
247
+ cwd: options.cwd,
248
+ model: options.model,
249
+ resumed: Boolean(options.resume),
250
+ permissionMode: options.permissionMode
251
+ });
252
+ output.write({ type: "message", role: "user", text: prompt });
253
+ const response = await agent.prompt({
254
+ sessionId,
255
+ prompt: [{ type: "text", text: prompt }],
256
+ ...options.model ? { _meta: { "nova-ai-cli/model": options.model } } : {}
257
+ });
258
+ const exitCode = interrupted ? 130 : response.stopReason === "max_turn_requests" ? 3 : response.stopReason === "cancelled" ? 2 : 0;
259
+ output.finishText();
260
+ output.write({
261
+ type: "result",
262
+ sessionId,
263
+ stopReason: response.stopReason,
264
+ exitCode
265
+ });
266
+ return exitCode;
267
+ } catch (error) {
268
+ output.finishText();
269
+ output.error(errorMessage(error));
270
+ return 1;
271
+ } finally {
272
+ if (dependencies.registerSignals !== false)
273
+ process.removeListener("SIGINT", onInterrupt);
274
+ if (sessionId) await agent.closeSession({ sessionId }).catch(() => ({}));
275
+ await connection.close().catch(() => {
276
+ });
277
+ await client.close().catch(() => {
278
+ });
279
+ }
280
+ }
281
+ function parseHeadlessArgs(args, defaultCwd) {
282
+ const options = {
283
+ prompt: null,
284
+ cwd: resolve(defaultCwd),
285
+ model: null,
286
+ resume: null,
287
+ continueLast: false,
288
+ outputFormat: "text",
289
+ mcp: true,
290
+ trustWorkspace: false,
291
+ permissionMode: "read-only",
292
+ help: false
293
+ };
294
+ const positional = [];
295
+ for (let index = 0; index < args.length; index++) {
296
+ const arg = args[index];
297
+ if (arg === "--") {
298
+ positional.push(...args.slice(index + 1));
299
+ break;
300
+ }
301
+ if (arg === "--json") options.outputFormat = "stream-json";
302
+ else if (arg === "--continue" || arg === "-c") options.continueLast = true;
303
+ else if (arg === "--output-format")
304
+ options.outputFormat = parseOutputFormat(takeValue(args, ++index, arg));
305
+ else if (arg.startsWith("--output-format="))
306
+ options.outputFormat = parseOutputFormat(arg.slice("--output-format=".length));
307
+ else if (arg === "--print" || arg === "-p") {
308
+ const next = args[index + 1];
309
+ if (next !== void 0 && !next.startsWith("-")) {
310
+ options.prompt = next;
311
+ index++;
312
+ }
313
+ } else if (arg === "--no-mcp") options.mcp = false;
314
+ else if (arg === "--trust-workspace") options.trustWorkspace = true;
315
+ else if (arg === "--help" || arg === "-h") options.help = true;
316
+ else if (arg === "--prompt")
317
+ options.prompt = takeValue(args, ++index, arg);
318
+ else if (arg.startsWith("--prompt="))
319
+ options.prompt = arg.slice("--prompt=".length);
320
+ else if (arg === "--cwd")
321
+ options.cwd = resolve(takeValue(args, ++index, arg));
322
+ else if (arg.startsWith("--cwd="))
323
+ options.cwd = resolve(arg.slice("--cwd=".length));
324
+ else if (arg === "--model") options.model = takeValue(args, ++index, arg);
325
+ else if (arg.startsWith("--model="))
326
+ options.model = arg.slice("--model=".length);
327
+ else if (arg === "--resume") options.resume = takeValue(args, ++index, arg);
328
+ else if (arg.startsWith("--resume="))
329
+ options.resume = arg.slice("--resume=".length);
330
+ else if (arg === "--permission-mode")
331
+ options.permissionMode = parsePermissionMode(
332
+ takeValue(args, ++index, arg)
333
+ );
334
+ else if (arg.startsWith("--permission-mode="))
335
+ options.permissionMode = parsePermissionMode(
336
+ arg.slice("--permission-mode=".length)
337
+ );
338
+ else if (arg.startsWith("-")) throw new Error(`Unknown option: ${arg}`);
339
+ else positional.push(arg);
340
+ }
341
+ if (options.prompt && positional.length) {
342
+ throw new Error(
343
+ "Provide the prompt either with --prompt or positionally, not both."
344
+ );
345
+ }
346
+ if (!options.prompt && positional.length)
347
+ options.prompt = positional.join(" ");
348
+ if (options.continueLast && options.resume) {
349
+ throw new Error("Use either --continue or --resume, not both.");
350
+ }
351
+ if (options.prompt !== null && !options.prompt.trim()) {
352
+ throw new Error("Prompt cannot be empty.");
353
+ }
354
+ return options;
355
+ }
356
+ function createOutput(format, stdout, stderr) {
357
+ let wroteText = false;
358
+ const summary = {
359
+ text: "",
360
+ tools: [],
361
+ permissions: []
362
+ };
363
+ const write = (record) => {
364
+ if (format === "stream-json") {
365
+ stdout.write(`${JSON.stringify({ schemaVersion: 1, ...record })}
366
+ `);
367
+ return;
368
+ }
369
+ if (format !== "json") return;
370
+ if (record.type === "result") {
371
+ stdout.write(
372
+ `${JSON.stringify({ schemaVersion: 1, ...record, text: summary.text, tools: summary.tools, permissions: summary.permissions })}
373
+ `
374
+ );
375
+ } else if (record.type === "error") {
376
+ stdout.write(`${JSON.stringify({ schemaVersion: 1, ...record })}
377
+ `);
378
+ } else if (record.type === "message.delta" && record.role === "assistant") {
379
+ summary.text += String(record.text ?? "");
380
+ } else if (record.type === "tool.finished") {
381
+ summary.tools.push({ toolCallId: record.toolCallId, status: record.status });
382
+ } else if (record.type === "permission") {
383
+ summary.permissions.push(record);
384
+ }
385
+ };
386
+ return {
387
+ write,
388
+ event: async (event) => {
389
+ if (event.type === "permission") {
390
+ write(event);
391
+ return;
392
+ }
393
+ const record = notificationRecord(event.method, event.params);
394
+ if (record) write(record);
395
+ if (format === "text" && record?.type === "message.delta" && record.role === "assistant") {
396
+ stdout.write(String(record.text ?? ""));
397
+ wroteText = true;
398
+ }
399
+ },
400
+ finishText: () => {
401
+ if (format === "text" && wroteText) {
402
+ stdout.write("\n");
403
+ wroteText = false;
404
+ }
405
+ },
406
+ error: (message) => {
407
+ if (format === "text") stderr.write(`${message}
408
+ `);
409
+ else write({ type: "error", message });
410
+ }
411
+ };
412
+ }
413
+ function notificationRecord(method, params) {
414
+ if (method === NOVA_NOTIFICATIONS.backgroundUpdate) {
415
+ return { type: "background.update", ...toRecord(params) ?? {} };
416
+ }
417
+ if (method === NOVA_NOTIFICATIONS.queueChanged) {
418
+ return { type: "queue.changed", ...toRecord(params) ?? {} };
419
+ }
420
+ if (method !== "session/update") {
421
+ return { type: "acp.notification", method, params };
422
+ }
423
+ const root = toRecord(params);
424
+ const update = toRecord(root?.update);
425
+ const sessionId = root?.sessionId;
426
+ switch (update?.sessionUpdate) {
427
+ case "agent_message_chunk":
428
+ return {
429
+ type: "message.delta",
430
+ sessionId,
431
+ role: "assistant",
432
+ text: textContent(update.content)
433
+ };
434
+ case "user_message_chunk":
435
+ return {
436
+ type: "message.delta",
437
+ sessionId,
438
+ role: "user",
439
+ text: textContent(update.content)
440
+ };
441
+ case "agent_thought_chunk":
442
+ return {
443
+ type: "thought",
444
+ sessionId,
445
+ text: textContent(update.content)
446
+ };
447
+ case "tool_call": {
448
+ const toolName = toRecord(update._meta)?.["nova-ai-cli/tool"];
449
+ return {
450
+ type: "tool.started",
451
+ sessionId,
452
+ toolCallId: update.toolCallId,
453
+ name: typeof toolName === "string" ? toolName : update.title,
454
+ title: update.title,
455
+ kind: update.kind,
456
+ input: update.rawInput
457
+ };
458
+ }
459
+ case "tool_call_update":
460
+ return {
461
+ type: update.status === "completed" || update.status === "failed" ? "tool.finished" : "tool.updated",
462
+ sessionId,
463
+ toolCallId: update.toolCallId,
464
+ status: update.status,
465
+ output: toRecord(update.rawOutput)?.output,
466
+ content: update.content
467
+ };
468
+ case "current_mode_update":
469
+ return {
470
+ type: "mode.changed",
471
+ sessionId,
472
+ mode: update.currentModeId
473
+ };
474
+ default:
475
+ return null;
476
+ }
477
+ }
478
+ function textContent(value) {
479
+ const content = toRecord(value);
480
+ return content?.type === "text" && typeof content.text === "string" ? content.text : "";
481
+ }
482
+ function toRecord(value) {
483
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
484
+ }
485
+ function parseOutputFormat(value) {
486
+ if (value === "text" || value === "json" || value === "stream-json") return value;
487
+ throw new Error(`Unknown output format: ${value}. Expected text, json, or stream-json.`);
488
+ }
489
+ function parsePermissionMode(value) {
490
+ if (value === "read-only" || value === "accept-edits" || value === "bypass-all") {
491
+ return value;
492
+ }
493
+ throw new Error(
494
+ `Unknown permission mode: ${value}. Expected read-only, accept-edits, or bypass-all.`
495
+ );
496
+ }
497
+ function takeValue(args, index, option) {
498
+ const value = args[index];
499
+ if (!value || value.startsWith("--")) {
500
+ throw new Error(`${option} requires a value.`);
501
+ }
502
+ return value;
503
+ }
504
+ async function readStdin() {
505
+ const chunks = [];
506
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
507
+ return Buffer.concat(chunks).toString("utf8");
508
+ }
509
+ function writeFailure(json, stdout, stderr, message) {
510
+ if (json)
511
+ stdout.write(
512
+ `${JSON.stringify({ schemaVersion: 1, type: "error", message })}
513
+ `
514
+ );
515
+ else stderr.write(`${message}
516
+ `);
517
+ return 1;
518
+ }
519
+ function errorMessage(error) {
520
+ return error instanceof Error ? error.message : String(error);
521
+ }
522
+ var HEADLESS_HELP = `Usage: nova-ai -p [options] [prompt]
523
+
524
+ Run one Nova agent request without the interactive terminal UI. The prompt
525
+ can also be piped on stdin.
526
+
527
+ Options:
528
+ -p, --print [prompt] Run headless (the prompt may follow)
529
+ --output-format <format> text (default), json (one object at the end)
530
+ or stream-json (JSON Lines while it runs)
531
+ --json Same as --output-format stream-json
532
+ --cwd <path> Workspace directory (default: current directory)
533
+ --model <id> Override the configured model
534
+ --resume <session-id> Continue a persisted session
535
+ -c, --continue Continue the most recent session of the workspace
536
+ --no-mcp Ignore workspace MCP configuration
537
+ --trust-workspace Trust this workspace (remembered): start the MCP
538
+ servers and apply the allow rules it declares
539
+ --permission-mode <mode> read-only (default), accept-edits, or bypass-all
540
+ -h, --help Show this help
541
+
542
+ Exit codes: 0 success, 1 configuration/runtime error, 2 cancelled,
543
+ 3 tool-turn safety limit, 130 interrupted.`;
544
+ var headless_default = runHeadless;
545
+ export {
546
+ HEADLESS_HELP,
547
+ headless_default as default,
548
+ parseHeadlessArgs,
549
+ runHeadless
550
+ };