zelari-code 1.30.3 → 1.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/cli/app.js +5 -0
  2. package/dist/cli/app.js.map +1 -1
  3. package/dist/cli/atMentions.js +77 -28
  4. package/dist/cli/atMentions.js.map +1 -1
  5. package/dist/cli/headless.js.map +1 -1
  6. package/dist/cli/hooks/useChatTurn.js +47 -30
  7. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  8. package/dist/cli/hooks/usePermissionBroker.js +87 -0
  9. package/dist/cli/hooks/usePermissionBroker.js.map +1 -0
  10. package/dist/cli/kraken/executor.js +109 -5
  11. package/dist/cli/kraken/executor.js.map +1 -1
  12. package/dist/cli/kraken/executor.workbench.test.js +134 -0
  13. package/dist/cli/kraken/executor.workbench.test.js.map +1 -0
  14. package/dist/cli/kraken/workbench.js +1 -0
  15. package/dist/cli/kraken/workbench.js.map +1 -1
  16. package/dist/cli/main.bundled.js +1837 -366
  17. package/dist/cli/main.bundled.js.map +4 -4
  18. package/dist/cli/main.js +24 -0
  19. package/dist/cli/main.js.map +1 -1
  20. package/dist/cli/mcp/brokerHandlers.js +123 -0
  21. package/dist/cli/mcp/brokerHandlers.js.map +1 -0
  22. package/dist/cli/mcp/mcpPermissionServer.js +271 -0
  23. package/dist/cli/mcp/mcpPermissionServer.js.map +1 -0
  24. package/dist/cli/mcp/mcpPresets.js +78 -5
  25. package/dist/cli/mcp/mcpPresets.js.map +1 -1
  26. package/dist/cli/mcp/permissionBroker.js +253 -0
  27. package/dist/cli/mcp/permissionBroker.js.map +1 -0
  28. package/dist/cli/mcp/permissionCli.js +46 -0
  29. package/dist/cli/mcp/permissionCli.js.map +1 -0
  30. package/dist/cli/provider/localCli/claudeProvider.js +149 -0
  31. package/dist/cli/provider/localCli/claudeProvider.js.map +1 -0
  32. package/dist/cli/provider/localCli/claudeStreamJson.js +175 -0
  33. package/dist/cli/provider/localCli/claudeStreamJson.js.map +1 -0
  34. package/dist/cli/provider/openai-compatible.js +59 -0
  35. package/dist/cli/provider/openai-compatible.js.map +1 -1
  36. package/dist/cli/runHeadless.js +59 -12
  37. package/dist/cli/runHeadless.js.map +1 -1
  38. package/dist/cli/skillConfigIo.js +1 -0
  39. package/dist/cli/skillConfigIo.js.map +1 -1
  40. package/dist/cli/slashCommands.js +22 -1
  41. package/dist/cli/slashCommands.js.map +1 -1
  42. package/package.json +2 -2
@@ -1,6 +1,9 @@
1
1
  /**
2
- * Named MCP presets for optional capabilities (Cua Driver, ).
2
+ * Named MCP presets for optional capabilities (Cua Driver, Composio Connect).
3
3
  * Does not vendor binaries — only writes ~/.zelari-code/mcp.json (or project).
4
+ *
5
+ * Presets are FACTORIES: env-dependent config (e.g. COMPOSIO_API_KEY) is read
6
+ * at apply time, never captured at module load.
4
7
  */
5
8
  import { upsertMcpServer, } from "./mcpConfigIo.js";
6
9
  /** Official Cua Driver MCP entry (trycua/cua). Binary must be on PATH. */
@@ -22,15 +25,85 @@ export const CUA_DRIVER_PRESET = {
22
25
  "Council: Cua tools are skipped for specialists unless ZELARI_CUA_COUNCIL=1",
23
26
  ],
24
27
  };
