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.
- package/dist/cli/app.js +5 -0
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/atMentions.js +77 -28
- package/dist/cli/atMentions.js.map +1 -1
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +47 -30
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/usePermissionBroker.js +87 -0
- package/dist/cli/hooks/usePermissionBroker.js.map +1 -0
- package/dist/cli/kraken/executor.js +109 -5
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/executor.workbench.test.js +134 -0
- package/dist/cli/kraken/executor.workbench.test.js.map +1 -0
- package/dist/cli/kraken/workbench.js +1 -0
- package/dist/cli/kraken/workbench.js.map +1 -1
- package/dist/cli/main.bundled.js +1837 -366
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +24 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/brokerHandlers.js +123 -0
- package/dist/cli/mcp/brokerHandlers.js.map +1 -0
- package/dist/cli/mcp/mcpPermissionServer.js +271 -0
- package/dist/cli/mcp/mcpPermissionServer.js.map +1 -0
- package/dist/cli/mcp/mcpPresets.js +78 -5
- package/dist/cli/mcp/mcpPresets.js.map +1 -1
- package/dist/cli/mcp/permissionBroker.js +253 -0
- package/dist/cli/mcp/permissionBroker.js.map +1 -0
- package/dist/cli/mcp/permissionCli.js +46 -0
- package/dist/cli/mcp/permissionCli.js.map +1 -0
- package/dist/cli/provider/localCli/claudeProvider.js +149 -0
- package/dist/cli/provider/localCli/claudeProvider.js.map +1 -0
- package/dist/cli/provider/localCli/claudeStreamJson.js +175 -0
- package/dist/cli/provider/localCli/claudeStreamJson.js.map +1 -0
- package/dist/cli/provider/openai-compatible.js +59 -0
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +59 -12
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/skillConfigIo.js +1 -0
- package/dist/cli/skillConfigIo.js.map +1 -1
- package/dist/cli/slashCommands.js +22 -1
- package/dist/cli/slashCommands.js.map +1 -1
- 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
|
-
|
|
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
|
|
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":"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"}
|
|
@@ -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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"}
|