@trops/dash-core 0.1.47 → 0.1.49

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.
@@ -22,6 +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$5 = require('@anthropic-ai/sdk');
26
+ var require$$0$4 = require('child_process');
25
27
  var require$$2$6 = require('os');
26
28
  var require$$3$3 = require('adm-zip');
27
29
  var require$$4$1 = require('url');
@@ -513,6 +515,40 @@ var openaiEvents$1 = {
513
515
  OPENAI_DESCRIBE_IMAGE_ERROR,
514
516
  };
515
517
 
518
+ /**
519
+ * Event Constants File - LLM Events
520
+ *
521
+ * This file contains event constants for LLM (Large Language Model)
522
+ * chat IPC communication — streaming, tool-use, and lifecycle.
523
+ */
524
+
525
+ // --- Renderer → Main (invoke) ---
526
+ const LLM_SEND_MESSAGE$1 = "llm-send-message";
527
+ const LLM_ABORT_REQUEST$1 = "llm-abort-request";
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
+
532
+ // --- Main → Renderer (send) ---
533
+ const LLM_STREAM_DELTA$3 = "llm-stream-delta";
534
+ const LLM_STREAM_TOOL_CALL$3 = "llm-stream-tool-call";
535
+ const LLM_STREAM_TOOL_RESULT$3 = "llm-stream-tool-result";
536
+ const LLM_STREAM_COMPLETE$3 = "llm-stream-complete";
537
+ const LLM_STREAM_ERROR$3 = "llm-stream-error";
538
+
539
+ var llmEvents$1 = {
540
+ LLM_SEND_MESSAGE: LLM_SEND_MESSAGE$1,
541
+ LLM_ABORT_REQUEST: LLM_ABORT_REQUEST$1,
542
+ LLM_LIST_CONNECTED_TOOLS: LLM_LIST_CONNECTED_TOOLS$1,
543
+ LLM_CHECK_CLI_AVAILABLE: LLM_CHECK_CLI_AVAILABLE$1,
544
+ LLM_CLEAR_CLI_SESSION: LLM_CLEAR_CLI_SESSION$1,
545
+ LLM_STREAM_DELTA: LLM_STREAM_DELTA$3,
546
+ LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$3,
547
+ LLM_STREAM_TOOL_RESULT: LLM_STREAM_TOOL_RESULT$3,
548
+ LLM_STREAM_COMPLETE: LLM_STREAM_COMPLETE$3,
549
+ LLM_STREAM_ERROR: LLM_STREAM_ERROR$3,
550
+ };
551
+
516
552
  /**
517
553
  * Event Constants File - Client Cache Events
518
554
  *
@@ -550,6 +586,7 @@ const registryEvents = registryEvents$1;
550
586
  const algoliaEvents = algoliaEvents$1;
551
587
  const menuItemEvents = menuItemEvents$1;
552
588
  const openaiEvents = openaiEvents$1;
589
+ const llmEvents = llmEvents$1;
553
590
  const clientCacheEvents = clientCacheEvents$1;
554
591
 
555
592
  const publicEvents = {
@@ -571,6 +608,7 @@ var events$8 = {
571
608
  ...algoliaEvents,
572
609
  ...menuItemEvents,
573
610
  ...openaiEvents,
611
+ ...llmEvents,
574
612
  ...clientCacheEvents,
575
613
  };
576
614
 
@@ -4776,28 +4814,28 @@ const fs$5 = require$$2;
4776
4814
  /**
4777
4815
  * Cached shell PATH result (resolved once, reused for all spawns).
4778
4816
  */
4779
- let _shellPath = null;
4817
+ let _shellPath$1 = null;
4780
4818
 
4781
4819
  /**
4782
4820
  * Get the user's full shell PATH (including nvm, homebrew, volta, etc.).
4783
4821
  * Electron GUI apps on macOS don't inherit the shell PATH, so we
4784
4822
  * resolve it once by invoking a login shell.
4785
4823
  */