28
+ /**
29
+ * Composio Connect MCP preset (composio.dev). 500+ app integrations exposed
30
+ * as MCP tools via `npx composio-mcp`. The API key passes through env
31
+ * (never written into argv/mcp.json args) and is read at APPLY time.
32
+ */
33
+ export function buildComposioPreset() {
34
+ return {
35
+ id: "composio",
36
+ servers: {
37
+ composio: {
38
+ command: "npx",
39
+ args: ["-y", "composio-mcp@latest"],
40
+ env: process.env.COMPOSIO_API_KEY
41
+ ? { COMPOSIO_API_KEY: process.env.COMPOSIO_API_KEY }
42
+ : {},
43
+ enabled: true,
44
+ },
45
+ },
46
+ notes: [
47
+ "Composio Connect — 500+ app integrations (GitHub, Slack, Notion, ...) as MCP tools.",
48
+ "Set COMPOSIO_API_KEY before applying: export COMPOSIO_API_KEY=<key>",
49
+ "Connect apps at https://composio.dev — each connected account becomes a tool.",
50
+ "The key is stored in mcp.json env, never in argv.",
51
+ "Kill switch: disable the composio server in mcp.json or ZELARI_MCP=0",
52
+ ],
53
+ };
54
+ }
55
+ /**
56
+ * Qwen-MM-Plugins MCP preset (QwenLM/Qwen-MM-Plugins). Multimodal tools
57
+ * (vision, video, audio, 3D, web search) served over stdio via `uvx`.
58
+ * The capability is selected at APPLY time through QWEN_MM_PLUGIN (default
59
+ * `qwen-mm-plugins-core`); API keys pass through env, never argv.
60
+ *
61
+ * Windows: not validated upstream — prefer WSL2 (Ubuntu) and run the
62
+ * upstream guided installer first: `bash install.sh`.
63
+ */
64
+ export function buildQwenMmPreset() {
65
+ const plugin = process.env.QWEN_MM_PLUGIN?.trim() || "qwen-mm-plugins-core";
66
+ return {
67
+ id: "qwen-mm-plugins",
68
+ servers: {
69
+ "qwen-mm-plugins": {
70
+ command: "uvx",
71
+ args: [plugin],
72
+ env: {
73
+ ...(process.env.DASHSCOPE_API_KEY
74
+ ? { DASHSCOPE_API_KEY: process.env.DASHSCOPE_API_KEY }
75
+ : {}),
76
+ ...(process.env.SERPER_API_KEY
77
+ ? { SERPER_API_KEY: process.env.SERPER_API_KEY }
78
+ : {}),
79
+ },
80
+ enabled: true,
81
+ },
82
+ },
83
+ notes: [
84
+ "Qwen-MM-Plugins — multimodal capabilities (core / video-memory / video-edit / blender / freecad) as MCP tools.",
85
+ "Run the upstream guided installer first: curl -fsSL https://raw.githubusercontent.com/QwenLM/Qwen-MM-Plugins/main/install.sh | bash",
86
+ "Windows: use WSL2 (Ubuntu); native Windows is not validated upstream.",
87
+ "Pick a capability: export QWEN_MM_PLUGIN=qwen-mm-plugins-video-memory (default: qwen-mm-plugins-core).",
88
+ "API keys read at apply time: DASHSCOPE_API_KEY, SERPER_API_KEY (native reading works without them).",
89
+ "Blender/FreeCAD variants: start the host app before invoking tools.",
90
+ "Kill switch: disable the qwen-mm-plugins server in mcp.json or ZELARI_MCP=0",
91
+ ],
92
+ };
93
+ }
25
94
  const PRESETS = {
26
- cua: CUA_DRIVER_PRESET,
27
- "cua-driver": CUA_DRIVER_PRESET,
95
+ cua: () => CUA_DRIVER_PRESET,
96
+ "cua-driver": () => CUA_DRIVER_PRESET,
97
+ composio: buildComposioPreset,
98
+ "qwen-mm-plugins": buildQwenMmPreset,
99
+ "qwen-mm": buildQwenMmPreset,
28
100
  };
29
101
  export function listMcpPresetIds() {
30
- return Object.keys(PRESETS).filter((k) => k === "cua"); // canonical ids only
102
+ return Object.keys(PRESETS).filter((k) => k === "cua" || k === "composio" || k === "qwen-mm-plugins"); // canonical ids only
31
103
  }
32
104
  export function getMcpPreset(id) {
33
- return PRESETS[id.trim().toLowerCase()] ?? null;
105
+ const factory = PRESETS[id.trim().toLowerCase()];
106
+ return factory ? factory() : null;
34
107
  }
