@trops/dash-core 0.1.48 → 0.1.50
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/electron/index.js +558 -26
- package/dist/electron/index.js.map +1 -1
- package/package.json +1 -1
package/dist/electron/index.js
CHANGED
|
@@ -22,7 +22,8 @@ var require$$2$4 = require('algoliasearch');
|
|
|
22
22
|
var require$$3$2 = require('node:path');
|
|
23
23
|
var require$$0$3 = require('openai');
|
|
24
24
|
require('live-plugin-manager');
|
|
25
|
-
var require$$0$
|
|
25
|
+
var require$$0$5 = require('@anthropic-ai/sdk');
|
|
26
|
+
var require$$0$4 = require('child_process');
|
|
26
27
|
var require$$2$6 = require('os');
|
|
27
28
|
var require$$3$3 = require('adm-zip');
|
|
28
29
|
var require$$4$1 = require('url');
|
|
@@ -525,23 +526,31 @@ var openaiEvents$1 = {
|
|
|
525
526
|
const LLM_SEND_MESSAGE$1 = "llm-send-message";
|
|
526
527
|
const LLM_ABORT_REQUEST$1 = "llm-abort-request";
|
|
527
528
|
const LLM_LIST_CONNECTED_TOOLS$1 = "llm-list-connected-tools";
|
|
529
|
+
const LLM_CHECK_CLI_AVAILABLE$1 = "llm-check-cli-available";
|
|
530
|
+
const LLM_CLEAR_CLI_SESSION$1 = "llm-clear-cli-session";
|
|
531
|
+
const LLM_CLI_SESSION_STATUS$1 = "llm-cli-session-status";
|
|
532
|
+
const LLM_CLI_END_SESSION$1 = "llm-cli-end-session";
|
|
528
533
|
|
|
529
534
|
// --- Main → Renderer (send) ---
|
|
530
|
-
const LLM_STREAM_DELTA$
|
|
531
|
-
const LLM_STREAM_TOOL_CALL$
|
|
532
|
-
const LLM_STREAM_TOOL_RESULT$
|
|
533
|
-
const LLM_STREAM_COMPLETE$
|
|
534
|
-
const LLM_STREAM_ERROR$
|
|
535
|
+
const LLM_STREAM_DELTA$3 = "llm-stream-delta";
|
|
536
|
+
const LLM_STREAM_TOOL_CALL$3 = "llm-stream-tool-call";
|
|
537
|
+
const LLM_STREAM_TOOL_RESULT$3 = "llm-stream-tool-result";
|
|
538
|
+
const LLM_STREAM_COMPLETE$3 = "llm-stream-complete";
|
|
539
|
+
const LLM_STREAM_ERROR$3 = "llm-stream-error";
|
|
535
540
|
|
|
536
541
|
var llmEvents$1 = {
|
|
537
542
|
LLM_SEND_MESSAGE: LLM_SEND_MESSAGE$1,
|
|
538
543
|
LLM_ABORT_REQUEST: LLM_ABORT_REQUEST$1,
|
|
539
544
|
LLM_LIST_CONNECTED_TOOLS: LLM_LIST_CONNECTED_TOOLS$1,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
+
LLM_CHECK_CLI_AVAILABLE: LLM_CHECK_CLI_AVAILABLE$1,
|
|
546
|
+
LLM_CLEAR_CLI_SESSION: LLM_CLEAR_CLI_SESSION$1,
|
|
547
|
+
LLM_STREAM_DELTA: LLM_STREAM_DELTA$3,
|
|
548
|
+
LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$3,
|
|
549
|
+
LLM_STREAM_TOOL_RESULT: LLM_STREAM_TOOL_RESULT$3,
|
|
550
|
+
LLM_STREAM_COMPLETE: LLM_STREAM_COMPLETE$3,
|
|
551
|
+
LLM_STREAM_ERROR: LLM_STREAM_ERROR$3,
|
|
552
|
+
LLM_CLI_SESSION_STATUS: LLM_CLI_SESSION_STATUS$1,
|
|
553
|
+
LLM_CLI_END_SESSION: LLM_CLI_END_SESSION$1,
|
|
545
554
|
};
|
|
546
555
|
|
|
547
556
|
/**
|
|
@@ -4809,28 +4818,28 @@ const fs$5 = require$$2;
|
|
|
4809
4818
|
/**
|
|
4810
4819
|
* Cached shell PATH result (resolved once, reused for all spawns).
|
|
4811
4820
|
*/
|
|
4812
|
-
let _shellPath = null;
|
|
4821
|
+
let _shellPath$1 = null;
|
|
4813
4822
|
|
|
4814
4823
|
/**
|
|
4815
4824
|
* Get the user's full shell PATH (including nvm, homebrew, volta, etc.).
|
|
4816
4825
|
* Electron GUI apps on macOS don't inherit the shell PATH, so we
|
|
4817
4826
|
* resolve it once by invoking a login shell.
|
|
4818
4827
|
*/
|
|
4819
|
-
function getShellPath() {
|
|
4820
|
-
if (_shellPath !== null) return _shellPath;
|
|
4828
|
+
function getShellPath$1() {
|
|
4829
|
+
if (_shellPath$1 !== null) return _shellPath$1;
|
|
4821
4830
|
|
|
4822
4831
|
try {
|
|
4823
4832
|
const { execSync } = require("child_process");
|
|
4824
4833
|
const shell = process.env.SHELL || "/bin/bash";
|
|
4825
|
-
_shellPath = execSync(`${shell} -ilc 'echo -n "$PATH"'`, {
|
|
4834
|
+
_shellPath$1 = execSync(`${shell} -ilc 'echo -n "$PATH"'`, {
|
|
4826
4835
|
encoding: "utf8",
|
|
4827
4836
|
timeout: 5000,
|
|
4828
4837
|
});
|
|
4829
4838
|
} catch {
|
|
4830
|
-
_shellPath = process.env.PATH || "";
|
|
4839
|
+
_shellPath$1 = process.env.PATH || "";
|
|
4831
4840
|
}
|
|
4832
4841
|
|
|
4833
|
-
return _shellPath;
|
|
4842
|
+
return _shellPath$1;
|
|
4834
4843
|
}
|
|
4835
4844
|
|
|
4836
4845
|
/**
|
|
@@ -4948,7 +4957,7 @@ const mcpController$2 = {
|
|
|
4948
4957
|
const env = { ...process.env };
|
|
4949
4958
|
// Ensure full shell PATH is available (Electron GUI apps
|
|
4950
4959
|
// on macOS don't inherit nvm/homebrew paths)
|
|
4951
|
-
env.PATH = getShellPath();
|
|
4960
|
+
env.PATH = getShellPath$1();
|
|
4952
4961
|
if (mcpConfig.envMapping && credentials) {
|
|
4953
4962
|
Object.entries(mcpConfig.envMapping).forEach(
|
|
4954
4963
|
([envVar, credentialKey]) => {
|
|
@@ -6317,6 +6326,438 @@ const pluginController$1 = {
|
|
|
6317
6326
|
|
|
6318
6327
|
var pluginController_1 = pluginController$1;
|
|
6319
6328
|
|
|
6329
|
+
/**
|
|
6330
|
+
* cliController.js
|
|
6331
|
+
*
|
|
6332
|
+
* Manages Claude Code CLI (`claude -p`) as an alternative LLM backend.
|
|
6333
|
+
* Spawns the CLI subprocess, parses stream-json NDJSON output, and emits
|
|
6334
|
+
* the same LLM_STREAM_* events as the Anthropic SDK path.
|
|
6335
|
+
*
|
|
6336
|
+
* Users with a Claude Pro/Max subscription and Claude Code installed
|
|
6337
|
+
* can use the Chat widget without a separate API key.
|
|
6338
|
+
*/
|
|
6339
|
+
|
|
6340
|
+
const { spawn, execSync } = require$$0$4;
|
|
6341
|
+
const {
|
|
6342
|
+
LLM_STREAM_DELTA: LLM_STREAM_DELTA$2,
|
|
6343
|
+
LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$2,
|
|
6344
|
+
LLM_STREAM_TOOL_RESULT: LLM_STREAM_TOOL_RESULT$2,
|
|
6345
|
+
LLM_STREAM_COMPLETE: LLM_STREAM_COMPLETE$2,
|
|
6346
|
+
LLM_STREAM_ERROR: LLM_STREAM_ERROR$2,
|
|
6347
|
+
} = llmEvents$1;
|
|
6348
|
+
|
|
6349
|
+
/**
|
|
6350
|
+
* Cached shell PATH result (resolved once, reused for all spawns).
|
|
6351
|
+
* Same pattern as mcpController.js.
|
|
6352
|
+
*/
|
|
6353
|
+
let _shellPath = null;
|
|
6354
|
+
|
|
6355
|
+
function getShellPath() {
|
|
6356
|
+
if (_shellPath !== null) return _shellPath;
|
|
6357
|
+
|
|
6358
|
+
try {
|
|
6359
|
+
const shell = process.env.SHELL || "/bin/bash";
|
|
6360
|
+
_shellPath = execSync(`${shell} -ilc 'echo -n "$PATH"'`, {
|
|
6361
|
+
encoding: "utf8",
|
|
6362
|
+
timeout: 5000,
|
|
6363
|
+
});
|
|
6364
|
+
} catch {
|
|
6365
|
+
_shellPath = process.env.PATH || "";
|
|
6366
|
+
}
|
|
6367
|
+
|
|
6368
|
+
return _shellPath;
|
|
6369
|
+
}
|
|
6370
|
+
|
|
6371
|
+
/**
|
|
6372
|
+
* Cached CLI binary path (resolved once via `which claude`).
|
|
6373
|
+
*/
|
|
6374
|
+
let _cliBinaryPath = undefined; // undefined = not yet checked
|
|
6375
|
+
|
|
6376
|
+
function resolveCliBinary() {
|
|
6377
|
+
if (_cliBinaryPath !== undefined) return _cliBinaryPath;
|
|
6378
|
+
|
|
6379
|
+
try {
|
|
6380
|
+
const fullPath = getShellPath();
|
|
6381
|
+
_cliBinaryPath = execSync("which claude", {
|
|
6382
|
+
encoding: "utf8",
|
|
6383
|
+
timeout: 5000,
|
|
6384
|
+
env: { ...process.env, PATH: fullPath },
|
|
6385
|
+
}).trim();
|
|
6386
|
+
} catch {
|
|
6387
|
+
_cliBinaryPath = null;
|
|
6388
|
+
}
|
|
6389
|
+
|
|
6390
|
+
return _cliBinaryPath;
|
|
6391
|
+
}
|
|
6392
|
+
|
|
6393
|
+
/**
|
|
6394
|
+
* Active CLI processes for abort support.
|
|
6395
|
+
* Map<requestId, ChildProcess>
|
|
6396
|
+
*/
|
|
6397
|
+
const activeProcesses = new Map();
|
|
6398
|
+
|
|
6399
|
+
/**
|
|
6400
|
+
* Session IDs for conversation continuity.
|
|
6401
|
+
* Map<widgetUuid, sessionId>
|
|
6402
|
+
*/
|
|
6403
|
+
const sessions = new Map();
|
|
6404
|
+
|
|
6405
|
+
/**
|
|
6406
|
+
* Send events safely to a window.
|
|
6407
|
+
*/
|
|
6408
|
+
function safeSend(win, channel, data) {
|
|
6409
|
+
if (win && !win.isDestroyed()) {
|
|
6410
|
+
win.webContents.send(channel, data);
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
|
|
6414
|
+
const cliController$2 = {
|
|
6415
|
+
/**
|
|
6416
|
+
* isAvailable
|
|
6417
|
+
* Check if the Claude Code CLI is installed and accessible.
|
|
6418
|
+
*
|
|
6419
|
+
* @returns {{ available: boolean, path?: string }}
|
|
6420
|
+
*/
|
|
6421
|
+
isAvailable: () => {
|
|
6422
|
+
const binaryPath = resolveCliBinary();
|
|
6423
|
+
if (binaryPath) {
|
|
6424
|
+
return { available: true, path: binaryPath };
|
|
6425
|
+
}
|
|
6426
|
+
return { available: false };
|
|
6427
|
+
},
|
|
6428
|
+
|
|
6429
|
+
/**
|
|
6430
|
+
* sendMessage
|
|
6431
|
+
* Stream a response from the Claude Code CLI with NDJSON parsing.
|
|
6432
|
+
*
|
|
6433
|
+
* @param {BrowserWindow} win - the window to send stream events to
|
|
6434
|
+
* @param {string} requestId - unique ID for this request
|
|
6435
|
+
* @param {object} params - { model, messages, systemPrompt, maxToolRounds, widgetUuid }
|
|
6436
|
+
*/
|
|
6437
|
+
sendMessage: async (win, requestId, params) => {
|
|
6438
|
+
const { model, messages, systemPrompt, widgetUuid } = params;
|
|
6439
|
+
|
|
6440
|
+
const binaryPath = resolveCliBinary();
|
|
6441
|
+
if (!binaryPath) {
|
|
6442
|
+
safeSend(win, LLM_STREAM_ERROR$2, {
|
|
6443
|
+
requestId,
|
|
6444
|
+
error:
|
|
6445
|
+
"Claude Code CLI not found. Install from https://claude.ai/download",
|
|
6446
|
+
code: "CLI_NOT_FOUND",
|
|
6447
|
+
});
|
|
6448
|
+
return;
|
|
6449
|
+
}
|
|
6450
|
+
|
|
6451
|
+
// Build CLI args
|
|
6452
|
+
const args = ["-p", "--output-format", "stream-json", "--verbose"];
|
|
6453
|
+
|
|
6454
|
+
if (model) {
|
|
6455
|
+
args.push("--model", model);
|
|
6456
|
+
}
|
|
6457
|
+
|
|
6458
|
+
if (systemPrompt) {
|
|
6459
|
+
args.push("--append-system-prompt", systemPrompt);
|
|
6460
|
+
}
|
|
6461
|
+
|
|
6462
|
+
// Resume existing session for conversation continuity
|
|
6463
|
+
const sessionId = widgetUuid ? sessions.get(widgetUuid) : null;
|
|
6464
|
+
if (sessionId) {
|
|
6465
|
+
args.push("--resume", sessionId);
|
|
6466
|
+
}
|
|
6467
|
+
|
|
6468
|
+
// Extract the user message (last user message in the array)
|
|
6469
|
+
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
6470
|
+
const userText =
|
|
6471
|
+
typeof lastUserMsg?.content === "string"
|
|
6472
|
+
? lastUserMsg.content
|
|
6473
|
+
: Array.isArray(lastUserMsg?.content)
|
|
6474
|
+
? lastUserMsg.content
|
|
6475
|
+
.filter((b) => b.type === "text")
|
|
6476
|
+
.map((b) => b.text)
|
|
6477
|
+
.join("\n")
|
|
6478
|
+
: "";
|
|
6479
|
+
|
|
6480
|
+
if (!userText) {
|
|
6481
|
+
safeSend(win, LLM_STREAM_ERROR$2, {
|
|
6482
|
+
requestId,
|
|
6483
|
+
error: "No user message to send.",
|
|
6484
|
+
code: "CLI_ERROR",
|
|
6485
|
+
});
|
|
6486
|
+
return;
|
|
6487
|
+
}
|
|
6488
|
+
|
|
6489
|
+
try {
|
|
6490
|
+
const fullPath = getShellPath();
|
|
6491
|
+
const child = spawn(binaryPath, args, {
|
|
6492
|
+
env: { ...process.env, PATH: fullPath },
|
|
6493
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
6494
|
+
});
|
|
6495
|
+
|
|
6496
|
+
activeProcesses.set(requestId, child);
|
|
6497
|
+
|
|
6498
|
+
// Pipe user message via stdin (not visible in ps)
|
|
6499
|
+
child.stdin.write(userText);
|
|
6500
|
+
child.stdin.end();
|
|
6501
|
+
|
|
6502
|
+
let stdoutBuffer = "";
|
|
6503
|
+
let stderrBuffer = "";
|
|
6504
|
+
let capturedSessionId = null;
|
|
6505
|
+
let retried = false;
|
|
6506
|
+
|
|
6507
|
+
// Track active tool calls for mapping results
|
|
6508
|
+
const activeToolCalls = new Map();
|
|
6509
|
+
|
|
6510
|
+
child.stdout.on("data", (chunk) => {
|
|
6511
|
+
stdoutBuffer += chunk.toString();
|
|
6512
|
+
|
|
6513
|
+
// Process complete lines
|
|
6514
|
+
const lines = stdoutBuffer.split("\n");
|
|
6515
|
+
stdoutBuffer = lines.pop(); // keep incomplete line in buffer
|
|
6516
|
+
|
|
6517
|
+
for (const line of lines) {
|
|
6518
|
+
if (!line.trim()) continue;
|
|
6519
|
+
|
|
6520
|
+
let parsed;
|
|
6521
|
+
try {
|
|
6522
|
+
parsed = JSON.parse(line);
|
|
6523
|
+
} catch {
|
|
6524
|
+
console.warn("[cliController] Skipping invalid JSON line:", line);
|
|
6525
|
+
continue;
|
|
6526
|
+
}
|
|
6527
|
+
|
|
6528
|
+
// Capture session ID from any message that has it
|
|
6529
|
+
if (parsed.session_id && widgetUuid) {
|
|
6530
|
+
capturedSessionId = parsed.session_id;
|
|
6531
|
+
sessions.set(widgetUuid, capturedSessionId);
|
|
6532
|
+
}
|
|
6533
|
+
|
|
6534
|
+
// Map CLI stream-json events to IPC events
|
|
6535
|
+
if (parsed.type === "content_block_delta") {
|
|
6536
|
+
if (parsed.delta?.type === "text_delta" && parsed.delta.text) {
|
|
6537
|
+
safeSend(win, LLM_STREAM_DELTA$2, {
|
|
6538
|
+
requestId,
|
|
6539
|
+
text: parsed.delta.text,
|
|
6540
|
+
});
|
|
6541
|
+
} else if (parsed.delta?.type === "input_json_delta") {
|
|
6542
|
+
// Update tool input incrementally
|
|
6543
|
+
const tc = activeToolCalls.get(parsed.index);
|
|
6544
|
+
if (tc) {
|
|
6545
|
+
tc.partialInput =
|
|
6546
|
+
(tc.partialInput || "") + (parsed.delta.partial_json || "");
|
|
6547
|
+
}
|
|
6548
|
+
}
|
|
6549
|
+
} else if (parsed.type === "content_block_start") {
|
|
6550
|
+
if (parsed.content_block?.type === "tool_use") {
|
|
6551
|
+
const toolBlock = parsed.content_block;
|
|
6552
|
+
activeToolCalls.set(parsed.index, {
|
|
6553
|
+
toolUseId: toolBlock.id,
|
|
6554
|
+
toolName: toolBlock.name,
|
|
6555
|
+
partialInput: "",
|
|
6556
|
+
});
|
|
6557
|
+
safeSend(win, LLM_STREAM_TOOL_CALL$2, {
|
|
6558
|
+
requestId,
|
|
6559
|
+
toolUseId: toolBlock.id,
|
|
6560
|
+
toolName: toolBlock.name,
|
|
6561
|
+
serverName: "Claude Code",
|
|
6562
|
+
input: toolBlock.input || {},
|
|
6563
|
+
});
|
|
6564
|
+
}
|
|
6565
|
+
} else if (parsed.type === "content_block_stop") {
|
|
6566
|
+
// Tool call completed — try to parse the accumulated input
|
|
6567
|
+
const tc = activeToolCalls.get(parsed.index);
|
|
6568
|
+
if (tc && tc.partialInput) {
|
|
6569
|
+
try {
|
|
6570
|
+
tc.finalInput = JSON.parse(tc.partialInput);
|
|
6571
|
+
} catch {
|
|
6572
|
+
tc.finalInput = tc.partialInput;
|
|
6573
|
+
}
|
|
6574
|
+
}
|
|
6575
|
+
} else if (parsed.type === "message_stop") {
|
|
6576
|
+
// Individual message completed (may be followed by more in tool-use loops)
|
|
6577
|
+
} else if (parsed.type === "result") {
|
|
6578
|
+
// Final result — conversation complete
|
|
6579
|
+
const content = [];
|
|
6580
|
+
if (parsed.result) {
|
|
6581
|
+
content.push({ type: "text", text: parsed.result });
|
|
6582
|
+
}
|
|
6583
|
+
|
|
6584
|
+
safeSend(win, LLM_STREAM_COMPLETE$2, {
|
|
6585
|
+
requestId,
|
|
6586
|
+
content,
|
|
6587
|
+
stopReason: parsed.stop_reason || "end_turn",
|
|
6588
|
+
usage: parsed.usage || {},
|
|
6589
|
+
});
|
|
6590
|
+
}
|
|
6591
|
+
}
|
|
6592
|
+
});
|
|
6593
|
+
|
|
6594
|
+
child.stderr.on("data", (chunk) => {
|
|
6595
|
+
stderrBuffer += chunk.toString();
|
|
6596
|
+
});
|
|
6597
|
+
|
|
6598
|
+
child.on("error", (err) => {
|
|
6599
|
+
activeProcesses.delete(requestId);
|
|
6600
|
+
safeSend(win, LLM_STREAM_ERROR$2, {
|
|
6601
|
+
requestId,
|
|
6602
|
+
error: `Failed to start Claude CLI: ${err.message}`,
|
|
6603
|
+
code: "CLI_SPAWN_ERROR",
|
|
6604
|
+
});
|
|
6605
|
+
});
|
|
6606
|
+
|
|
6607
|
+
child.on("close", (code) => {
|
|
6608
|
+
activeProcesses.delete(requestId);
|
|
6609
|
+
|
|
6610
|
+
// Process any remaining buffer
|
|
6611
|
+
if (stdoutBuffer.trim()) {
|
|
6612
|
+
try {
|
|
6613
|
+
const parsed = JSON.parse(stdoutBuffer);
|
|
6614
|
+
if (parsed.session_id && widgetUuid) {
|
|
6615
|
+
sessions.set(widgetUuid, parsed.session_id);
|
|
6616
|
+
}
|
|
6617
|
+
if (parsed.type === "result") {
|
|
6618
|
+
const content = [];
|
|
6619
|
+
if (parsed.result) {
|
|
6620
|
+
content.push({ type: "text", text: parsed.result });
|
|
6621
|
+
}
|
|
6622
|
+
safeSend(win, LLM_STREAM_COMPLETE$2, {
|
|
6623
|
+
requestId,
|
|
6624
|
+
content,
|
|
6625
|
+
stopReason: parsed.stop_reason || "end_turn",
|
|
6626
|
+
usage: parsed.usage || {},
|
|
6627
|
+
});
|
|
6628
|
+
return;
|
|
6629
|
+
}
|
|
6630
|
+
} catch {
|
|
6631
|
+
// ignore
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
|
|
6635
|
+
if (code !== 0 && code !== null) {
|
|
6636
|
+
// Check if resume failed and retry without it
|
|
6637
|
+
if (sessionId && !retried && stderrBuffer.includes("session")) {
|
|
6638
|
+
retried = true;
|
|
6639
|
+
if (widgetUuid) sessions.delete(widgetUuid);
|
|
6640
|
+
// Retry without --resume
|
|
6641
|
+
cliController$2.sendMessage(win, requestId, {
|
|
6642
|
+
...params,
|
|
6643
|
+
_retryWithoutResume: true,
|
|
6644
|
+
});
|
|
6645
|
+
return;
|
|
6646
|
+
}
|
|
6647
|
+
|
|
6648
|
+
// Check for auth errors
|
|
6649
|
+
if (
|
|
6650
|
+
stderrBuffer.includes("auth") ||
|
|
6651
|
+
stderrBuffer.includes("login") ||
|
|
6652
|
+
stderrBuffer.includes("not authenticated")
|
|
6653
|
+
) {
|
|
6654
|
+
safeSend(win, LLM_STREAM_ERROR$2, {
|
|
6655
|
+
requestId,
|
|
6656
|
+
error:
|
|
6657
|
+
"Claude Code CLI is not authenticated. Run `claude auth login` in your terminal.",
|
|
6658
|
+
code: "CLI_AUTH_ERROR",
|
|
6659
|
+
});
|
|
6660
|
+
return;
|
|
6661
|
+
}
|
|
6662
|
+
|
|
6663
|
+
safeSend(win, LLM_STREAM_ERROR$2, {
|
|
6664
|
+
requestId,
|
|
6665
|
+
error: `Claude CLI exited with code ${code}${stderrBuffer ? ": " + stderrBuffer.slice(0, 500) : ""}`,
|
|
6666
|
+
code: "CLI_ERROR",
|
|
6667
|
+
});
|
|
6668
|
+
}
|
|
6669
|
+
});
|
|
6670
|
+
} catch (err) {
|
|
6671
|
+
activeProcesses.delete(requestId);
|
|
6672
|
+
safeSend(win, LLM_STREAM_ERROR$2, {
|
|
6673
|
+
requestId,
|
|
6674
|
+
error: `Failed to start Claude CLI: ${err.message}`,
|
|
6675
|
+
code: "CLI_SPAWN_ERROR",
|
|
6676
|
+
});
|
|
6677
|
+
}
|
|
6678
|
+
},
|
|
6679
|
+
|
|
6680
|
+
/**
|
|
6681
|
+
* abortRequest
|
|
6682
|
+
* Kill an in-flight CLI process.
|
|
6683
|
+
*
|
|
6684
|
+
* @param {string} requestId - the request to cancel
|
|
6685
|
+
* @returns {{ success: boolean }}
|
|
6686
|
+
*/
|
|
6687
|
+
abortRequest: (requestId) => {
|
|
6688
|
+
const child = activeProcesses.get(requestId);
|
|
6689
|
+
if (child) {
|
|
6690
|
+
child.kill("SIGTERM");
|
|
6691
|
+
activeProcesses.delete(requestId);
|
|
6692
|
+
return { success: true };
|
|
6693
|
+
}
|
|
6694
|
+
return { success: false, message: "Request not found" };
|
|
6695
|
+
},
|
|
6696
|
+
|
|
6697
|
+
/**
|
|
6698
|
+
* clearSession
|
|
6699
|
+
* Remove the stored session ID for a widget (called on "New Chat").
|
|
6700
|
+
*
|
|
6701
|
+
* @param {string} widgetUuid - the widget whose session to clear
|
|
6702
|
+
* @returns {{ success: boolean }}
|
|
6703
|
+
*/
|
|
6704
|
+
clearSession: (widgetUuid) => {
|
|
6705
|
+
if (widgetUuid && sessions.has(widgetUuid)) {
|
|
6706
|
+
sessions.delete(widgetUuid);
|
|
6707
|
+
return { success: true };
|
|
6708
|
+
}
|
|
6709
|
+
return { success: false };
|
|
6710
|
+
},
|
|
6711
|
+
|
|
6712
|
+
/**
|
|
6713
|
+
* getSessionStatus
|
|
6714
|
+
* Check if a CLI session exists and whether a process is active for a widget.
|
|
6715
|
+
*
|
|
6716
|
+
* @param {string} widgetUuid - the widget to check
|
|
6717
|
+
* @returns {{ hasSession: boolean, sessionId?: string, isProcessActive: boolean }}
|
|
6718
|
+
*/
|
|
6719
|
+
getSessionStatus: (widgetUuid) => {
|
|
6720
|
+
const sessionId = widgetUuid ? sessions.get(widgetUuid) : null;
|
|
6721
|
+
// Check if any active process belongs to this widget
|
|
6722
|
+
let isProcessActive = false;
|
|
6723
|
+
for (const [, child] of activeProcesses) {
|
|
6724
|
+
if (!child.killed) {
|
|
6725
|
+
isProcessActive = true;
|
|
6726
|
+
break;
|
|
6727
|
+
}
|
|
6728
|
+
}
|
|
6729
|
+
return {
|
|
6730
|
+
hasSession: !!sessionId,
|
|
6731
|
+
sessionId: sessionId || undefined,
|
|
6732
|
+
isProcessActive,
|
|
6733
|
+
};
|
|
6734
|
+
},
|
|
6735
|
+
|
|
6736
|
+
/**
|
|
6737
|
+
* endSession
|
|
6738
|
+
* Kill any active CLI process AND clear the session for a widget.
|
|
6739
|
+
*
|
|
6740
|
+
* @param {string} widgetUuid - the widget whose session to end
|
|
6741
|
+
* @returns {{ success: boolean }}
|
|
6742
|
+
*/
|
|
6743
|
+
endSession: (widgetUuid) => {
|
|
6744
|
+
// Kill any active processes for this widget
|
|
6745
|
+
for (const [reqId, child] of activeProcesses) {
|
|
6746
|
+
if (reqId.startsWith(widgetUuid)) {
|
|
6747
|
+
child.kill("SIGTERM");
|
|
6748
|
+
activeProcesses.delete(reqId);
|
|
6749
|
+
}
|
|
6750
|
+
}
|
|
6751
|
+
// Clear the session
|
|
6752
|
+
if (widgetUuid && sessions.has(widgetUuid)) {
|
|
6753
|
+
sessions.delete(widgetUuid);
|
|
6754
|
+
}
|
|
6755
|
+
return { success: true };
|
|
6756
|
+
},
|
|
6757
|
+
};
|
|
6758
|
+
|
|
6759
|
+
var cliController_1 = cliController$2;
|
|
6760
|
+
|
|
6320
6761
|
/**
|
|
6321
6762
|
* llmController.js
|
|
6322
6763
|
*
|
|
@@ -6328,8 +6769,9 @@ var pluginController_1 = pluginController$1;
|
|
|
6328
6769
|
* per-request, receiving the full messages array each time.
|
|
6329
6770
|
*/
|
|
6330
6771
|
|
|
6331
|
-
const Anthropic = require$$0$
|
|
6772
|
+
const Anthropic = require$$0$5;
|
|
6332
6773
|
const mcpController$1 = mcpController_1;
|
|
6774
|
+
const cliController$1 = cliController_1;
|
|
6333
6775
|
const {
|
|
6334
6776
|
LLM_STREAM_DELTA: LLM_STREAM_DELTA$1,
|
|
6335
6777
|
LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$1,
|
|
@@ -6398,6 +6840,11 @@ const llmController$1 = {
|
|
|
6398
6840
|
* @param {object} params - { apiKey, model, messages, tools, toolServerMap, systemPrompt, maxToolRounds }
|
|
6399
6841
|
*/
|
|
6400
6842
|
sendMessage: async (win, requestId, params) => {
|
|
6843
|
+
// Route to CLI backend if specified
|
|
6844
|
+
if (params.backend === "claude-code") {
|
|
6845
|
+
return cliController$1.sendMessage(win, requestId, params);
|
|
6846
|
+
}
|
|
6847
|
+
|
|
6401
6848
|
const {
|
|
6402
6849
|
apiKey,
|
|
6403
6850
|
model = "claude-sonnet-4-20250514",
|
|
@@ -6621,7 +7068,8 @@ const llmController$1 = {
|
|
|
6621
7068
|
activeRequests.delete(requestId);
|
|
6622
7069
|
return { success: true };
|
|
6623
7070
|
}
|
|
6624
|
-
|
|
7071
|
+
// Fallback to CLI controller
|
|
7072
|
+
return cliController$1.abortRequest(requestId);
|
|
6625
7073
|
},
|
|
6626
7074
|
};
|
|
6627
7075
|
|
|
@@ -7889,6 +8337,10 @@ const {
|
|
|
7889
8337
|
LLM_SEND_MESSAGE,
|
|
7890
8338
|
LLM_ABORT_REQUEST,
|
|
7891
8339
|
LLM_LIST_CONNECTED_TOOLS,
|
|
8340
|
+
LLM_CHECK_CLI_AVAILABLE,
|
|
8341
|
+
LLM_CLEAR_CLI_SESSION,
|
|
8342
|
+
LLM_CLI_SESSION_STATUS,
|
|
8343
|
+
LLM_CLI_END_SESSION,
|
|
7892
8344
|
LLM_STREAM_DELTA,
|
|
7893
8345
|
LLM_STREAM_TOOL_CALL,
|
|
7894
8346
|
LLM_STREAM_TOOL_RESULT,
|
|
@@ -7926,51 +8378,129 @@ const llmApi$2 = {
|
|
|
7926
8378
|
*/
|
|
7927
8379
|
listConnectedTools: () => ipcRenderer$2.invoke(LLM_LIST_CONNECTED_TOOLS),
|
|
7928
8380
|
|
|
8381
|
+
/**
|
|
8382
|
+
* checkCliAvailable
|
|
8383
|
+
* Check if the Claude Code CLI is installed and accessible.
|
|
8384
|
+
*
|
|
8385
|
+
* @returns {Promise<{ available: boolean, path?: string }>}
|
|
8386
|
+
*/
|
|
8387
|
+
checkCliAvailable: () => ipcRenderer$2.invoke(LLM_CHECK_CLI_AVAILABLE),
|
|
8388
|
+
|
|
8389
|
+
/**
|
|
8390
|
+
* clearCliSession
|
|
8391
|
+
* Clear the CLI conversation session for a widget (for "New Chat").
|
|
8392
|
+
*
|
|
8393
|
+
* @param {string} widgetUuid - the widget whose session to clear
|
|
8394
|
+
* @returns {Promise<{ success: boolean }>}
|
|
8395
|
+
*/
|
|
8396
|
+
clearCliSession: (widgetUuid) =>
|
|
8397
|
+
ipcRenderer$2.invoke(LLM_CLEAR_CLI_SESSION, { widgetUuid }),
|
|
8398
|
+
|
|
8399
|
+
/**
|
|
8400
|
+
* getCliSessionStatus
|
|
8401
|
+
* Check if a CLI session is active for a widget.
|
|
8402
|
+
*
|
|
8403
|
+
* @param {string} widgetUuid - the widget to check
|
|
8404
|
+
* @returns {Promise<{ hasSession: boolean, sessionId?: string, isProcessActive: boolean }>}
|
|
8405
|
+
*/
|
|
8406
|
+
getCliSessionStatus: (widgetUuid) =>
|
|
8407
|
+
ipcRenderer$2.invoke(LLM_CLI_SESSION_STATUS, { widgetUuid }),
|
|
8408
|
+
|
|
8409
|
+
/**
|
|
8410
|
+
* endCliSession
|
|
8411
|
+
* Kill any active CLI process AND clear the session for a widget.
|
|
8412
|
+
*
|
|
8413
|
+
* @param {string} widgetUuid - the widget whose session to end
|
|
8414
|
+
* @returns {Promise<{ success: boolean }>}
|
|
8415
|
+
*/
|
|
8416
|
+
endCliSession: (widgetUuid) =>
|
|
8417
|
+
ipcRenderer$2.invoke(LLM_CLI_END_SESSION, { widgetUuid }),
|
|
8418
|
+
|
|
7929
8419
|
// --- Stream event listeners ---
|
|
8420
|
+
// Each on* method returns the wrapped callback so callers can remove
|
|
8421
|
+
// their own listener without nuking listeners from other widgets.
|
|
7930
8422
|
|
|
7931
8423
|
/**
|
|
7932
8424
|
* onStreamDelta
|
|
7933
8425
|
* Listen for text chunks as they stream in.
|
|
8426
|
+
* @returns {Function} wrapped callback for use with removeStreamListener
|
|
7934
8427
|
*/
|
|
7935
8428
|
onStreamDelta: (callback) => {
|
|
7936
|
-
|
|
8429
|
+
const wrapped = (_event, data) => callback(data);
|
|
8430
|
+
ipcRenderer$2.on(LLM_STREAM_DELTA, wrapped);
|
|
8431
|
+
return wrapped;
|
|
7937
8432
|
},
|
|
7938
8433
|
|
|
7939
8434
|
/**
|
|
7940
8435
|
* onStreamToolCall
|
|
7941
8436
|
* Listen for tool call notifications.
|
|
8437
|
+
* @returns {Function} wrapped callback for use with removeStreamListener
|
|
7942
8438
|
*/
|
|
7943
8439
|
onStreamToolCall: (callback) => {
|
|
7944
|
-
|
|
8440
|
+
const wrapped = (_event, data) => callback(data);
|
|
8441
|
+
ipcRenderer$2.on(LLM_STREAM_TOOL_CALL, wrapped);
|
|
8442
|
+
return wrapped;
|
|
7945
8443
|
},
|
|
7946
8444
|
|
|
7947
8445
|
/**
|
|
7948
8446
|
* onStreamToolResult
|
|
7949
8447
|
* Listen for tool result notifications.
|
|
8448
|
+
* @returns {Function} wrapped callback for use with removeStreamListener
|
|
7950
8449
|
*/
|
|
7951
8450
|
onStreamToolResult: (callback) => {
|
|
7952
|
-
|
|
8451
|
+
const wrapped = (_event, data) => callback(data);
|
|
8452
|
+
ipcRenderer$2.on(LLM_STREAM_TOOL_RESULT, wrapped);
|
|
8453
|
+
return wrapped;
|
|
7953
8454
|
},
|
|
7954
8455
|
|
|
7955
8456
|
/**
|
|
7956
8457
|
* onStreamComplete
|
|
7957
8458
|
* Listen for stream completion (final response).
|
|
8459
|
+
* @returns {Function} wrapped callback for use with removeStreamListener
|
|
7958
8460
|
*/
|
|
7959
8461
|
onStreamComplete: (callback) => {
|
|
7960
|
-
|
|
8462
|
+
const wrapped = (_event, data) => callback(data);
|
|
8463
|
+
ipcRenderer$2.on(LLM_STREAM_COMPLETE, wrapped);
|
|
8464
|
+
return wrapped;
|
|
7961
8465
|
},
|
|
7962
8466
|
|
|
7963
8467
|
/**
|
|
7964
8468
|
* onStreamError
|
|
7965
8469
|
* Listen for stream errors.
|
|
8470
|
+
* @returns {Function} wrapped callback for use with removeStreamListener
|
|
7966
8471
|
*/
|
|
7967
8472
|
onStreamError: (callback) => {
|
|
7968
|
-
|
|
8473
|
+
const wrapped = (_event, data) => callback(data);
|
|
8474
|
+
ipcRenderer$2.on(LLM_STREAM_ERROR, wrapped);
|
|
8475
|
+
return wrapped;
|
|
8476
|
+
},
|
|
8477
|
+
|
|
8478
|
+
/**
|
|
8479
|
+
* removeStreamListener
|
|
8480
|
+
* Remove a specific stream listener by channel and callback reference.
|
|
8481
|
+
*
|
|
8482
|
+
* @param {string} channel - the IPC channel name
|
|
8483
|
+
* @param {Function} wrapped - the callback returned by on*
|
|
8484
|
+
*/
|
|
8485
|
+
removeStreamListener: (channel, wrapped) => {
|
|
8486
|
+
ipcRenderer$2.removeListener(channel, wrapped);
|
|
8487
|
+
},
|
|
8488
|
+
|
|
8489
|
+
/**
|
|
8490
|
+
* Stream channel constants for use with removeStreamListener.
|
|
8491
|
+
*/
|
|
8492
|
+
streamChannels: {
|
|
8493
|
+
delta: LLM_STREAM_DELTA,
|
|
8494
|
+
toolCall: LLM_STREAM_TOOL_CALL,
|
|
8495
|
+
toolResult: LLM_STREAM_TOOL_RESULT,
|
|
8496
|
+
complete: LLM_STREAM_COMPLETE,
|
|
8497
|
+
error: LLM_STREAM_ERROR,
|
|
7969
8498
|
},
|
|
7970
8499
|
|
|
7971
8500
|
/**
|
|
7972
8501
|
* removeAllStreamListeners
|
|
7973
|
-
* Clean up
|
|
8502
|
+
* Clean up ALL LLM stream listeners (global).
|
|
8503
|
+
* Prefer removeStreamListener for scoped cleanup.
|
|
7974
8504
|
*/
|
|
7975
8505
|
removeAllStreamListeners: () => {
|
|
7976
8506
|
ipcRenderer$2.removeAllListeners(LLM_STREAM_DELTA);
|
|
@@ -9553,6 +10083,7 @@ const openaiController = openaiController_1;
|
|
|
9553
10083
|
const menuItemsController = menuItemsController_1;
|
|
9554
10084
|
const pluginController = pluginController_1;
|
|
9555
10085
|
const llmController = llmController_1;
|
|
10086
|
+
const cliController = cliController_1;
|
|
9556
10087
|
|
|
9557
10088
|
// --- Utils ---
|
|
9558
10089
|
const clientCache = requireClientCache();
|
|
@@ -9609,6 +10140,7 @@ var electron = {
|
|
|
9609
10140
|
menuItemsController,
|
|
9610
10141
|
pluginController,
|
|
9611
10142
|
llmController,
|
|
10143
|
+
cliController,
|
|
9612
10144
|
|
|
9613
10145
|
// Controller functions (flat) — spread for convenient destructuring
|
|
9614
10146
|
...controllers,
|