4786
- function getShellPath() {
4787
- if (_shellPath !== null) return _shellPath;
4824
+ function getShellPath$1() {
4825
+ if (_shellPath$1 !== null) return _shellPath$1;
4788
4826
 
4789
4827
  try {
4790
4828
  const { execSync } = require("child_process");
4791
4829
  const shell = process.env.SHELL || "/bin/bash";
4792
- _shellPath = execSync(`${shell} -ilc 'echo -n "$PATH"'`, {
4830
+ _shellPath$1 = execSync(`${shell} -ilc 'echo -n "$PATH"'`, {
4793
4831
  encoding: "utf8",
4794
4832
  timeout: 5000,
4795
4833
  });
4796
4834
  } catch {
4797
- _shellPath = process.env.PATH || "";
4835
+ _shellPath$1 = process.env.PATH || "";
4798
4836
  }
4799
4837
 
4800
- return _shellPath;
4838
+ return _shellPath$1;
4801
4839
  }
4802
4840
 
4803
4841
  /**
@@ -4839,7 +4877,7 @@ function interpolate(template, credentials) {
4839
4877
  });
4840
4878
  }
4841
4879
 
4842
- const mcpController$1 = {
4880
+ const mcpController$2 = {
4843
4881
  /**
4844
4882
  * startServer
4845
4883
  * Start an MCP server with the given config and credentials
@@ -4877,7 +4915,7 @@ const mcpController$1 = {
4877
4915
  try {
4878
4916
  // Stop if in stale/error state
4879
4917
  if (activeServers.has(serverName)) {
4880
- await mcpController$1.stopServer(win, serverName);
4918
+ await mcpController$2.stopServer(win, serverName);
4881
4919
  }
4882
4920
 
4883
4921
  console.log(
@@ -4915,7 +4953,7 @@ const mcpController$1 = {
4915
4953
  const env = { ...process.env };
4916
4954
  // Ensure full shell PATH is available (Electron GUI apps
4917
4955
  // on macOS don't inherit nvm/homebrew paths)
4918
- env.PATH = getShellPath();
4956
+ env.PATH = getShellPath$1();
4919
4957
  if (mcpConfig.envMapping && credentials) {
4920
4958
  Object.entries(mcpConfig.envMapping).forEach(
4921
4959
  ([envVar, credentialKey]) => {
@@ -5313,6 +5351,28 @@ const mcpController$1 = {
5313
5351
  }
5314
5352
  },
5315
5353
 
5354
+ /**
5355
+ * listConnectedServers
5356
+ * Returns all connected servers with their cached tool lists.
5357
+ * Used by llmController to discover available MCP tools.
5358
+ *
5359
+ * @returns {Array<{ serverName, tools, resources, status }>}
5360
+ */
5361
+ listConnectedServers: () => {
5362
+ const servers = [];
5363
+ for (const [serverName, server] of activeServers) {
5364
+ if (server.status === STATUS.CONNECTED) {
5365
+ servers.push({
5366
+ serverName,
5367
+ tools: server.tools || [],
5368
+ resources: server.resources || [],
5369
+ status: server.status,
5370
+ });
5371
+ }
5372
+ }
5373
+ return servers;
5374
+ },
5375
+
5316
5376
  /**
5317
5377
  * stopAllServers
5318
5378
  * Stop all running MCP servers (called on app quit)
@@ -5328,14 +5388,14 @@ const mcpController$1 = {
5328
5388
  );
5329
5389
  const promises = [];
5330
5390
  for (const [serverName] of activeServers) {
5331
- promises.push(mcpController$1.stopServer(null, serverName));
5391
+ promises.push(mcpController$2.stopServer(null, serverName));
5332
5392
  }
5333
5393
  await Promise.allSettled(promises);
5334
5394
  console.log("[mcpController] All servers stopped");
5335
5395
  },
5336
5396
  };
5337
5397
 
5338
- var mcpController_1 = mcpController$1;
5398
+ var mcpController_1 = mcpController$2;
5339
5399
 
5340
5400
  /**
5341
5401
  * registryController.js
@@ -6262,6 +6322,709 @@ const pluginController$1 = {
6262
6322
 
6263
6323
  var pluginController_1 = pluginController$1;
6264
6324
 
6325
+ /**
6326
+ * cliController.js
6327
+ *
6328
+ * Manages Claude Code CLI (`claude -p`) as an alternative LLM backend.
6329
+ * Spawns the CLI subprocess, parses stream-json NDJSON output, and emits
6330
+ * the same LLM_STREAM_* events as the Anthropic SDK path.
6331
+ *
6332
+ * Users with a Claude Pro/Max subscription and Claude Code installed
6333
+ * can use the Chat widget without a separate API key.
6334
+ */
6335
+
6336
+ const { spawn, execSync } = require$$0$4;
6337
+ const {
6338
+ LLM_STREAM_DELTA: LLM_STREAM_DELTA$2,
6339
+ LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$2,
6340
+ LLM_STREAM_TOOL_RESULT: LLM_STREAM_TOOL_RESULT$2,
6341
+ LLM_STREAM_COMPLETE: LLM_STREAM_COMPLETE$2,
6342
+ LLM_STREAM_ERROR: LLM_STREAM_ERROR$2,
6343
+ } = llmEvents$1;
6344
+
6345
+ /**
6346
+ * Cached shell PATH result (resolved once, reused for all spawns).
6347
+ * Same pattern as mcpController.js.
6348
+ */
6349
+ let _shellPath = null;
6350
+
6351
+ function getShellPath() {
6352
+ if (_shellPath !== null) return _shellPath;
6353
+
6354
+ try {
6355
+ const shell = process.env.SHELL || "/bin/bash";
6356
+ _shellPath = execSync(`${shell} -ilc 'echo -n "$PATH"'`, {
6357
+ encoding: "utf8",
6358
+ timeout: 5000,
6359
+ });
6360
+ } catch {
6361
+ _shellPath = process.env.PATH || "";
6362
+ }
6363
+
6364
+ return _shellPath;
6365
+ }
6366
+
6367
+ /**
6368
+ * Cached CLI binary path (resolved once via `which claude`).
6369
+ */
6370
+ let _cliBinaryPath = undefined; // undefined = not yet checked
6371
+
6372
+ function resolveCliBinary() {
6373
+ if (_cliBinaryPath !== undefined) return _cliBinaryPath;
6374
+
6375
+ try {
6376
+ const fullPath = getShellPath();
6377
+ _cliBinaryPath = execSync("which claude", {
6378
+ encoding: "utf8",
6379
+ timeout: 5000,
6380
+ env: { ...process.env, PATH: fullPath },
6381
+ }).trim();
6382
+ } catch {
6383
+ _cliBinaryPath = null;
6384
+ }
6385
+
6386
+ return _cliBinaryPath;
6387
+ }
6388
+
6389
+ /**
6390
+ * Active CLI processes for abort support.
6391
+ * Map<requestId, ChildProcess>
6392
+ */
6393
+ const activeProcesses = new Map();
6394
+
6395
+ /**
6396
+ * Session IDs for conversation continuity.
6397
+ * Map<widgetUuid, sessionId>
6398
+ */
6399
+ const sessions = new Map();
6400
+
6401
+ /**
6402
+ * Send events safely to a window.
6403
+ */
6404
+ function safeSend(win, channel, data) {
6405
+ if (win && !win.isDestroyed()) {
6406
+ win.webContents.send(channel, data);
6407
+ }
6408
+ }
6409
+
6410
+ const cliController$2 = {
6411
+ /**
6412
+ * isAvailable
6413
+ * Check if the Claude Code CLI is installed and accessible.
6414
+ *
6415
+ * @returns {{ available: boolean, path?: string }}
6416
+ */
6417
+ isAvailable: () => {
6418
+ const binaryPath = resolveCliBinary();
6419
+ if (binaryPath) {
6420
+ return { available: true, path: binaryPath };
6421
+ }
6422
+ return { available: false };
6423
+ },
6424
+
6425
+ /**
6426
+ * sendMessage
6427
+ * Stream a response from the Claude Code CLI with NDJSON parsing.
6428
+ *
6429
+ * @param {BrowserWindow} win - the window to send stream events to
6430
+ * @param {string} requestId - unique ID for this request
6431
+ * @param {object} params - { model, messages, systemPrompt, maxToolRounds, widgetUuid }
6432
+ */
6433
+ sendMessage: async (win, requestId, params) => {
6434
+ const { model, messages, systemPrompt, widgetUuid } = params;
6435
+
6436
+ const binaryPath = resolveCliBinary();
6437
+ if (!binaryPath) {
6438
+ safeSend(win, LLM_STREAM_ERROR$2, {
6439
+ requestId,
6440
+ error:
6441
+ "Claude Code CLI not found. Install from https://claude.ai/download",
6442
+ code: "CLI_NOT_FOUND",
6443
+ });
6444
+ return;
6445
+ }
6446
+
6447
+ // Build CLI args
6448
+ const args = ["-p", "--output-format", "stream-json", "--verbose"];
6449
+
6450
+ if (model) {
6451
+ args.push("--model", model);
6452
+ }
6453
+
6454
+ if (systemPrompt) {
6455
+ args.push("--append-system-prompt", systemPrompt);
6456
+ }
6457
+
6458
+ // Resume existing session for conversation continuity
6459
+ const sessionId = widgetUuid ? sessions.get(widgetUuid) : null;
6460
+ if (sessionId) {
6461
+ args.push("--resume", sessionId);
6462
+ }
6463
+
6464
+ // Extract the user message (last user message in the array)
6465
+ const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
6466
+ const userText =
6467
+ typeof lastUserMsg?.content === "string"
6468
+ ? lastUserMsg.content
6469
+ : Array.isArray(lastUserMsg?.content)
6470
+ ? lastUserMsg.content
6471
+ .filter((b) => b.type === "text")
6472
+ .map((b) => b.text)
6473
+ .join("\n")
6474
+ : "";
6475
+
6476
+ if (!userText) {
6477
+ safeSend(win, LLM_STREAM_ERROR$2, {
6478
+ requestId,
6479
+ error: "No user message to send.",
6480
+ code: "CLI_ERROR",
6481
+ });
6482
+ return;
6483
+ }
6484
+
6485
+ try {
6486
+ const fullPath = getShellPath();
6487
+ const child = spawn(binaryPath, args, {
6488
+ env: { ...process.env, PATH: fullPath },
6489
+ stdio: ["pipe", "pipe", "pipe"],
6490
+ });
6491
+
6492
+ activeProcesses.set(requestId, child);
6493
+
6494
+ // Pipe user message via stdin (not visible in ps)
6495
+ child.stdin.write(userText);
6496
+ child.stdin.end();
6497
+
6498
+ let stdoutBuffer = "";
6499
+ let stderrBuffer = "";
6500
+ let capturedSessionId = null;
6501
+ let retried = false;
6502
+
6503
+ // Track active tool calls for mapping results
6504
+ const activeToolCalls = new Map();
6505
+
6506
+ child.stdout.on("data", (chunk) => {
6507
+ stdoutBuffer += chunk.toString();
6508
+
6509
+ // Process complete lines
6510
+ const lines = stdoutBuffer.split("\n");
6511
+ stdoutBuffer = lines.pop(); // keep incomplete line in buffer
6512
+
6513
+ for (const line of lines) {
6514
+ if (!line.trim()) continue;
6515
+
6516
+ let parsed;
6517
+ try {
6518
+ parsed = JSON.parse(line);
6519
+ } catch {
6520
+ console.warn("[cliController] Skipping invalid JSON line:", line);
6521
+ continue;
6522
+ }
6523
+
6524
+ // Capture session ID from any message that has it
6525
+ if (parsed.session_id && widgetUuid) {
6526
+ capturedSessionId = parsed.session_id;
6527
+ sessions.set(widgetUuid, capturedSessionId);
6528
+ }
6529
+
6530
+ // Map CLI stream-json events to IPC events
6531
+ if (parsed.type === "content_block_delta") {
6532
+ if (parsed.delta?.type === "text_delta" && parsed.delta.text) {
6533
+ safeSend(win, LLM_STREAM_DELTA$2, {
6534
+ requestId,
6535
+ text: parsed.delta.text,
6536
+ });
6537
+ } else if (parsed.delta?.type === "input_json_delta") {
6538
+ // Update tool input incrementally
6539
+ const tc = activeToolCalls.get(parsed.index);
6540
+ if (tc) {
6541
+ tc.partialInput =
6542
+ (tc.partialInput || "") + (parsed.delta.partial_json || "");
6543
+ }
6544
+ }
6545
+ } else if (parsed.type === "content_block_start") {
6546
+ if (parsed.content_block?.type === "tool_use") {
6547
+ const toolBlock = parsed.content_block;
6548
+ activeToolCalls.set(parsed.index, {
6549
+ toolUseId: toolBlock.id,
6550
+ toolName: toolBlock.name,
6551
+ partialInput: "",
6552
+ });
6553
+ safeSend(win, LLM_STREAM_TOOL_CALL$2, {
6554
+ requestId,
6555
+ toolUseId: toolBlock.id,
6556
+ toolName: toolBlock.name,
6557
+ serverName: "Claude Code",
6558
+ input: toolBlock.input || {},
6559
+ });
6560
+ }
6561
+ } else if (parsed.type === "content_block_stop") {
6562
+ // Tool call completed — try to parse the accumulated input
6563
+ const tc = activeToolCalls.get(parsed.index);
6564
+ if (tc && tc.partialInput) {
6565
+ try {
6566
+ tc.finalInput = JSON.parse(tc.partialInput);
6567
+ } catch {
6568
+ tc.finalInput = tc.partialInput;
6569
+ }
6570
+ }
6571
+ } else if (parsed.type === "message_stop") {
6572
+ // Individual message completed (may be followed by more in tool-use loops)
6573
+ } else if (parsed.type === "result") {
6574
+ // Final result — conversation complete
6575
+ const content = [];
6576
+ if (parsed.result) {
6577
+ content.push({ type: "text", text: parsed.result });
6578
+ }
6579
+
6580
+ safeSend(win, LLM_STREAM_COMPLETE$2, {
6581
+ requestId,
6582
+ content,
6583
+ stopReason: parsed.stop_reason || "end_turn",
6584
+ usage: parsed.usage || {},
6585
+ });
6586
+ }
6587
+ }
6588
+ });
6589
+
6590
+ child.stderr.on("data", (chunk) => {
6591
+ stderrBuffer += chunk.toString();
6592
+ });
6593
+
6594
+ child.on("error", (err) => {
6595
+ activeProcesses.delete(requestId);
6596
+ safeSend(win, LLM_STREAM_ERROR$2, {
6597
+ requestId,
6598
+ error: `Failed to start Claude CLI: ${err.message}`,
6599
+ code: "CLI_SPAWN_ERROR",
6600
+ });
6601
+ });
6602
+
6603
+ child.on("close", (code) => {
6604
+ activeProcesses.delete(requestId);
6605
+
6606
+ // Process any remaining buffer
6607
+ if (stdoutBuffer.trim()) {
6608
+ try {
6609
+ const parsed = JSON.parse(stdoutBuffer);
6610
+ if (parsed.session_id && widgetUuid) {
6611
+ sessions.set(widgetUuid, parsed.session_id);
6612
+ }
6613
+ if (parsed.type === "result") {
6614
+ const content = [];
6615
+ if (parsed.result) {
6616
+ content.push({ type: "text", text: parsed.result });
6617
+ }
6618
+ safeSend(win, LLM_STREAM_COMPLETE$2, {
6619
+ requestId,
6620
+ content,
6621
+ stopReason: parsed.stop_reason || "end_turn",
6622
+ usage: parsed.usage || {},
6623
+ });
6624
+ return;
6625
+ }
6626
+ } catch {
6627
+ // ignore
6628
+ }
6629
+ }
6630
+
6631
+ if (code !== 0 && code !== null) {
6632
+ // Check if resume failed and retry without it
6633
+ if (sessionId && !retried && stderrBuffer.includes("session")) {
6634
+ retried = true;
6635
+ if (widgetUuid) sessions.delete(widgetUuid);
6636
+ // Retry without --resume
6637
+ cliController$2.sendMessage(win, requestId, {
6638
+ ...params,
6639
+ _retryWithoutResume: true,
6640
+ });
6641
+ return;
6642
+ }
6643
+
6644
+ // Check for auth errors
6645
+ if (
6646
+ stderrBuffer.includes("auth") ||
6647
+ stderrBuffer.includes("login") ||
6648
+ stderrBuffer.includes("not authenticated")
6649
+ ) {
6650
+ safeSend(win, LLM_STREAM_ERROR$2, {
6651
+ requestId,
6652
+ error:
6653
+ "Claude Code CLI is not authenticated. Run `claude auth login` in your terminal.",
6654
+ code: "CLI_AUTH_ERROR",
6655
+ });
6656
+ return;
6657
+ }
6658
+
6659
+ safeSend(win, LLM_STREAM_ERROR$2, {
6660
+ requestId,
6661
+ error: `Claude CLI exited with code ${code}${stderrBuffer ? ": " + stderrBuffer.slice(0, 500) : ""}`,
6662
+ code: "CLI_ERROR",
6663
+ });
6664
+ }
6665
+ });
6666
+ } catch (err) {
6667
+ activeProcesses.delete(requestId);
6668
+ safeSend(win, LLM_STREAM_ERROR$2, {
6669
+ requestId,
6670
+ error: `Failed to start Claude CLI: ${err.message}`,
6671
+ code: "CLI_SPAWN_ERROR",
6672
+ });
6673
+ }
6674
+ },
6675
+
6676
+ /**
6677
+ * abortRequest
6678
+ * Kill an in-flight CLI process.
6679
+ *
6680
+ * @param {string} requestId - the request to cancel
6681
+ * @returns {{ success: boolean }}
6682
+ */
6683
+ abortRequest: (requestId) => {
6684
+ const child = activeProcesses.get(requestId);
6685
+ if (child) {
6686
+ child.kill("SIGTERM");
6687
+ activeProcesses.delete(requestId);
6688
+ return { success: true };
6689
+ }
6690
+ return { success: false, message: "Request not found" };
6691
+ },
6692
+
6693
+ /**
6694
+ * clearSession
6695
+ * Remove the stored session ID for a widget (called on "New Chat").
6696
+ *
6697
+ * @param {string} widgetUuid - the widget whose session to clear
6698
+ * @returns {{ success: boolean }}
6699
+ */
6700
+ clearSession: (widgetUuid) => {
6701
+ if (widgetUuid && sessions.has(widgetUuid)) {
6702
+ sessions.delete(widgetUuid);
6703
+ return { success: true };
6704
+ }
6705
+ return { success: false };
6706
+ },
6707
+ };
6708
+
6709
+ var cliController_1 = cliController$2;
6710
+
6711
+ /**
6712
+ * llmController.js
6713
+ *
6714
+ * Manages LLM chat interactions in the main process.
6715
+ * Streams responses from Anthropic's Messages API, handles tool-use loops
6716
+ * by calling MCP tools via mcpController, and supports request cancellation.
6717
+ *
6718
+ * Conversation state lives in the renderer — this controller is stateless
6719
+ * per-request, receiving the full messages array each time.
6720
+ */
6721
+
6722
+ const Anthropic = require$$0$5;
6723
+ const mcpController$1 = mcpController_1;
6724
+ const cliController$1 = cliController_1;
6725
+ const {
6726
+ LLM_STREAM_DELTA: LLM_STREAM_DELTA$1,
6727
+ LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$1,
6728
+ LLM_STREAM_TOOL_RESULT: LLM_STREAM_TOOL_RESULT$1,
6729
+ LLM_STREAM_COMPLETE: LLM_STREAM_COMPLETE$1,
6730
+ LLM_STREAM_ERROR: LLM_STREAM_ERROR$1,
6731
+ } = llmEvents$1;
6732
+
6733
+ /**
6734
+ * In-flight requests for cancellation support.
6735
+ * Map<requestId, AbortController>
6736
+ */
6737
+ const activeRequests = new Map();
6738
+
6739
+ /**
6740
+ * Default maximum tool-use rounds to prevent infinite loops.
6741
+ */
6742
+ const DEFAULT_MAX_TOOL_ROUNDS = 10;
6743
+
6744
+ /**
6745
+ * Convert MCP tool format to Anthropic tool format.
6746
+ * MCP: { name, description, inputSchema }
6747
+ * Anthropic: { name, description, input_schema }
6748
+ */
6749
+ function mcpToolToAnthropic(tool) {
6750
+ return {
6751
+ name: tool.name,
6752
+ description: tool.description || "",
6753
+ input_schema: tool.inputSchema || { type: "object", properties: {} },
6754
+ };
6755
+ }
6756
+
6757
+ /**
6758
+ * Extract text content from an MCP tool result.
6759
+ * MCP results can be { content: [{ type: "text", text: "..." }] } or plain strings.
6760
+ */
6761
+ function extractToolResultText(mcpResult) {
6762
+ if (!mcpResult) return "No result returned.";
6763
+ if (mcpResult.error) return `Error: ${mcpResult.message}`;
6764
+
6765
+ const result = mcpResult.result;
6766
+ if (!result) return "No result returned.";
6767
+
6768
+ // MCP standard: { content: [{ type, text }] }
6769
+ if (result.content && Array.isArray(result.content)) {
6770
+ return result.content
6771
+ .filter((c) => c.type === "text")
6772
+ .map((c) => c.text)
6773
+ .join("\n");
6774
+ }
6775
+
6776
+ // Plain string
6777
+ if (typeof result === "string") return result;
6778
+
6779
+ // Fallback: JSON stringify
6780
+ return JSON.stringify(result);
6781
+ }
6782
+
6783
+ const llmController$1 = {
6784
+ /**
6785
+ * sendMessage
6786
+ * Stream a response from the Anthropic Messages API with tool-use support.
6787
+ *
6788
+ * @param {BrowserWindow} win - the window to send stream events to
6789
+ * @param {string} requestId - unique ID for this request (for filtering + cancellation)
6790
+ * @param {object} params - { apiKey, model, messages, tools, toolServerMap, systemPrompt, maxToolRounds }
6791
+ */
6792
+ sendMessage: async (win, requestId, params) => {
6793
+ // Route to CLI backend if specified
6794
+ if (params.backend === "claude-code") {
6795
+ return cliController$1.sendMessage(win, requestId, params);
6796
+ }
6797
+
6798
+ const {
6799
+ apiKey,
6800
+ model = "claude-sonnet-4-20250514",
6801
+ messages,
6802
+ tools = [],
6803
+ toolServerMap = {},
6804
+ systemPrompt,
6805
+ maxToolRounds = DEFAULT_MAX_TOOL_ROUNDS,
6806
+ } = params;
6807
+
6808
+ // Set up abort controller
6809
+ const abortController = new AbortController();
6810
+ activeRequests.set(requestId, abortController);
6811
+
6812
+ try {
6813
+ if (!apiKey) {
6814
+ throw new Error(
6815
+ "API key is required. Add an Anthropic provider in your dashboard settings.",
6816
+ );
6817
+ }
6818
+
6819
+ const client = new Anthropic({ apiKey });
6820
+
6821
+ // Convert MCP tools to Anthropic format
6822
+ const anthropicTools = tools.map(mcpToolToAnthropic);
6823
+
6824
+ // Build the conversation — mutable copy for tool-use loop
6825
+ let currentMessages = [...messages];
6826
+ let toolRound = 0;
6827
+
6828
+ while (toolRound <= maxToolRounds) {
6829
+ // Check for abort before each API call
6830
+ if (abortController.signal.aborted) {
6831
+ return;
6832
+ }
6833
+
6834
+ // Build request params
6835
+ const requestParams = {
6836
+ model,
6837
+ max_tokens: 8192,
6838
+ messages: currentMessages,
6839
+ stream: true,
6840
+ };
6841
+ if (systemPrompt) {
6842
+ requestParams.system = systemPrompt;
6843
+ }
6844
+ if (anthropicTools.length > 0) {
6845
+ requestParams.tools = anthropicTools;
6846
+ }
6847
+
6848
+ // Stream the response
6849
+ const stream = client.messages.stream(requestParams, {
6850
+ signal: abortController.signal,
6851
+ });
6852
+
6853
+ let responseContentBlocks = [];
6854
+ let stopReason = null;
6855
+
6856
+ // Collect text deltas and content blocks
6857
+ stream.on("text", (text) => {
6858
+ if (win && !win.isDestroyed()) {
6859
+ win.webContents.send(LLM_STREAM_DELTA$1, { requestId, text });
6860
+ }
6861
+ });
6862
+
6863
+ // Wait for the full message
6864
+ const finalMessage = await stream.finalMessage();
6865
+ responseContentBlocks = finalMessage.content;
6866
+ stopReason = finalMessage.stop_reason;
6867
+
6868
+ // Check for tool use
6869
+ const toolUseBlocks = responseContentBlocks.filter(
6870
+ (block) => block.type === "tool_use",
6871
+ );
6872
+
6873
+ if (stopReason === "end_turn" || toolUseBlocks.length === 0) {
6874
+ // Done — send complete event
6875
+ if (win && !win.isDestroyed()) {
6876
+ win.webContents.send(LLM_STREAM_COMPLETE$1, {
6877
+ requestId,
6878
+ content: responseContentBlocks,
6879
+ stopReason,
6880
+ usage: finalMessage.usage,
6881
+ });
6882
+ }
6883
+ return;
6884
+ }
6885
+
6886
+ // Tool use round
6887
+ toolRound++;
6888
+
6889
+ // Append assistant message with tool_use blocks
6890
+ currentMessages.push({
6891
+ role: "assistant",
6892
+ content: responseContentBlocks,
6893
+ });
6894
+
6895
+ // Execute each tool call
6896
+ const toolResults = [];
6897
+ for (const toolBlock of toolUseBlocks) {
6898
+ if (abortController.signal.aborted) return;
6899
+
6900
+ const serverName = toolServerMap[toolBlock.name];
6901
+
6902
+ // Notify renderer of tool call
6903
+ if (win && !win.isDestroyed()) {
6904
+ win.webContents.send(LLM_STREAM_TOOL_CALL$1, {
6905
+ requestId,
6906
+ toolUseId: toolBlock.id,
6907
+ toolName: toolBlock.name,
6908
+ serverName,
6909
+ input: toolBlock.input,
6910
+ });
6911
+ }
6912
+
6913
+ let resultText;
6914
+ let isError = false;
6915
+
6916
+ if (!serverName) {
6917
+ resultText = `Error: No MCP server found for tool "${toolBlock.name}". The server may have disconnected.`;
6918
+ isError = true;
6919
+ } else {
6920
+ try {
6921
+ const mcpResult = await mcpController$1.callTool(
6922
+ win,
6923
+ serverName,
6924
+ toolBlock.name,
6925
+ toolBlock.input,
6926
+ );
6927
+ if (mcpResult.error) {
6928
+ resultText = `Error: ${mcpResult.message}`;
6929
+ isError = true;
6930
+ } else {
6931
+ resultText = extractToolResultText(mcpResult);
6932
+ }
6933
+ } catch (err) {
6934
+ resultText = `Error calling tool: ${err.message}`;
6935
+ isError = true;
6936
+ }
6937
+ }
6938
+
6939
+ // Notify renderer of tool result
6940
+ if (win && !win.isDestroyed()) {
6941
+ win.webContents.send(LLM_STREAM_TOOL_RESULT$1, {
6942
+ requestId,
6943
+ toolUseId: toolBlock.id,
6944
+ toolName: toolBlock.name,
6945
+ result: resultText,
6946
+ isError,
6947
+ });
6948
+ }
6949
+
6950
+ toolResults.push({
6951
+ type: "tool_result",
6952
+ tool_use_id: toolBlock.id,
6953
+ content: resultText,
6954
+ is_error: isError,
6955
+ });
6956
+ }
6957
+
6958
+ // Append tool results as user message
6959
+ currentMessages.push({
6960
+ role: "user",
6961
+ content: toolResults,
6962
+ });
6963
+
6964
+ // Loop continues — next API call with tool results
6965
+ }
6966
+
6967
+ // Exceeded max tool rounds
6968
+ if (win && !win.isDestroyed()) {
6969
+ win.webContents.send(LLM_STREAM_ERROR$1, {
6970
+ requestId,
6971
+ error: `Exceeded maximum tool-use rounds (${maxToolRounds}). The assistant may be stuck in a loop.`,
6972
+ code: "MAX_TOOL_ROUNDS",
6973
+ });
6974
+ }
6975
+ } catch (error) {
6976
+ if (abortController.signal.aborted) {
6977
+ // Request was cancelled — not an error
6978
+ return;
6979
+ }
6980
+
6981
+ console.error(`[llmController] Error in sendMessage:`, error);
6982
+
6983
+ const errorPayload = {
6984
+ requestId,
6985
+ error: error.message || "Unknown error",
6986
+ code: error.status || error.code || "UNKNOWN",
6987
+ };
6988
+
6989
+ // Handle rate limiting
6990
+ if (error.status === 429) {
6991
+ errorPayload.code = "RATE_LIMITED";
6992
+ const retryAfter = error.headers?.["retry-after"];
6993
+ if (retryAfter) {
6994
+ errorPayload.retryAfter = parseInt(retryAfter, 10);
6995
+ }
6996
+ }
6997
+
6998
+ if (win && !win.isDestroyed()) {
6999
+ win.webContents.send(LLM_STREAM_ERROR$1, errorPayload);
7000
+ }
7001
+ } finally {
7002
+ activeRequests.delete(requestId);
7003
+ }
7004
+ },
7005
+
7006
+ /**
7007
+ * abortRequest
7008
+ * Cancel an in-flight LLM request.
7009
+ *
7010
+ * @param {BrowserWindow} win - the window (unused but kept for API consistency)
7011
+ * @param {string} requestId - the request to cancel
7012
+ * @returns {{ success: boolean }}
7013
+ */
7014
+ abortRequest: (win, requestId) => {
7015
+ const controller = activeRequests.get(requestId);
7016
+ if (controller) {
7017
+ controller.abort();
7018
+ activeRequests.delete(requestId);
7019
+ return { success: true };
7020
+ }
7021
+ // Fallback to CLI controller
7022
+ return cliController$1.abortRequest(requestId);
7023
+ },
7024
+ };
7025
+
7026
+ var llmController_1 = llmController$1;
7027
+
6265
7028
  /**
6266
7029
  * clientFactories.js
6267
7030
  *
@@ -6392,7 +7155,7 @@ var controller = {
6392
7155
  searchIndex,
6393
7156
  };
6394
7157
 
6395
- const { ipcRenderer: ipcRenderer$g } = require$$0;
7158
+ const { ipcRenderer: ipcRenderer$h } = require$$0;
6396
7159
  const {
6397
7160
  SECURE_STORE_ENCRYPTION_CHECK,
6398
7161
  SECURE_STORE_SET_DATA,
@@ -6404,10 +7167,10 @@ const {
6404
7167
  */
6405
7168
  const secureStoreApi$2 = {
6406
7169
  isEncryptionAvailable: () =>
6407
- ipcRenderer$g.invoke(SECURE_STORE_ENCRYPTION_CHECK, {}),
7170
+ ipcRenderer$h.invoke(SECURE_STORE_ENCRYPTION_CHECK, {}),
6408
7171
  saveData: (key, value) =>
6409
- ipcRenderer$g.invoke(SECURE_STORE_SET_DATA, { key, value }),
6410
- getData: (key) => ipcRenderer$g.invoke(SECURE_STORE_GET_DATA, { key }),
7172
+ ipcRenderer$h.invoke(SECURE_STORE_SET_DATA, { key, value }),
7173
+ getData: (key) => ipcRenderer$h.invoke(SECURE_STORE_GET_DATA, { key }),
6411
7174
  };
6412
7175
 
6413
7176
  var secureStoreApi_1 = secureStoreApi$2;
@@ -6418,7 +7181,7 @@ var secureStoreApi_1 = secureStoreApi$2;
6418
7181
  * Handle the workspace configuration file
6419
7182
  */
6420
7183
 
6421
- const { ipcRenderer: ipcRenderer$f } = require$$0;
7184
+ const { ipcRenderer: ipcRenderer$g } = require$$0;
6422
7185
  const {
6423
7186
  WORKSPACE_LIST,
6424
7187
  WORKSPACE_SAVE,
@@ -6435,7 +7198,7 @@ const workspaceApi$2 = {
6435
7198
  */
6436
7199
  listWorkspacesForApplication: (appId) => {
6437
7200
  console.log("listWorkspacesForApplication called with appId:", appId);
6438
- return ipcRenderer$f.invoke(WORKSPACE_LIST, { appId });
7201
+ return ipcRenderer$g.invoke(WORKSPACE_LIST, { appId });
6439
7202
  },
6440
7203
 
6441
7204
  /**
@@ -6446,7 +7209,7 @@ const workspaceApi$2 = {
6446
7209
  * @returns
6447
7210
  */
6448
7211
  saveWorkspaceForApplication: (appId, data) =>
6449
- ipcRenderer$f.invoke(WORKSPACE_SAVE, { appId, data }),
7212
+ ipcRenderer$g.invoke(WORKSPACE_SAVE, { appId, data }),
6450
7213
 
6451
7214
  /**
6452
7215
  * deleteWorkspaceForApplication
@@ -6456,7 +7219,7 @@ const workspaceApi$2 = {
6456
7219
  * @returns
6457
7220
  */
6458
7221
  deleteWorkspaceForApplication: (appId, workspaceId) =>
6459
- ipcRenderer$f.invoke(WORKSPACE_DELETE, { appId, workspaceId }),
7222
+ ipcRenderer$g.invoke(WORKSPACE_DELETE, { appId, workspaceId }),
6460
7223
  };
6461
7224
 
6462
7225
  var workspaceApi_1 = workspaceApi$2;
@@ -6468,15 +7231,15 @@ var workspaceApi_1 = workspaceApi$2;
6468
7231
  */
6469
7232
 
6470
7233
  // ipcRenderer that must be used to invoke the events
6471
- const { ipcRenderer: ipcRenderer$e } = require$$0;
7234
+ const { ipcRenderer: ipcRenderer$f } = require$$0;
6472
7235
 
6473
7236
  const { LAYOUT_LIST, LAYOUT_SAVE } = events$8;
6474
7237
 
6475
7238
  const layoutApi$2 = {
6476
7239
  listLayoutsForApplication: (appId) =>
6477
- ipcRenderer$e.invoke(LAYOUT_LIST, { appId }),
7240
+ ipcRenderer$f.invoke(LAYOUT_LIST, { appId }),
6478
7241
  saveLayoutForApplication: (appId, data) =>
6479
- ipcRenderer$e.invoke(LAYOUT_SAVE, { appId, data }),
7242
+ ipcRenderer$f.invoke(LAYOUT_SAVE, { appId, data }),
6480
7243
  };
6481
7244
 
6482
7245
  var layoutApi_1 = layoutApi$2;
@@ -6488,7 +7251,7 @@ var layoutApi_1 = layoutApi$2;
6488
7251
  */
6489
7252
 
6490
7253
  // ipcRenderer that must be used to invoke the events
6491
- const { ipcRenderer: ipcRenderer$d } = require$$0;
7254
+ const { ipcRenderer: ipcRenderer$e } = require$$0;
6492
7255
 
6493
7256
  const {
6494
7257
  DATA_JSON_TO_CSV_FILE,
@@ -6507,7 +7270,7 @@ const {
6507
7270
  const dataApi$2 = {
6508
7271
  // convert a json array of objects to a csv string and save to file
6509
7272
  convertJsonToCsvFile: (appId, jsonObject, filename) =>
6510
- ipcRenderer$d.invoke(DATA_JSON_TO_CSV_FILE, {
7273
+ ipcRenderer$e.invoke(DATA_JSON_TO_CSV_FILE, {
6511
7274
  appId,
6512
7275
  jsonObject,
6513
7276
  filename,
@@ -6515,10 +7278,10 @@ const dataApi$2 = {
6515
7278
 
6516
7279
  // convert a json array of objects to a csv string and return a string
6517
7280
  convertJsonToCsvString: (appId, jsonObject) =>
6518
- ipcRenderer$d.invoke(DATA_JSON_TO_CSV_STRING, { appId, jsonObject }),
7281
+ ipcRenderer$e.invoke(DATA_JSON_TO_CSV_STRING, { appId, jsonObject }),
6519
7282
 
6520
7283
  parseXMLStream: (filepath, outpath, start) =>
6521
- ipcRenderer$d.invoke(PARSE_XML_STREAM, {
7284
+ ipcRenderer$e.invoke(PARSE_XML_STREAM, {
6522
7285
  filepath,
6523
7286
  outpath,
6524
7287
  start,
@@ -6532,7 +7295,7 @@ const dataApi$2 = {
6532
7295
  headers = null,
6533
7296
  limit = null,
6534
7297
  ) => {
6535
- ipcRenderer$d.invoke(PARSE_CSV_STREAM, {
7298
+ ipcRenderer$e.invoke(PARSE_CSV_STREAM, {
6536
7299
  filepath,
6537
7300
  outpath,
6538
7301
  delimiter,
@@ -6543,15 +7306,15 @@ const dataApi$2 = {
6543
7306
  },
6544
7307
 
6545
7308
  readLinesFromFile: (filepath, lineCount) => {
6546
- ipcRenderer$d.invoke(READ_LINES, { filepath, lineCount });
7309
+ ipcRenderer$e.invoke(READ_LINES, { filepath, lineCount });
6547
7310
  },
6548
7311
 
6549
7312
  readJSONFromFile: (filepath, objectCount = null) => {
6550
- ipcRenderer$d.invoke(READ_JSON, { filepath, objectCount });
7313
+ ipcRenderer$e.invoke(READ_JSON, { filepath, objectCount });
6551
7314
  },
6552
7315
 
6553
7316
  readDataFromURL: (url, toFilepath) => {
6554
- ipcRenderer$d.invoke(READ_DATA_URL, { url, toFilepath });
7317
+ ipcRenderer$e.invoke(READ_DATA_URL, { url, toFilepath });
6555
7318
  },
6556
7319
 
6557
7320
  /*
@@ -6560,7 +7323,7 @@ const dataApi$2 = {
6560
7323
  * @param {object} returnEmpty the return empty object
6561
7324
  */
6562
7325
  saveData: (data, filename, append, returnEmpty, uuid) =>
6563
- ipcRenderer$d.invoke(DATA_SAVE_TO_FILE, {
7326
+ ipcRenderer$e.invoke(DATA_SAVE_TO_FILE, {
6564
7327
  data,
6565
7328
  filename,
6566
7329
  append,
@@ -6572,14 +7335,14 @@ const dataApi$2 = {
6572
7335
  * @param {string} filename the filename to read (not path)
6573
7336
  */
6574
7337
  readData: (filename, returnEmpty = []) =>
6575
- ipcRenderer$d.invoke(DATA_READ_FROM_FILE, { filename, returnEmpty }),
7338
+ ipcRenderer$e.invoke(DATA_READ_FROM_FILE, { filename, returnEmpty }),
6576
7339
 
6577
7340
  /**
6578
7341
  * transformFile
6579
7342
  * @returns
6580
7343
  */
6581
7344
  transformFile: (filepath, outFilepath, mappingFunctionBody, args) => {
6582
- ipcRenderer$d.invoke(TRANSFORM_FILE, {
7345
+ ipcRenderer$e.invoke(TRANSFORM_FILE, {
6583
7346
  filepath,
6584
7347
  outFilepath,
6585
7348
  mappingFunctionBody,
@@ -6588,7 +7351,7 @@ const dataApi$2 = {
6588
7351
  },
6589
7352
 
6590
7353
  extractColorsFromImageURL: (url) => {
6591
- ipcRenderer$d.invoke(EXTRACT_COLORS_FROM_IMAGE, {
7354
+ ipcRenderer$e.invoke(EXTRACT_COLORS_FROM_IMAGE, {
6592
7355
  url,
6593
7356
  });
6594
7357
  },
@@ -6603,7 +7366,7 @@ var dataApi_1 = dataApi$2;
6603
7366
  */
6604
7367
 
6605
7368
  // ipcRenderer that must be used to invoke the events
6606
- const { ipcRenderer: ipcRenderer$c } = require$$0;
7369
+ const { ipcRenderer: ipcRenderer$d } = require$$0;
6607
7370
 
6608
7371
  const {
6609
7372
  SETTINGS_GET,
@@ -6614,14 +7377,14 @@ const {
6614
7377
  } = events$8;
6615
7378
 
6616
7379
  const settingsApi$2 = {
6617
- getSettingsForApplication: () => ipcRenderer$c.invoke(SETTINGS_GET, {}),
7380
+ getSettingsForApplication: () => ipcRenderer$d.invoke(SETTINGS_GET, {}),
6618
7381
  saveSettingsForApplication: (data) =>
6619
- ipcRenderer$c.invoke(SETTINGS_SAVE, { data }),
6620
- getDataDirectory: () => ipcRenderer$c.invoke(SETTINGS_GET_DATA_DIR, {}),
7382
+ ipcRenderer$d.invoke(SETTINGS_SAVE, { data }),
7383
+ getDataDirectory: () => ipcRenderer$d.invoke(SETTINGS_GET_DATA_DIR, {}),
6621
7384
  setDataDirectory: (dataDirectory) =>
6622
- ipcRenderer$c.invoke(SETTINGS_SET_DATA_DIR, { dataDirectory }),
7385
+ ipcRenderer$d.invoke(SETTINGS_SET_DATA_DIR, { dataDirectory }),
6623
7386
  migrateDataDirectory: (oldDirectory, newDirectory) =>
6624
- ipcRenderer$c.invoke(SETTINGS_MIGRATE_DATA_DIR, {
7387
+ ipcRenderer$d.invoke(SETTINGS_MIGRATE_DATA_DIR, {
6625
7388
  oldDirectory,
6626
7389
  newDirectory,
6627
7390
  }),
@@ -6636,7 +7399,7 @@ var settingsApi_1 = settingsApi$2;
6636
7399
  */
6637
7400
 
6638
7401
  // ipcRenderer that must be used to invoke the events
6639
- const { ipcRenderer: ipcRenderer$b } = require$$0;
7402
+ const { ipcRenderer: ipcRenderer$c } = require$$0;
6640
7403
 
6641
7404
  const { CHOOSE_FILE } = events$8;
6642
7405
 
@@ -6648,7 +7411,7 @@ const dialogApi$2 = {
6648
7411
  */
6649
7412
  chooseFile: (allowFile = true, extensions = ["*"]) => {
6650
7413
  console.log("dialog api choose file");
6651
- return ipcRenderer$b.invoke(CHOOSE_FILE, { allowFile, extensions });
7414
+ return ipcRenderer$c.invoke(CHOOSE_FILE, { allowFile, extensions });
6652
7415
  },
6653
7416
  };
6654
7417
 
@@ -6667,7 +7430,7 @@ var dialogApi_1 = dialogApi$2;
6667
7430
  * mainApi.widgets.uninstall('Weather')
6668
7431
  */
6669
7432
 
6670
- const { ipcRenderer: ipcRenderer$a } = require$$0;
7433
+ const { ipcRenderer: ipcRenderer$b } = require$$0;
6671
7434
 
6672
7435
  const widgetApi$2 = {
6673
7436
  /**
@@ -6676,7 +7439,7 @@ const widgetApi$2 = {
6676
7439
  */
6677
7440
  list: async () => {
6678
7441
  try {
6679
- return await ipcRenderer$a.invoke("widget:list");
7442
+ return await ipcRenderer$b.invoke("widget:list");
6680
7443
  } catch (error) {
6681
7444
  console.error("[WidgetApi] Error listing widgets:", error);
6682
7445
  throw error;
@@ -6690,7 +7453,7 @@ const widgetApi$2 = {
6690
7453
  */
6691
7454
  get: async (widgetName) => {
6692
7455
  try {
6693
- return await ipcRenderer$a.invoke("widget:get", widgetName);
7456
+ return await ipcRenderer$b.invoke("widget:get", widgetName);
6694
7457
  } catch (error) {
6695
7458
  console.error(`[WidgetApi] Error getting widget ${widgetName}:`, error);
6696
7459
  throw error;
@@ -6721,7 +7484,7 @@ const widgetApi$2 = {
6721
7484
  console.log(
6722
7485
  `[WidgetApi] Installing widget: ${widgetName} from ${downloadUrl}`,
6723
7486
  );
6724
- const config = await ipcRenderer$a.invoke(
7487
+ const config = await ipcRenderer$b.invoke(
6725
7488
  "widget:install",
6726
7489
  widgetName,
6727
7490
  downloadUrl,
@@ -6761,7 +7524,7 @@ const widgetApi$2 = {
6761
7524
  console.log(
6762
7525
  `[WidgetApi] Installing local widget: ${widgetName} from ${localPath}`,
6763
7526
  );
6764
- const config = await ipcRenderer$a.invoke(
7527
+ const config = await ipcRenderer$b.invoke(
6765
7528
  "widget:install-local",
6766
7529
  widgetName,
6767
7530
  localPath,
@@ -6792,7 +7555,7 @@ const widgetApi$2 = {
6792
7555
  loadFolder: async (folderPath) => {
6793
7556
  try {
6794
7557
  console.log(`[WidgetApi] Loading widgets from folder: ${folderPath}`);
6795
- const results = await ipcRenderer$a.invoke(
7558
+ const results = await ipcRenderer$b.invoke(
6796
7559
  "widget:load-folder",
6797
7560
  folderPath,
6798
7561
  );
@@ -6816,7 +7579,7 @@ const widgetApi$2 = {
6816
7579
  uninstall: async (widgetName) => {
6817
7580
  try {
6818
7581
  console.log(`[WidgetApi] Uninstalling widget: ${widgetName}`);
6819
- const success = await ipcRenderer$a.invoke("widget:uninstall", widgetName);
7582
+ const success = await ipcRenderer$b.invoke("widget:uninstall", widgetName);
6820
7583
  if (success) {
6821
7584
  console.log(`[WidgetApi] ✓ Widget ${widgetName} uninstalled`);
6822
7585
  } else {
@@ -6839,7 +7602,7 @@ const widgetApi$2 = {
6839
7602
  */
6840
7603
  getCachePath: async () => {
6841
7604
  try {
6842
- return await ipcRenderer$a.invoke("widget:cache-path");
7605
+ return await ipcRenderer$b.invoke("widget:cache-path");
6843
7606
  } catch (error) {
6844
7607
  console.error("[WidgetApi] Error getting cache path:", error);
6845
7608
  throw error;
@@ -6853,7 +7616,7 @@ const widgetApi$2 = {
6853
7616
  */
6854
7617
  getStoragePath: async () => {
6855
7618
  try {
6856
- return await ipcRenderer$a.invoke("widget:storage-path");
7619
+ return await ipcRenderer$b.invoke("widget:storage-path");
6857
7620
  } catch (error) {
6858
7621
  console.error("[WidgetApi] Error getting storage path:", error);
6859
7622
  throw error;
@@ -6870,7 +7633,7 @@ const widgetApi$2 = {
6870
7633
  setStoragePath: async (customPath) => {
6871
7634
  try {
6872
7635
  console.log(`[WidgetApi] Setting storage path to: ${customPath}`);
6873
- const result = await ipcRenderer$a.invoke(
7636
+ const result = await ipcRenderer$b.invoke(
6874
7637
  "widget:set-storage-path",
6875
7638
  customPath,
6876
7639
  );
@@ -6892,7 +7655,7 @@ const widgetApi$2 = {
6892
7655
  */
6893
7656
  getComponentConfigs: async () => {
6894
7657
  try {
6895
- return await ipcRenderer$a.invoke("widget:get-component-configs");
7658
+ return await ipcRenderer$b.invoke("widget:get-component-configs");
6896
7659
  } catch (error) {
6897
7660
  console.error("[WidgetApi] Error getting component configs:", error);
6898
7661
  return [];
@@ -6907,7 +7670,7 @@ const widgetApi$2 = {
6907
7670
  */
6908
7671
  readBundle: async (widgetName) => {
6909
7672
  try {
6910
- return await ipcRenderer$a.invoke("widget:read-bundle", widgetName);
7673
+ return await ipcRenderer$b.invoke("widget:read-bundle", widgetName);
6911
7674
  } catch (error) {
6912
7675
  console.error(
6913
7676
  `[WidgetApi] Error reading bundle for ${widgetName}:`,
@@ -6924,7 +7687,7 @@ const widgetApi$2 = {
6924
7687
  */
6925
7688
  readAllBundles: async () => {
6926
7689
  try {
6927
- return await ipcRenderer$a.invoke("widget:read-all-bundles");
7690
+ return await ipcRenderer$b.invoke("widget:read-all-bundles");
6928
7691
  } catch (error) {
6929
7692
  console.error("[WidgetApi] Error reading all bundles:", error);
6930
7693
  return [];
@@ -6944,7 +7707,7 @@ const widgetApi$2 = {
6944
7707
  * });
6945
7708
  */
6946
7709
  onInstalled: (callback) => {
6947
- ipcRenderer$a.on("widget:installed", (event, data) => {
7710
+ ipcRenderer$b.on("widget:installed", (event, data) => {
6948
7711
  callback(data);
6949
7712
  });
6950
7713
  },
@@ -6962,7 +7725,7 @@ const widgetApi$2 = {
6962
7725
  * });
6963
7726
  */
6964
7727
  onLoaded: (callback) => {
6965
- ipcRenderer$a.on("widgets:loaded", (event, data) => {
7728
+ ipcRenderer$b.on("widgets:loaded", (event, data) => {
6966
7729
  callback(data);
6967
7730
  });
6968
7731
  },
@@ -6973,7 +7736,7 @@ const widgetApi$2 = {
6973
7736
  * @param {Function} callback - The callback to remove
6974
7737
  */
6975
7738
  removeInstalledListener: (callback) => {
6976
- ipcRenderer$a.removeListener("widget:installed", callback);
7739
+ ipcRenderer$b.removeListener("widget:installed", callback);
6977
7740
  },
6978
7741
 
6979
7742
  /**
@@ -6982,7 +7745,7 @@ const widgetApi$2 = {
6982
7745
  * @param {Function} callback - The callback to remove
6983
7746
  */
6984
7747
  removeLoadedListener: (callback) => {
6985
- ipcRenderer$a.removeListener("widgets:loaded", callback);
7748
+ ipcRenderer$b.removeListener("widgets:loaded", callback);
6986
7749
  },
6987
7750
  };
6988
7751
 
@@ -6995,7 +7758,7 @@ var widgetApi_1 = widgetApi$2;
6995
7758
  * Communicates with main process via IPC to handle encryption and file storage
6996
7759
  */
6997
7760
 
6998
- const { ipcRenderer: ipcRenderer$9 } = require$$0;
7761
+ const { ipcRenderer: ipcRenderer$a } = require$$0;
6999
7762
  const {
7000
7763
  PROVIDER_SAVE,
7001
7764
  PROVIDER_LIST,
@@ -7025,7 +7788,7 @@ const providerApi$2 = {
7025
7788
  providerClass = "credential",
7026
7789
  mcpConfig = null,
7027
7790
  ) =>
7028
- ipcRenderer$9.invoke(PROVIDER_SAVE, {
7791
+ ipcRenderer$a.invoke(PROVIDER_SAVE, {
7029
7792
  appId,
7030
7793
  providerName,
7031
7794
  providerType,
@@ -7042,7 +7805,7 @@ const providerApi$2 = {
7042
7805
  * @param {String} appId - the appId specified in the dash initialization
7043
7806
  * @returns {Promise<Array>} Array of provider objects with name, type, credentials
7044
7807
  */
7045
- listProviders: (appId) => ipcRenderer$9.invoke(PROVIDER_LIST, { appId }),
7808
+ listProviders: (appId) => ipcRenderer$a.invoke(PROVIDER_LIST, { appId }),
7046
7809
 
7047
7810
  /**
7048
7811
  * getProvider
@@ -7054,7 +7817,7 @@ const providerApi$2 = {
7054
7817
  * @returns {Promise<Object>} Provider object with name, type, credentials
7055
7818
  */
7056
7819
  getProvider: (appId, providerName) =>
7057
- ipcRenderer$9.invoke(PROVIDER_GET, { appId, providerName }),
7820
+ ipcRenderer$a.invoke(PROVIDER_GET, { appId, providerName }),
7058
7821
 
7059
7822
  /**
7060
7823
  * deleteProvider
@@ -7066,7 +7829,7 @@ const providerApi$2 = {
7066
7829
  * @returns {Promise}
7067
7830
  */
7068
7831
  deleteProvider: (appId, providerName) =>
7069
- ipcRenderer$9.invoke(PROVIDER_DELETE, { appId, providerName }),
7832
+ ipcRenderer$a.invoke(PROVIDER_DELETE, { appId, providerName }),
7070
7833
 
7071
7834
  /**
7072
7835
  * listProvidersForApplication
@@ -7076,14 +7839,14 @@ const providerApi$2 = {
7076
7839
  * @param {String} appId - the appId specified in the dash initialization
7077
7840
  */
7078
7841
  listProvidersForApplication: (appId) => {
7079
- ipcRenderer$9
7842
+ ipcRenderer$a
7080
7843
  .invoke(PROVIDER_LIST, { appId })
7081
7844
  .then((result) => {
7082
7845
  // Emit the event for ElectronDashboardApi to listen to
7083
- ipcRenderer$9.send("PROVIDER_LIST_COMPLETE", result);
7846
+ ipcRenderer$a.send("PROVIDER_LIST_COMPLETE", result);
7084
7847
  })
7085
7848
  .catch((error) => {
7086
- ipcRenderer$9.send("PROVIDER_LIST_ERROR", {
7849
+ ipcRenderer$a.send("PROVIDER_LIST_ERROR", {
7087
7850
  error: error.message,
7088
7851
  });
7089
7852
  });
@@ -7100,7 +7863,7 @@ const providerApi$2 = {
7100
7863
  providerType,
7101
7864
  credentials,
7102
7865
  ) => {
7103
- ipcRenderer$9
7866
+ ipcRenderer$a
7104
7867
  .invoke(PROVIDER_SAVE, {
7105
7868
  appId,
7106
7869
  providerName,
@@ -7108,10 +7871,10 @@ const providerApi$2 = {
7108
7871
  credentials,
7109
7872
  })
7110
7873
  .then((result) => {
7111
- ipcRenderer$9.send("PROVIDER_SAVE_COMPLETE", result);
7874
+ ipcRenderer$a.send("PROVIDER_SAVE_COMPLETE", result);
7112
7875
  })
7113
7876
  .catch((error) => {
7114
- ipcRenderer$9.send("PROVIDER_SAVE_ERROR", {
7877
+ ipcRenderer$a.send("PROVIDER_SAVE_ERROR", {
7115
7878
  error: error.message,
7116
7879
  });
7117
7880
  });
@@ -7123,13 +7886,13 @@ const providerApi$2 = {
7123
7886
  * Event-listener-based version for use with ElectronDashboardApi
7124
7887
  */
7125
7888
  getProviderForApplication: (appId, providerName) => {
7126
- ipcRenderer$9
7889
+ ipcRenderer$a
7127
7890
  .invoke(PROVIDER_GET, { appId, providerName })
7128
7891
  .then((result) => {
7129
- ipcRenderer$9.send("PROVIDER_GET_COMPLETE", result);
7892
+ ipcRenderer$a.send("PROVIDER_GET_COMPLETE", result);
7130
7893
  })
7131
7894
  .catch((error) => {
7132
- ipcRenderer$9.send("PROVIDER_GET_ERROR", {
7895
+ ipcRenderer$a.send("PROVIDER_GET_ERROR", {
7133
7896
  error: error.message,
7134
7897
  });
7135
7898
  });
@@ -7141,13 +7904,13 @@ const providerApi$2 = {
7141
7904
  * Event-listener-based version for use with ElectronDashboardApi
7142
7905
  */
7143
7906
  deleteProviderForApplication: (appId, providerName) => {
7144
- ipcRenderer$9
7907
+ ipcRenderer$a
7145
7908
  .invoke(PROVIDER_DELETE, { appId, providerName })
7146
7909
  .then((result) => {
7147
- ipcRenderer$9.send("PROVIDER_DELETE_COMPLETE", result);
7910
+ ipcRenderer$a.send("PROVIDER_DELETE_COMPLETE", result);
7148
7911
  })
7149
7912
  .catch((error) => {
7150
- ipcRenderer$9.send("PROVIDER_DELETE_ERROR", {
7913
+ ipcRenderer$a.send("PROVIDER_DELETE_ERROR", {
7151
7914
  error: error.message,
7152
7915
  });
7153
7916
  });
@@ -7163,7 +7926,7 @@ var providerApi_1 = providerApi$2;
7163
7926
  * Communicates with main process via IPC to manage MCP server lifecycle.
7164
7927
  */
7165
7928
 
7166
- const { ipcRenderer: ipcRenderer$8 } = require$$0;
7929
+ const { ipcRenderer: ipcRenderer$9 } = require$$0;
7167
7930
  const {
7168
7931
  MCP_START_SERVER,
7169
7932
  MCP_STOP_SERVER,
@@ -7186,7 +7949,7 @@ const mcpApi$2 = {
7186
7949
  * @returns {Promise<{ success, serverName, tools, status } | { error, message }>}
7187
7950
  */
7188
7951
  startServer: (serverName, mcpConfig, credentials) =>
7189
- ipcRenderer$8.invoke(MCP_START_SERVER, {
7952
+ ipcRenderer$9.invoke(MCP_START_SERVER, {
7190
7953
  serverName,
7191
7954
  mcpConfig,
7192
7955
  credentials,
@@ -7200,7 +7963,7 @@ const mcpApi$2 = {
7200
7963
  * @returns {Promise<{ success, serverName } | { error, message }>}
7201
7964
  */
7202
7965
  stopServer: (serverName) =>
7203
- ipcRenderer$8.invoke(MCP_STOP_SERVER, { serverName }),
7966
+ ipcRenderer$9.invoke(MCP_STOP_SERVER, { serverName }),
7204
7967
 
7205
7968
  /**
7206
7969
  * listTools
@@ -7209,7 +7972,7 @@ const mcpApi$2 = {
7209
7972
  * @param {string} serverName the server name
7210
7973
  * @returns {Promise<{ tools } | { error, message }>}
7211
7974
  */
7212
- listTools: (serverName) => ipcRenderer$8.invoke(MCP_LIST_TOOLS, { serverName }),
7975
+ listTools: (serverName) => ipcRenderer$9.invoke(MCP_LIST_TOOLS, { serverName }),
7213
7976
 
7214
7977
  /**
7215
7978
  * callTool
@@ -7222,7 +7985,7 @@ const mcpApi$2 = {
7222
7985
  * @returns {Promise<{ result } | { error, message }>}
7223
7986
  */
7224
7987
  callTool: (serverName, toolName, args, allowedTools = null) =>
7225
- ipcRenderer$8.invoke(MCP_CALL_TOOL, {
7988
+ ipcRenderer$9.invoke(MCP_CALL_TOOL, {
7226
7989
  serverName,
7227
7990
  toolName,
7228
7991
  args,
@@ -7237,7 +8000,7 @@ const mcpApi$2 = {
7237
8000
  * @returns {Promise<{ resources } | { error, message }>}
7238
8001
  */
7239
8002
  listResources: (serverName) =>
7240
- ipcRenderer$8.invoke(MCP_LIST_RESOURCES, { serverName }),
8003
+ ipcRenderer$9.invoke(MCP_LIST_RESOURCES, { serverName }),
7241
8004
 
7242
8005
  /**
7243
8006
  * readResource
@@ -7248,7 +8011,7 @@ const mcpApi$2 = {
7248
8011
  * @returns {Promise<{ resource } | { error, message }>}
7249
8012
  */
7250
8013
  readResource: (serverName, uri) =>
7251
- ipcRenderer$8.invoke(MCP_READ_RESOURCE, { serverName, uri }),
8014
+ ipcRenderer$9.invoke(MCP_READ_RESOURCE, { serverName, uri }),
7252
8015
 
7253
8016
  /**
7254
8017
  * getServerStatus
@@ -7258,7 +8021,7 @@ const mcpApi$2 = {
7258
8021
  * @returns {Promise<{ status, tools, error }>}
7259
8022
  */
7260
8023
  getServerStatus: (serverName) =>
7261
- ipcRenderer$8.invoke(MCP_SERVER_STATUS, { serverName }),
8024
+ ipcRenderer$9.invoke(MCP_SERVER_STATUS, { serverName }),
7262
8025
 
7263
8026
  /**
7264
8027
  * getCatalog
@@ -7266,7 +8029,7 @@ const mcpApi$2 = {
7266
8029
  *
7267
8030
  * @returns {Promise<{ catalog } | { error, message }>}
7268
8031
  */
7269
- getCatalog: () => ipcRenderer$8.invoke(MCP_GET_CATALOG),
8032
+ getCatalog: () => ipcRenderer$9.invoke(MCP_GET_CATALOG),
7270
8033
  };
7271
8034
 
7272
8035
  var mcpApi_1 = mcpApi$2;
@@ -7284,7 +8047,7 @@ var mcpApi_1 = mcpApi$2;
7284
8047
  * mainApi.registry.checkUpdates([{ name: "weather-widgets", version: "1.0.0" }])
7285
8048
  */
7286
8049
 
7287
- const { ipcRenderer: ipcRenderer$7 } = require$$0;
8050
+ const { ipcRenderer: ipcRenderer$8 } = require$$0;
7288
8051
 
7289
8052
  const registryApi$2 = {
7290
8053
  /**
@@ -7294,7 +8057,7 @@ const registryApi$2 = {
7294
8057
  */
7295
8058
  fetchIndex: async (forceRefresh = false) => {
7296
8059
  try {
7297
- return await ipcRenderer$7.invoke("registry:fetch-index", forceRefresh);
8060
+ return await ipcRenderer$8.invoke("registry:fetch-index", forceRefresh);
7298
8061
  } catch (error) {
7299
8062
  console.error("[RegistryApi] Error fetching index:", error);
7300
8063
  throw error;
@@ -7309,7 +8072,7 @@ const registryApi$2 = {
7309
8072
  */
7310
8073
  search: async (query = "", filters = {}) => {
7311
8074
  try {
7312
- return await ipcRenderer$7.invoke("registry:search", query, filters);
8075
+ return await ipcRenderer$8.invoke("registry:search", query, filters);
7313
8076
  } catch (error) {
7314
8077
  console.error("[RegistryApi] Error searching registry:", error);
7315
8078
  throw error;
@@ -7323,7 +8086,7 @@ const registryApi$2 = {
7323
8086
  */
7324
8087
  getPackage: async (packageName) => {
7325
8088
  try {
7326
- return await ipcRenderer$7.invoke("registry:get-package", packageName);
8089
+ return await ipcRenderer$8.invoke("registry:get-package", packageName);
7327
8090
  } catch (error) {
7328
8091
  console.error(
7329
8092
  `[RegistryApi] Error getting package ${packageName}:`,
@@ -7340,7 +8103,7 @@ const registryApi$2 = {
7340
8103
  */
7341
8104
  checkUpdates: async (installedWidgets = []) => {
7342
8105
  try {
7343
- return await ipcRenderer$7.invoke(
8106
+ return await ipcRenderer$8.invoke(
7344
8107
  "registry:check-updates",
7345
8108
  installedWidgets,
7346
8109
  );
@@ -7359,17 +8122,17 @@ var registryApi_1 = registryApi$2;
7359
8122
  * Handle the theme configuration file
7360
8123
  */
7361
8124
 
7362
- const { ipcRenderer: ipcRenderer$6 } = require$$0;
8125
+ const { ipcRenderer: ipcRenderer$7 } = require$$0;
7363
8126
 
7364
8127
  const { THEME_LIST, THEME_SAVE, THEME_DELETE } = events$8;
7365
8128
 
7366
8129
  const themeApi$2 = {
7367
8130
  listThemesForApplication: (appId) =>
7368
- ipcRenderer$6.invoke(THEME_LIST, { appId }),
8131
+ ipcRenderer$7.invoke(THEME_LIST, { appId }),
7369
8132
  saveThemeForApplication: (appId, themeName, themeObject) =>
7370
- ipcRenderer$6.invoke(THEME_SAVE, { appId, themeName, themeObject }),
8133
+ ipcRenderer$7.invoke(THEME_SAVE, { appId, themeName, themeObject }),
7371
8134
  deleteThemeForApplication: (appId, themeKey) =>
7372
- ipcRenderer$6.invoke(THEME_DELETE, { appId, themeKey }),
8135
+ ipcRenderer$7.invoke(THEME_DELETE, { appId, themeKey }),
7373
8136
  };
7374
8137
 
7375
8138
  var themeApi_1 = themeApi$2;
@@ -7381,7 +8144,7 @@ var themeApi_1 = themeApi$2;
7381
8144
  */
7382
8145
 
7383
8146
  // ipcRenderer that must be used to invoke the events
7384
- const { ipcRenderer: ipcRenderer$5 } = require$$0;
8147
+ const { ipcRenderer: ipcRenderer$6 } = require$$0;
7385
8148
 
7386
8149
  const {
7387
8150
  ALGOLIA_LIST_INDICES,
@@ -7395,10 +8158,10 @@ const {
7395
8158
 
7396
8159
  const algoliaApi$2 = {
7397
8160
  listIndices: (application) =>
7398
- ipcRenderer$5.invoke(ALGOLIA_LIST_INDICES, application),
8161
+ ipcRenderer$6.invoke(ALGOLIA_LIST_INDICES, application),
7399
8162
 
7400
8163
  browseObjects: (appId, apiKey, indexName) => {
7401
- ipcRenderer$5.invoke(ALGOLIA_BROWSE_OBJECTS, {
8164
+ ipcRenderer$6.invoke(ALGOLIA_BROWSE_OBJECTS, {
7402
8165
  appId,
7403
8166
  apiKey,
7404
8167
  indexName,
@@ -7406,10 +8169,10 @@ const algoliaApi$2 = {
7406
8169
  });
7407
8170
  },
7408
8171
 
7409
- saveSynonyms: () => ipcRenderer$5.invoke(ALGOLIA_SAVE_SYNONYMS, {}),
8172
+ saveSynonyms: () => ipcRenderer$6.invoke(ALGOLIA_SAVE_SYNONYMS, {}),
7410
8173
 
7411
8174
  getAnalyticsForQuery: (application, indexName, query) =>
7412
- ipcRenderer$5.invoke(ALGOLIA_ANALYTICS_FOR_QUERY, {
8175
+ ipcRenderer$6.invoke(ALGOLIA_ANALYTICS_FOR_QUERY, {
7413
8176
  application,
7414
8177
  indexName,
7415
8178
  query,
@@ -7422,7 +8185,7 @@ const algoliaApi$2 = {
7422
8185
  dir,
7423
8186
  createIfNotExists = false,
7424
8187
  ) =>
7425
- ipcRenderer$5.invoke(ALGOLIA_PARTIAL_UPDATE_OBJECTS, {
8188
+ ipcRenderer$6.invoke(ALGOLIA_PARTIAL_UPDATE_OBJECTS, {
7426
8189
  appId,
7427
8190
  apiKey,
7428
8191
  indexName,
@@ -7431,7 +8194,7 @@ const algoliaApi$2 = {
7431
8194
  }),
7432
8195
 
7433
8196
  createBatchesFromFile: (filepath, batchFilepath, batchSize) => {
7434
- ipcRenderer$5.invoke(ALGOLIA_CREATE_BATCH, {
8197
+ ipcRenderer$6.invoke(ALGOLIA_CREATE_BATCH, {
7435
8198
  filepath,
7436
8199
  batchFilepath,
7437
8200
  batchSize,
@@ -7439,7 +8202,7 @@ const algoliaApi$2 = {
7439
8202
  },
7440
8203
 
7441
8204
  browseObjectsToFile: (appId, apiKey, indexName, toFilename, query = "") => {
7442
- ipcRenderer$5.invoke(ALGOLIA_BROWSE_OBJECTS, {
8205
+ ipcRenderer$6.invoke(ALGOLIA_BROWSE_OBJECTS, {
7443
8206
  appId,
7444
8207
  apiKey,
7445
8208
  indexName,
@@ -7449,7 +8212,7 @@ const algoliaApi$2 = {
7449
8212
  },
7450
8213
 
7451
8214
  search: (appId, apiKey, indexName, query = "", options = {}) =>
7452
- ipcRenderer$5.invoke(ALGOLIA_SEARCH, {
8215
+ ipcRenderer$6.invoke(ALGOLIA_SEARCH, {
7453
8216
  appId,
7454
8217
  apiKey,
7455
8218
  indexName,
@@ -7464,14 +8227,14 @@ var algoliaApi_1 = algoliaApi$2;
7464
8227
  * openAI
7465
8228
  */
7466
8229
 
7467
- const { ipcRenderer: ipcRenderer$4 } = require$$0;
8230
+ const { ipcRenderer: ipcRenderer$5 } = require$$0;
7468
8231
 
7469
8232
  const { OPENAI_DESCRIBE_IMAGE } = openaiEvents$1;
7470
8233
 
7471
8234
  const openaiApi$2 = {
7472
8235
  // convert a json array of objects to a csv string and save to file
7473
8236
  describeImage: (imageUrl, apiKey, prompt = "What's in this image?") =>
7474
- ipcRenderer$4.invoke(OPENAI_DESCRIBE_IMAGE, { imageUrl, apiKey, prompt }),
8237
+ ipcRenderer$5.invoke(OPENAI_DESCRIBE_IMAGE, { imageUrl, apiKey, prompt }),
7475
8238
  };
7476
8239
 
7477
8240
  var openaiApi_1 = openaiApi$2;
@@ -7482,14 +8245,14 @@ var openaiApi_1 = openaiApi$2;
7482
8245
  */
7483
8246
 
7484
8247
  // ipcRenderer that must be used to invoke the events
7485
- const { ipcRenderer: ipcRenderer$3 } = require$$0;
8248
+ const { ipcRenderer: ipcRenderer$4 } = require$$0;
7486
8249
 
7487
8250
  const { MENU_ITEMS_SAVE, MENU_ITEMS_LIST } = events$8;
7488
8251
 
7489
8252
  const menuItemsApi$2 = {
7490
8253
  saveMenuItem: (appId, menuItem) =>
7491
- ipcRenderer$3.invoke(MENU_ITEMS_SAVE, { appId, menuItem }),
7492
- listMenuItems: (appId) => ipcRenderer$3.invoke(MENU_ITEMS_LIST, { appId }),
8254
+ ipcRenderer$4.invoke(MENU_ITEMS_SAVE, { appId, menuItem }),
8255
+ listMenuItems: (appId) => ipcRenderer$4.invoke(MENU_ITEMS_LIST, { appId }),
7493
8256
  };
7494
8257
 
7495
8258
  var menuItemsApi_1 = menuItemsApi$2;
@@ -7501,16 +8264,143 @@ var menuItemsApi_1 = menuItemsApi$2;
7501
8264
  */
7502
8265
 
7503
8266
  // ipcRenderer that must be used to invoke the events
7504
- const { ipcRenderer: ipcRenderer$2 } = require$$0;
8267
+ const { ipcRenderer: ipcRenderer$3 } = require$$0;
7505
8268
 
7506
8269
  const pluginApi$2 = {
7507
8270
  install: (packageName, filepath) =>
7508
- ipcRenderer$2.invoke("plugin-install", { packageName, filepath }),
7509
- uninstall: (filepath) => ipcRenderer$2.invoke("plugin-uninstall", filepath),
8271
+ ipcRenderer$3.invoke("plugin-install", { packageName, filepath }),
8272
+ uninstall: (filepath) => ipcRenderer$3.invoke("plugin-uninstall", filepath),
7510
8273
  };
7511
8274
 
7512
8275
  var pluginApi_1 = pluginApi$2;
7513
8276
 
8277
+ /**
8278
+ * llmApi.js
8279
+ *
8280
+ * Preload bridge for LLM chat operations.
8281
+ * Communicates with main process via IPC for streaming chat,
8282
+ * tool-use events, and request cancellation.
8283
+ */
8284
+
8285
+ const { ipcRenderer: ipcRenderer$2 } = require$$0;
8286
+ const {
8287
+ LLM_SEND_MESSAGE,
8288
+ LLM_ABORT_REQUEST,
8289
+ LLM_LIST_CONNECTED_TOOLS,
8290
+ LLM_CHECK_CLI_AVAILABLE,
8291
+ LLM_CLEAR_CLI_SESSION,
8292
+ LLM_STREAM_DELTA,
8293
+ LLM_STREAM_TOOL_CALL,
8294
+ LLM_STREAM_TOOL_RESULT,
8295
+ LLM_STREAM_COMPLETE,
8296
+ LLM_STREAM_ERROR,
8297
+ } = llmEvents$1;
8298
+
8299
+ const llmApi$2 = {
8300
+ /**
8301
+ * sendMessage
8302
+ * Start a streaming LLM request with optional tool-use.
8303
+ *
8304
+ * @param {string} requestId - unique ID for filtering stream events
8305
+ * @param {object} params - { apiKey, model, messages, tools, toolServerMap, systemPrompt, maxToolRounds }
8306
+ * @returns {Promise<void>}
8307
+ */
8308
+ sendMessage: (requestId, params) =>
8309
+ ipcRenderer$2.invoke(LLM_SEND_MESSAGE, { requestId, ...params }),
8310
+
8311
+ /**
8312
+ * abortRequest
8313
+ * Cancel an in-flight LLM request.
8314
+ *
8315
+ * @param {string} requestId - the request to cancel
8316
+ * @returns {Promise<{ success: boolean }>}
8317
+ */
8318
+ abortRequest: (requestId) =>
8319
+ ipcRenderer$2.invoke(LLM_ABORT_REQUEST, { requestId }),
8320
+
8321
+ /**
8322
+ * listConnectedTools
8323
+ * Get all tools from all connected MCP servers.
8324
+ *
8325
+ * @returns {Promise<Array<{ serverName, tools, resources, status }>>}
8326
+ */
8327
+ listConnectedTools: () => ipcRenderer$2.invoke(LLM_LIST_CONNECTED_TOOLS),
8328
+
8329
+ /**
8330
+ * checkCliAvailable
8331
+ * Check if the Claude Code CLI is installed and accessible.
8332
+ *
8333
+ * @returns {Promise<{ available: boolean, path?: string }>}
8334
+ */
8335
+ checkCliAvailable: () => ipcRenderer$2.invoke(LLM_CHECK_CLI_AVAILABLE),
8336
+
8337
+ /**
8338
+ * clearCliSession
8339
+ * Clear the CLI conversation session for a widget (for "New Chat").
8340
+ *
8341
+ * @param {string} widgetUuid - the widget whose session to clear
8342
+ * @returns {Promise<{ success: boolean }>}
8343
+ */
8344
+ clearCliSession: (widgetUuid) =>
8345
+ ipcRenderer$2.invoke(LLM_CLEAR_CLI_SESSION, { widgetUuid }),
8346
+
8347
+ // --- Stream event listeners ---
8348
+
8349
+ /**
8350
+ * onStreamDelta
8351
+ * Listen for text chunks as they stream in.
8352
+ */
8353
+ onStreamDelta: (callback) => {
8354
+ ipcRenderer$2.on(LLM_STREAM_DELTA, (_event, data) => callback(data));
8355
+ },
8356
+
8357
+ /**
8358
+ * onStreamToolCall
8359
+ * Listen for tool call notifications.
8360
+ */
8361
+ onStreamToolCall: (callback) => {
8362
+ ipcRenderer$2.on(LLM_STREAM_TOOL_CALL, (_event, data) => callback(data));
8363
+ },
8364
+
8365
+ /**
8366
+ * onStreamToolResult
8367
+ * Listen for tool result notifications.
8368
+ */
8369
+ onStreamToolResult: (callback) => {
8370
+ ipcRenderer$2.on(LLM_STREAM_TOOL_RESULT, (_event, data) => callback(data));
8371
+ },
8372
+
8373
+ /**
8374
+ * onStreamComplete
8375
+ * Listen for stream completion (final response).
8376
+ */
8377
+ onStreamComplete: (callback) => {
8378
+ ipcRenderer$2.on(LLM_STREAM_COMPLETE, (_event, data) => callback(data));
8379
+ },
8380
+
8381
+ /**
8382
+ * onStreamError
8383
+ * Listen for stream errors.
8384
+ */
8385
+ onStreamError: (callback) => {
8386
+ ipcRenderer$2.on(LLM_STREAM_ERROR, (_event, data) => callback(data));
8387
+ },
8388
+
8389
+ /**
8390
+ * removeAllStreamListeners
8391
+ * Clean up all LLM stream listeners.
8392
+ */
8393
+ removeAllStreamListeners: () => {
8394
+ ipcRenderer$2.removeAllListeners(LLM_STREAM_DELTA);
8395
+ ipcRenderer$2.removeAllListeners(LLM_STREAM_TOOL_CALL);
8396
+ ipcRenderer$2.removeAllListeners(LLM_STREAM_TOOL_RESULT);
8397
+ ipcRenderer$2.removeAllListeners(LLM_STREAM_COMPLETE);
8398
+ ipcRenderer$2.removeAllListeners(LLM_STREAM_ERROR);
8399
+ },
8400
+ };
8401
+
8402
+ var llmApi_1 = llmApi$2;
8403
+
7514
8404
  /**
7515
8405
  * clientCacheApi.js
7516
8406
  *
@@ -8964,6 +9854,7 @@ const algoliaApi$1 = algoliaApi_1;
8964
9854
  const openaiApi$1 = openaiApi_1;
8965
9855
  const menuItemsApi$1 = menuItemsApi_1;
8966
9856
  const pluginApi$1 = pluginApi_1;
9857
+ const llmApi$1 = llmApi_1;
8967
9858
  const clientCacheApi$1 = clientCacheApi_1;
8968
9859
 
8969
9860
  // Events constants
@@ -9027,6 +9918,9 @@ function createMainApi$1(extensions = {}) {
9027
9918
 
9028
9919
  pathPlugins: "",
9029
9920
 
9921
+ // LLM
9922
+ llm: llmApi$1,
9923
+
9030
9924
  // APIs previously in template
9031
9925
  algolia: algoliaApi$1,
9032
9926
  openai: openaiApi$1,
@@ -9076,6 +9970,8 @@ const algoliaController = algoliaController_1;
9076
9970
  const openaiController = openaiController_1;
9077
9971
  const menuItemsController = menuItemsController_1;
9078
9972
  const pluginController = pluginController_1;
9973
+ const llmController = llmController_1;
9974
+ const cliController = cliController_1;
9079
9975
 
9080
9976
  // --- Utils ---
9081
9977
  const clientCache = requireClientCache();
@@ -9101,6 +9997,7 @@ const algoliaApi = algoliaApi_1;
9101
9997
  const openaiApi = openaiApi_1;
9102
9998
  const menuItemsApi = menuItemsApi_1;
9103
9999
  const pluginApi = pluginApi_1;
10000
+ const llmApi = llmApi_1;
9104
10001
  const clientCacheApi = clientCacheApi_1;
9105
10002
 
9106
10003
  // --- Events ---
@@ -9130,6 +10027,8 @@ var electron = {
9130
10027
  openaiController,
9131
10028
  menuItemsController,
9132
10029
  pluginController,
10030
+ llmController,
10031
+ cliController,
9133
10032
 
9134
10033
  // Controller functions (flat) — spread for convenient destructuring
9135
10034
  ...controllers,
@@ -9150,6 +10049,7 @@ var electron = {
9150
10049
  openaiApi,
9151
10050
  menuItemsApi,
9152
10051
  pluginApi,
10052
+ llmApi,
9153
10053
  clientCacheApi,
9154
10054
 
9155
10055
  // Events