35
108
  export function applyMcpPreset(opts) {
36
109
  const preset = getMcpPreset(opts.presetId);
@@ -1 +1 @@
1
- {"version":3,"file":"mcpPresets.js","sourceRoot":"","sources":["../../../src/cli/mcp/mcpPresets.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,eAAe,GAEhB,MAAM,kBAAkB,CAAC;AAU1B,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAc;IAC1C,EAAE,EAAE,KAAK;IACT,OAAO,EAAE;QACP,YAAY,EAAE;YACZ,OAAO,EAAE,YAAY;YACrB,IAAI,EAAE,CAAC,KAAK,CAAC;YACb,OAAO,EAAE,IAAI;SACd;KACF;IACD,KAAK,EAAE;QACL,4EAA4E;QAC5E,mEAAmE;QACnE,qDAAqD;QACrD,8EAA8E;QAC9E,mEAAmE;QACnE,4EAA4E;KAC7E;CACF,CAAC;AAEF,MAAM,OAAO,GAA8B;IACzC,GAAG,EAAE,iBAAiB;IACtB,YAAY,EAAE,iBAAiB;CAChC,CAAC;AAEF,MAAM,UAAU,gBAAgB;IAC9B,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,qBAAqB;AAC/E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAO,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAI9B;IAGC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,mBAAmB,IAAI,CAAC,QAAQ,aAAa,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SACpF,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC;IACnC,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,MAAM,CAAC,GAAG,eAAe,CAAC;YACxB,KAAK;YACL,IAAI;YACJ,MAAM;YACN,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QAChD,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC9D,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC7B,OAAO,CAAC,KAAK,YAAY,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AACnE,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,KAAK,GAAG,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"mcpPresets.js","sourceRoot":"","sources":["../../../src/cli/mcp/mcpPresets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * permissionBroker — socket broker for external-agent permission prompts.
3
+ *
4
+ * Pattern ported from OpenMausBot (`server/permission-proxy.ts`, MIT): an
5
+ * external CLI agent (e.g. `claude --permission-prompt-tool`) spawns
6
+ * `zelari-code --permission-mcp <socket>` as a child MCP stdio server; that
7
+ * child forwards `approve` / `ask_user` requests to THIS broker (the parent
8
+ * zelari-code TUI/headless process) over a local socket, and the human
9
+ * decision flows back.
10
+ *
11
+ * Transport: JSON-lines over `node:net` — unix socket on POSIX, named pipe
12
+ * on Windows (same API). Zero new dependencies.
13
+ *
14
+ * Wire protocol (broker side):
15
+ * client → broker: {"t":"ask","id":"<uuid>","kind":"permission"|"question", ...}
16
+ * broker → client: {"t":"answer","id":"<uuid>","behavior":"allow"|"deny","message"?,"always"?,"answer"?}
17
+ *
18
+ * If no handler is registered (or the handler throws), the broker answers
19
+ * `deny` — the agent must never hang waiting on a broken approval channel.
20
+ *
21
+ * @since v1.30.0
22
+ */
23
+ import { connect, createServer } from "node:net";
24
+ import { unlink } from "node:fs";
25
+ export const PERMISSION_BROKER_DEFAULT_TIMEOUT_MS = 60_000;
26
+ export const PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS = 2_000;
27
+ function sendJson(socket, obj) {
28
+ if (socket.destroyed)
29
+ return;
30
+ socket.write(JSON.stringify(obj) + "\n");
31
+ }
32
+ function safeSocketPath(socketPath) {
33
+ return socketPath.trim();
34
+ }
35
+ /**
36
+ * Start the broker listener on `socketPath`. Resolves once listening.
37
+ *
38
+ * Each connection is treated as a stream of `ask` messages; every request
39
+ * gets exactly one `answer` (handler result, handler absence, error, or a
40
+ * per-request timeout — never nothing).
41
+ */
42
+ export function startPermissionBroker(socketPath, handlers, opts) {
43
+ const path = safeSocketPath(socketPath);
44
+ const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
45
+ const sockets = new Set();
46
+ const server = createServer((socket) => {
47
+ socket.setEncoding("utf8");
48
+ sockets.add(socket);
49
+ socket.on("close", () => sockets.delete(socket));
50
+ socket.on("error", () => {
51
+ // Client vanished mid-request — nothing to answer.
52
+ });
53
+ let buffer = "";
54
+ socket.on("data", (chunk) => {
55
+ buffer += chunk;
56
+ let nl;
57
+ while ((nl = buffer.indexOf("\n")) >= 0) {
58
+ const line = buffer.slice(0, nl).trim();
59
+ buffer = buffer.slice(nl + 1);
60
+ if (!line)
61
+ continue;
62
+ void handleLine(socket, line);
63
+ }
64
+ });
65
+ });
66
+ server.on("error", () => {
67
+ // Post-listen errors (e.g. client reset) are non-fatal.
68
+ });
69
+ async function handleLine(socket, line) {
70
+ let msg;
71
+ try {
72
+ msg = JSON.parse(line);
73
+ }
74
+ catch {
75
+ return; // ignore non-JSON noise
76
+ }
77
+ if (!msg || msg.t !== "ask" || typeof msg.id !== "string" || !msg.id)
78
+ return;
79
+ const id = msg.id;
80
+ let settled = false;
81
+ let timer;
82
+ const reply = (answer) => {
83
+ if (settled)
84
+ return;
85
+ settled = true;
86
+ if (timer)
87
+ clearTimeout(timer);
88
+ sendJson(socket, { t: "answer", id, ...answer });
89
+ };
90
+ timer = setTimeout(() => {
91
+ reply({
92
+ behavior: "deny",
93
+ message: `[zelari] permission broker timed out after ${requestTimeoutMs}ms`,
94
+ });
95
+ }, requestTimeoutMs);
96
+ if (typeof timer.unref === "function")
97
+ timer.unref();
98
+ try {
99
+ if (msg.kind === "question") {
100
+ if (!handlers.onQuestion) {
101
+ reply({
102
+ behavior: "deny",
103
+ message: "[zelari] no interactive answer available (question handler missing)",
104
+ });
105
+ return;
106
+ }
107
+ const answer = await handlers.onQuestion({
108
+ id,
109
+ question: msg.question ?? "",
110
+ choices: msg.choices,
111
+ context: msg.context,
112
+ });
113
+ if (answer == null) {
114
+ reply({
115
+ behavior: "deny",
116
+ message: "[zelari] question cancelled (no answer)",
117
+ });
118
+ return;
119
+ }
120
+ if (answer.behavior === "deny") {
121
+ reply({
122
+ behavior: "deny",
123
+ message: answer.message ?? "[zelari] question denied by handler",
124
+ });
125
+ return;
126
+ }
127
+ reply({ behavior: "allow", answer: answer.answer ?? "" });
128
+ return;
129
+ }
130
+ // kind === "permission"
131
+ const answer = handlers.onPermission
132
+ ? await handlers.onPermission({
133
+ id,
134
+ tool: msg.tool ?? "unknown",
135
+ input: msg.input,
136
+ toolUseId: msg.toolUseId,
137
+ suggestions: msg.suggestions,
138
+ })
139
+ : {
140
+ behavior: "deny",
141
+ message: "[zelari] permission broker has no approval handler",
142
+ };
143
+ reply(answer);
144
+ }
145
+ catch (err) {
146
+ reply({
147
+ behavior: "deny",
148
+ message: `[zelari] permission broker error: ${err instanceof Error ? err.message : String(err)}`,
149
+ });
150
+ }
151
+ }
152
+ return new Promise((resolve, reject) => {
153
+ const onError = (err) => reject(err);
154
+ server.once("error", onError);
155
+ server.listen(path, () => {
156
+ server.removeListener("error", onError);
157
+ resolve({
158
+ socketPath: path,
159
+ stop: () => new Promise((res) => {
160
+ for (const s of sockets)
161
+ s.destroy();
162
+ sockets.clear();
163
+ // Windows named pipes: server.close() waits for every accepted
164
+ // connection to end — closeAllConnections() forces that so stop()
165
+ // never hangs on a stale client handle.
166
+ const closeAll = server.closeAllConnections;
167
+ if (typeof closeAll === "function")
168
+ closeAll.call(server);
169
+ let done = false;
170
+ const finish = () => {
171
+ if (done)
172
+ return;
173
+ done = true;
174
+ if (process.platform !== "win32") {
175
+ unlink(path, () => res());
176
+ }
177
+ else {
178
+ res();
179
+ }
180
+ };
181
+ server.close(() => finish());
182
+ // Safety net: never let stop() hang (e.g. platform quirks).
183
+ setTimeout(finish, 1_000).unref?.();
184
+ }),
185
+ });
186
+ });
187
+ });
188
+ }
189
+ /**
190
+ * Send one `ask` over the wire and await the matching `answer`.
191
+ *
192
+ * Used by the MCP stdio server (`--permission-mcp`) to reach the broker in
193
+ * the parent zelari process. Rejects (never hangs) when:
194
+ * - the socket cannot be reached within `connectTimeoutMs`,
195
+ * - the broker closes the connection without answering,
196
+ * - no answer arrives within `requestTimeoutMs`.
197
+ */
198
+ export function requestBrokerAsk(socketPath, ask, opts) {
199
+ const path = safeSocketPath(socketPath);
200
+ const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
201
+ const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
202
+ return new Promise((resolve, reject) => {
203
+ const socket = connect(path);
204
+ let buffer = "";
205
+ let settled = false;
206
+ const settle = (fn) => {
207
+ if (settled)
208
+ return;
209
+ settled = true;
210
+ clearTimeout(connectTimer);
211
+ clearTimeout(requestTimer);
212
+ socket.destroy();
213
+ fn();
214
+ };
215
+ const connectTimer = setTimeout(() => {
216
+ settle(() => reject(new Error(`permission broker unavailable at "${path}" (connect timed out after ${connectTimeoutMs}ms)`)));
217
+ }, connectTimeoutMs);
218
+ const requestTimer = setTimeout(() => {
219
+ settle(() => reject(new Error(`permission broker timed out after ${requestTimeoutMs}ms waiting for an answer`)));
220
+ }, requestTimeoutMs);
221
+ socket.setEncoding("utf8");
222
+ socket.on("connect", () => clearTimeout(connectTimer));
223
+ socket.on("error", (err) => settle(() => reject(err)));
224
+ socket.on("close", () => {
225
+ settle(() => reject(new Error("permission broker closed the connection without an answer")));
226
+ });
227
+ socket.on("data", (chunk) => {
228
+ buffer += chunk;
229
+ let nl;
230
+ while ((nl = buffer.indexOf("\n")) >= 0) {
231
+ const line = buffer.slice(0, nl).trim();
232
+ buffer = buffer.slice(nl + 1);
233
+ if (!line)
234
+ continue;
235
+ let msg;
236
+ try {
237
+ msg = JSON.parse(line);
238
+ }
239
+ catch {
240
+ continue; // ignore non-JSON noise
241
+ }
242
+ if (msg?.t === "answer" && msg.id === ask.id) {
243
+ // Strip transport fields (t, id) — the caller wants the answer only.
244
+ const { t: _t, id: _id, ...answer } = msg;
245
+ settle(() => resolve(answer));
246
+ return;
247
+ }
248
+ }
249
+ });
250
+ sendJson(socket, ask);
251
+ });
252
+ }
253
+ //# sourceMappingURL=permissionBroker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissionBroker.js","sourceRoot":"","sources":["../../../src/cli/mcp/permissionBroker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,OAAO,EAAE,YAAY,EAA4B,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,oCAAoC,GAAG,MAAM,CAAC;AAC3D,MAAM,CAAC,MAAM,4CAA4C,GAAG,KAAK,CAAC;AAgElE,SAAS,QAAQ,CAAC,MAAc,EAAE,GAAY;IAC5C,IAAI,MAAM,CAAC,SAAS;QAAE,OAAO;IAC7B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,cAAc,CAAC,UAAkB;IACxC,OAAO,UAAU,CAAC,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAkB,EAClB,QAAkC,EAClC,IAAoC;IAEpC,MAAM,IAAI,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,gBAAgB,GACpB,IAAI,EAAE,gBAAgB,IAAI,oCAAoC,CAAC;IACjE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,MAAM,MAAM,GAAW,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QAC7C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,mDAAmD;QACrD,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC;YAChB,IAAI,EAAU,CAAC;YACf,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACxC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC9B,IAAI,CAAC,IAAI;oBAAE,SAAS;gBACpB,KAAK,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACtB,wDAAwD;IAC1D,CAAC,CAAC,CAAC;IAEH,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,IAAY;QACpD,IAAI,GAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,wBAAwB;QAClC,CAAC;QACD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO;QAE7E,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QAClB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,KAAiC,CAAC;QAEtC,MAAM,KAAK,GAAG,CAAC,MAAuB,EAAE,EAAE;YACxC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC;QAEF,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,KAAK,CAAC;gBACJ,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,8CAA8C,gBAAgB,IAAI;aAC5E,CAAC,CAAC;QACL,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACrB,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU;YAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QAErD,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC5B,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;oBACzB,KAAK,CAAC;wBACJ,QAAQ,EAAE,MAAM;wBAChB,OAAO,EACL,qEAAqE;qBACxE,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC;oBACvC,EAAE;oBACF,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE;oBAC5B,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,OAAO,EAAE,GAAG,CAAC,OAAO;iBACrB,CAAC,CAAC;gBACH,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;oBACnB,KAAK,CAAC;wBACJ,QAAQ,EAAE,MAAM;wBAChB,OAAO,EAAE,yCAAyC;qBACnD,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBACD,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;oBAC/B,KAAK,CAAC;wBACJ,QAAQ,EAAE,MAAM;wBAChB,OAAO,EACL,MAAM,CAAC,OAAO,IAAI,qCAAqC;qBAC1D,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBACD,KAAK,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC1D,OAAO;YACT,CAAC;YACD,wBAAwB;YACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,YAAY;gBAClC,CAAC,CAAC,MAAM,QAAQ,CAAC,YAAY,CAAC;oBAC1B,EAAE;oBACF,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,SAAS;oBAC3B,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,WAAW,EAAE,GAAG,CAAC,WAAW;iBAC7B,CAAC;gBACJ,CAAC,CAAC;oBACE,QAAQ,EAAE,MAAe;oBACzB,OAAO,EAAE,oDAAoD;iBAC9D,CAAC;YACN,KAAK,CAAC,MAAM,CAAC,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC;gBACJ,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,qCACP,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE;aACH,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,IAAI,OAAO,CAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7D,MAAM,OAAO,GAAG,CAAC,GAAU,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YACvB,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACxC,OAAO,CAAC;gBACN,UAAU,EAAE,IAAI;gBAChB,IAAI,EAAE,GAAG,EAAE,CACT,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,EAAE;oBACxB,KAAK,MAAM,CAAC,IAAI,OAAO;wBAAE,CAAC,CAAC,OAAO,EAAE,CAAC;oBACrC,OAAO,CAAC,KAAK,EAAE,CAAC;oBAChB,+DAA+D;oBAC/D,kEAAkE;oBAClE,wCAAwC;oBACxC,MAAM,QAAQ,GACZ,MACD,CAAC,mBAAmB,CAAC;oBACtB,IAAI,OAAO,QAAQ,KAAK,UAAU;wBAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAC1D,IAAI,IAAI,GAAG,KAAK,CAAC;oBACjB,MAAM,MAAM,GAAG,GAAG,EAAE;wBAClB,IAAI,IAAI;4BAAE,OAAO;wBACjB,IAAI,GAAG,IAAI,CAAC;wBACZ,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;4BACjC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;wBAC5B,CAAC;6BAAM,CAAC;4BACN,GAAG,EAAE,CAAC;wBACR,CAAC;oBACH,CAAC,CAAC;oBACF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;oBAC7B,4DAA4D;oBAC5D,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBACtC,CAAC,CAAC;aACL,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAkB,EAClB,GAAqB,EACrB,IAGC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,gBAAgB,GACpB,IAAI,EAAE,gBAAgB,IAAI,oCAAoC,CAAC;IACjE,MAAM,gBAAgB,GACpB,IAAI,EAAE,gBAAgB,IAAI,4CAA4C,CAAC;IAEzE,OAAO,IAAI,OAAO,CAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,MAAM,GAAG,CAAC,EAAc,EAAE,EAAE;YAChC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3B,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3B,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,EAAE,EAAE,CAAC;QACP,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CACJ,IAAI,KAAK,CACP,qCAAqC,IAAI,8BAA8B,gBAAgB,KAAK,CAC7F,CACF,CACF,CAAC;QACJ,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACrB,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CACJ,IAAI,KAAK,CACP,qCAAqC,gBAAgB,0BAA0B,CAChF,CACF,CACF,CAAC;QACJ,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAErB,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CAAC,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC,CAC/E,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC;YAChB,IAAI,EAAU,CAAC;YACf,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACxC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC9B,IAAI,CAAC,IAAI;oBAAE,SAAS;gBACpB,IAAI,GAAgC,CAAC;gBACrC,IAAI,CAAC;oBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgC,CAAC;gBACxD,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS,CAAC,wBAAwB;gBACpC,CAAC;gBACD,IAAI,GAAG,EAAE,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC;oBAC7C,qEAAqE;oBACrE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,GAAG,CAAC;oBAC1C,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAyB,CAAC,CAAC,CAAC;oBACjD,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * permissionCli — entry point for `zelari-code --permission-mcp <socket>`.
3
+ *
4
+ * Standalone MCP stdio server (OpenMausBot permission-proxy pattern).
5
+ * Kept as a separate module so the child process spawned by an external CLI
6
+ * (`claude --permission-prompt-tool "zelari-code --permission-mcp <sock>"`)
7
+ * never mounts Ink / the TUI / preflight checks.
8
+ *
9
+ * The process stays alive until the parent closes stdin (or we get
10
+ * SIGINT/SIGTERM), then exits cleanly.
11
+ *
12
+ * @since v1.30.0
13
+ */
14
+ import { startPermissionMcpServer } from "./mcpPermissionServer.js";
15
+ export async function runPermissionCli(socketPath) {
16
+ const server = await startPermissionMcpServer({ socketPath });
17
+ process.stderr.write(`[zelari-code --permission-mcp] listening on ${socketPath}\n`);
18
+ let exiting = false;
19
+ const shutdown = async (code) => {
20
+ if (exiting)
21
+ return;
22
+ exiting = true;
23
+ try {
24
+ await server.stop();
25
+ }
26
+ catch {
27
+ // best-effort teardown
28
+ }
29
+ process.exit(code);
30
+ };
31
+ // Parent CLI closed our stdin → we are done.
32
+ process.stdin.on("close", () => {
33
+ void shutdown(0);
34
+ });
35
+ process.on("SIGINT", () => {
36
+ void shutdown(0);
37
+ });
38
+ process.on("SIGTERM", () => {
39
+ void shutdown(0);
40
+ });
41
+ // Keep the stream flowing even if the parent never writes (readable side).
42
+ process.stdin.resume?.();
43
+ await server.closed;
44
+ await shutdown(0);
45
+ }
46
+ //# sourceMappingURL=permissionCli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissionCli.js","sourceRoot":"","sources":["../../../src/cli/mcp/permissionCli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAkB;IACvD,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;IAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+CAA+C,UAAU,IAAI,CAC9D,CAAC;IAEF,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAY,EAAE,EAAE;QACtC,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,CAAC;IAEF,6CAA6C;IAC7C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC7B,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACzB,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;IAEzB,MAAM,MAAM,CAAC,MAAM,CAAC;IACpB,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * claudeProvider — local-CLI provider (Slice B, OpenMausBot driver pattern,
3
+ * MIT). Implements zelari's `ProviderStreamFn` by spawning an external agent
4
+ * CLI (`claude` by default; `ZELARI_LOCAL_CLI` to override) in print mode
5
+ * with `--output-format stream-json --input-format stream-json`, feeding it
6
+ * the harness conversation, and translating its events into ProviderDelta.
7
+ *
8
+ * The external CLI is an autonomous agent: it executes its own tools and, when
9
+ * `ZELARI_PERM_SOCKET` is set (Slice A broker in the parent zelari process),
10
+ * approval prompts arrive in the zelari TUI via
11
+ * `--permission-prompt-tool "zelari-code --permission-mcp <socket>"`.
12
+ *
13
+ * Stateless per harness contract: the harness re-invokes the provider with
14
+ * the full updated conversation on every tool-loop iteration, so each call
15
+ * spawns a fresh CLI process.
16
+ *
17
+ * `cli`/`args` are injectable for tests (a fake CLI script).
18
+ *
19
+ * @since v1.30.0
20
+ */
21
+ import { spawn } from 'node:child_process';
22
+ import { buildClaudeInputLines, createClaudeStreamParser, } from './claudeStreamJson.js';
23
+ const DEFAULT_ARGS = [
24
+ '-p',
25
+ '--output-format',
26
+ 'stream-json',
27
+ '--input-format',
28
+ 'stream-json',
29
+ '--verbose',
30
+ ];
31
+ /** Resolve the child exit code, waiting for the 'exit' event if needed. */
32
+ function waitForExit(child, timeoutMs = 2_000) {
33
+ return new Promise((resolve) => {
34
+ if (child.exitCode != null)
35
+ return resolve(child.exitCode);
36
+ const timer = setTimeout(() => resolve(child.exitCode ?? null), timeoutMs);
37
+ timer.unref?.();
38
+ child.once('exit', (code) => {
39
+ clearTimeout(timer);
40
+ resolve(code ?? null);
41
+ });
42
+ });
43
+ }
44
+ export function createLocalCliProvider(opts = {}) {
45
+ return async function* (params) {
46
+ const cli = opts.cli ?? process.env.ZELARI_LOCAL_CLI ?? 'claude';
47
+ const model = opts.model ?? params.model;
48
+ const permSocket = opts.permissionSocketPath ?? process.env.ZELARI_PERM_SOCKET ?? '';
49
+ const args = opts.args ?? [...DEFAULT_ARGS];
50
+ if (opts.args === undefined) {
51
+ if (model) {
52
+ args.push('--model', model);
53
+ }
54
+ if (permSocket) {
55
+ args.push('--permission-prompt-tool', `zelari-code --permission-mcp ${permSocket}`);
56
+ }
57
+ }
58
+ const spawnFn = opts.spawnFn ?? spawn;
59
+ let child;
60
+ try {
61
+ child = spawnFn(cli, args, {
62
+ stdio: ['pipe', 'pipe', 'pipe'],
63
+ ...(opts.env ? { env: opts.env } : {}),
64
+ });
65
+ }
66
+ catch (err) {
67
+ yield {
68
+ kind: 'error',
69
+ message: `[local-cli] failed to spawn ${cli}: ${err instanceof Error ? err.message : String(err)}`,
70
+ };
71
+ return;
72
+ }
73
+ const spawnErrorBox = { err: null };
74
+ child.once('error', (err) => {
75
+ spawnErrorBox.err = err;
76
+ });
77
+ const onAbort = () => {
78
+ child.kill();
79
+ };
80
+ params.signal?.addEventListener('abort', onAbort, { once: true });
81
+ let stderrTail = '';
82
+ child.stderr.setEncoding('utf8');
83
+ child.stderr.on('data', (d) => {
84
+ stderrTail = (stderrTail + d).slice(-2000);
85
+ });
86
+ // Feed the conversation, then close stdin so the CLI starts.
87
+ for (const line of buildClaudeInputLines(params.messages)) {
88
+ child.stdin.write(line + '\n');
89
+ }
90
+ child.stdin.end();
91
+ const parser = createClaudeStreamParser();
92
+ let buf = '';
93
+ let finished = false;
94
+ try {
95
+ for await (const chunk of child.stdout) {
96
+ buf += chunk.toString('utf8');
97
+ let nl;
98
+ while ((nl = buf.indexOf('\n')) >= 0) {
99
+ const line = buf.slice(0, nl);
100
+ buf = buf.slice(nl + 1);
101
+ for (const delta of parser.push(line)) {
102
+ if (delta.kind === 'finish')
103
+ finished = true;
104
+ yield delta;
105
+ }
106
+ }
107
+ }
108
+ // Trailing line without newline.
109
+ if (buf.trim().length > 0) {
110
+ for (const delta of parser.push(buf)) {
111
+ if (delta.kind === 'finish')
112
+ finished = true;
113
+ yield delta;
114
+ }
115
+ }
116
+ if (!finished) {
117
+ const spawnErr = spawnErrorBox.err;
118
+ if (spawnErr) {
119
+ yield {
120
+ kind: 'error',
121
+ message: `[local-cli] ${cli} failed to start: ${spawnErr.message}`,
122
+ };
123
+ }
124
+ else {
125
+ // stdout may close a tick before the 'exit' event fires, so await
126
+ // the exit code deterministically instead of reading child.exitCode
127
+ // right away (it can still be null here).
128
+ const exitCode = await waitForExit(child);
129
+ if (exitCode != null && exitCode !== 0) {
130
+ yield {
131
+ kind: 'error',
132
+ message: `[local-cli] ${cli} exited ${exitCode}: ${stderrTail.trim() || 'no stderr'}`,
133
+ };
134
+ }
135
+ else {
136
+ // Stream ended without a result event (e.g. killed) — close cleanly.
137
+ yield { kind: 'finish', reason: parser.stopReason || 'stop' };
138
+ }
139
+ }
140
+ }
141
+ }
142
+ finally {
143
+ params.signal?.removeEventListener('abort', onAbort);
144
+ if (child.exitCode == null)
145
+ child.kill();
146
+ }
147
+ };
148
+ }
149
+ //# sourceMappingURL=claudeProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudeProvider.js","sourceRoot":"","sources":["../../../../src/cli/provider/localCli/claudeProvider.ts"],"names":[],"mappings":"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"}