turbollm 1.5.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +4 -2
  2. package/dist/cli.js +96 -77
  3. package/dist/webdist/assets/{AgentsScreen-8OttuI7h.js → AgentsScreen-qd7_yqju.js} +1 -1
  4. package/dist/webdist/assets/{ChatScreen-CfBmxIt8.js → ChatScreen-DYeJQoLV.js} +5 -5
  5. package/dist/webdist/assets/CustomizeScreen-DBXBuQPm.js +4 -0
  6. package/dist/webdist/assets/DeveloperScreen-CCWBQQ4y.js +1 -0
  7. package/dist/webdist/assets/{EnginesScreen-CRI2LrjT.js → EnginesScreen-DQQLI0Qv.js} +6 -6
  8. package/dist/webdist/assets/{ModelDetailDialog-C4BsJs7p.js → ModelDetailDialog-C-dRTp-J.js} +1 -1
  9. package/dist/webdist/assets/{ModelDirs-DCeaDz1y.js → ModelDirs-E_tFMIjS.js} +1 -1
  10. package/dist/webdist/assets/ModelsScreen-JeCIZIdr.js +1 -0
  11. package/dist/webdist/assets/{SettingsScreen-CSlU6sWv.js → SettingsScreen-BFhQBO1d.js} +1 -1
  12. package/dist/webdist/assets/{arc-DjTUhjoA.js → arc-820YYJ1l.js} +1 -1
  13. package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-DwDhf7SF.js → architectureDiagram-3BPJPVTR-BTSJZjgr.js} +1 -1
  14. package/dist/webdist/assets/arrow-left-BJXqu8NS.js +1 -0
  15. package/dist/webdist/assets/{blockDiagram-GPEHLZMM-Bu71X43M.js → blockDiagram-GPEHLZMM-E_gowUIA.js} +1 -1
  16. package/dist/webdist/assets/{c4Diagram-AAUBKEIU-BXcC49Vg.js → c4Diagram-AAUBKEIU-DwJcumW7.js} +1 -1
  17. package/dist/webdist/assets/channel-6NEG4b4S.js +1 -0
  18. package/dist/webdist/assets/check-GYT4Bnhm.js +1 -0
  19. package/dist/webdist/assets/chevron-right-BTg0-ylt.js +1 -0
  20. package/dist/webdist/assets/{chunk-2J33WTMH-Nh_29xmy.js → chunk-2J33WTMH-DWJ9qmM_.js} +1 -1
  21. package/dist/webdist/assets/{chunk-4BX2VUAB-BfUMrwIP.js → chunk-4BX2VUAB-DgyEqU31.js} +1 -1
  22. package/dist/webdist/assets/{chunk-55IACEB6-BxCAqEO_.js → chunk-55IACEB6-CsI85v4r.js} +1 -1
  23. package/dist/webdist/assets/{chunk-727SXJPM-Bc6NhPVZ.js → chunk-727SXJPM-YRdSK3U3.js} +1 -1
  24. package/dist/webdist/assets/{chunk-AQP2D5EJ-ClNqClqF.js → chunk-AQP2D5EJ-BnTfokPI.js} +1 -1
  25. package/dist/webdist/assets/{chunk-FMBD7UC4-ICYO5OUp.js → chunk-FMBD7UC4-Dt8O7iM6.js} +1 -1
  26. package/dist/webdist/assets/{chunk-ND2GUHAM-DxZN4K23.js → chunk-ND2GUHAM-OsM9VDgQ.js} +1 -1
  27. package/dist/webdist/assets/{chunk-QZHKN3VN-CtQVNvJX.js → chunk-QZHKN3VN-CW3L84fX.js} +1 -1
  28. package/dist/webdist/assets/{circle-check-CR9l1s2L.js → circle-check-iwGQW5-3.js} +1 -1
  29. package/dist/webdist/assets/{circle-x-ED1-1ibi.js → circle-x-CDfcuELr.js} +1 -1
  30. package/dist/webdist/assets/classDiagram-4FO5ZUOK-jpAzq28n.js +1 -0
  31. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-jpAzq28n.js +1 -0
  32. package/dist/webdist/assets/{common-CaH63tS-.js → common-BstanhE0.js} +1 -1
  33. package/dist/webdist/assets/{copy-button-C5VRk_o-.js → copy-button-DrRexlJn.js} +1 -1
  34. package/dist/webdist/assets/{cose-bilkent-S5V4N54A-CORAJAXp.js → cose-bilkent-S5V4N54A-cNqjZyGk.js} +1 -1
  35. package/dist/webdist/assets/{dagre-BM42HDAG-BtWY5mpY.js → dagre-BM42HDAG-zr_aSu4P.js} +1 -1
  36. package/dist/webdist/assets/{diagram-2AECGRRQ-DsQUpPv-.js → diagram-2AECGRRQ-D8fYvWd0.js} +1 -1
  37. package/dist/webdist/assets/{diagram-5GNKFQAL-DgtsKEO_.js → diagram-5GNKFQAL-Cp2jBV3j.js} +1 -1
  38. package/dist/webdist/assets/{diagram-KO2AKTUF-CiNG347O.js → diagram-KO2AKTUF-SdWp_DPO.js} +1 -1
  39. package/dist/webdist/assets/{diagram-LMA3HP47-Br0x0kkW.js → diagram-LMA3HP47-DHwQz9MP.js} +1 -1
  40. package/dist/webdist/assets/{diagram-OG6HWLK6-aTkPDelf.js → diagram-OG6HWLK6-FGejDPqJ.js} +1 -1
  41. package/dist/webdist/assets/dialog-Cpwjl0SL.js +1 -0
  42. package/dist/webdist/assets/{erDiagram-TEJ5UH35-ByL7kkdh.js → erDiagram-TEJ5UH35-Ci0ArYoY.js} +1 -1
  43. package/dist/webdist/assets/{flowDiagram-I6XJVG4X-C7jVJZbe.js → flowDiagram-I6XJVG4X-g96T9cRZ.js} +1 -1
  44. package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-kahXGkyv.js → ganttDiagram-6RSMTGT7-CFGWWtss.js} +1 -1
  45. package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-CtKjbqTV.js → gitGraphDiagram-PVQCEYII-FFeX8xGw.js} +1 -1
  46. package/dist/webdist/assets/{index-DQgAgJpy.css → index-CJj8sGhF.css} +1 -1
  47. package/dist/webdist/assets/{index-aRpf26fA.js → index-CM4wnT7v.js} +2 -2
  48. package/dist/webdist/assets/{infoDiagram-5YYISTIA-KG2oeaMN.js → infoDiagram-5YYISTIA-CTuVSOzN.js} +1 -1
  49. package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-CH0hIyAm.js → ishikawaDiagram-YF4QCWOH-BMyIM99h.js} +1 -1
  50. package/dist/webdist/assets/{journeyDiagram-JHISSGLW-DHcRpFXW.js → journeyDiagram-JHISSGLW-CcKLWYl8.js} +1 -1
  51. package/dist/webdist/assets/{kanban-definition-UN3LZRKU-BwNZnCjT.js → kanban-definition-UN3LZRKU-CuzVk9Q_.js} +1 -1
  52. package/dist/webdist/assets/{linear-CFNabJkq.js → linear-DvYrxcXL.js} +1 -1
  53. package/dist/webdist/assets/{mermaid.core-D_XNYY97.js → mermaid.core-DIGvaW9x.js} +4 -4
  54. package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-5Frzs0Fa.js → mindmap-definition-RKZ34NQL-Dh_7fzN2.js} +1 -1
  55. package/dist/webdist/assets/{pencil-C9dBz8-u.js → pencil-veezqsms.js} +1 -1
  56. package/dist/webdist/assets/{personas-BRFNty6U.js → personas-DdUgL1dH.js} +3 -2
  57. package/dist/webdist/assets/{pieDiagram-4H26LBE5-DfjGFU8f.js → pieDiagram-4H26LBE5-G_BsYb0k.js} +1 -1
  58. package/dist/webdist/assets/{plus-CBiMXB28.js → plus-BLEwDzPR.js} +1 -1
  59. package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-BVmu899Y.js → quadrantDiagram-W4KKPZXB-B1DekSy3.js} +1 -1
  60. package/dist/webdist/assets/{refresh-cw-DzuecN3W.js → refresh-cw-ByeAabM7.js} +1 -1
  61. package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-BqBVvvpq.js → requirementDiagram-4Y6WPE33-DPYkFc77.js} +1 -1
  62. package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-ieJbgOGC.js → sankeyDiagram-5OEKKPKP-HfCQTOkK.js} +1 -1
  63. package/dist/webdist/assets/{save-EjedjVdP.js → save-CuyovDOw.js} +1 -1
  64. package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-C_ciRbLo.js → sequenceDiagram-3UESZ5HK-C71xQD9L.js} +1 -1
  65. package/dist/webdist/assets/{skeleton-DRSPFN2X.js → skeleton-XeFkC2WF.js} +1 -1
  66. package/dist/webdist/assets/{stateDiagram-AJRCARHV-BdPdoXlm.js → stateDiagram-AJRCARHV-C0m1fR_s.js} +1 -1
  67. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-C1sHKz6d.js +1 -0
  68. package/dist/webdist/assets/{timeline-definition-PNZ67QCA-CR9Y6hB5.js → timeline-definition-PNZ67QCA-B5SB89Kk.js} +1 -1
  69. package/dist/webdist/assets/{trash-2-DGDj2PvU.js → trash-2-MMGQet84.js} +1 -1
  70. package/dist/webdist/assets/{vennDiagram-CIIHVFJN-DtGAe7_y.js → vennDiagram-CIIHVFJN-Bs3E_UnU.js} +1 -1
  71. package/dist/webdist/assets/{wardley-L42UT6IY-42TAPuTN.js → wardley-L42UT6IY-D-lZEV6Q.js} +1 -1
  72. package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-BGz-N75m.js → wardleyDiagram-YWT4CUSO-DIiAQmgm.js} +1 -1
  73. package/dist/webdist/assets/{wrench-TCdFNEP4.js → wrench-C7hemMKG.js} +1 -1
  74. package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-st1-aHMk.js → xychartDiagram-2RQKCTM6-DViSTAkf.js} +1 -1
  75. package/dist/webdist/index.html +2 -2
  76. package/package.json +1 -1
  77. package/dist/webdist/assets/CustomizeScreen-B6ZmStT4.js +0 -4
  78. package/dist/webdist/assets/DeveloperScreen-K4D_3xic.js +0 -1
  79. package/dist/webdist/assets/ModelsScreen-AuNKEh06.js +0 -1
  80. package/dist/webdist/assets/channel-CPA4-Zpk.js +0 -1
  81. package/dist/webdist/assets/check-DYBCD2_I.js +0 -1
  82. package/dist/webdist/assets/classDiagram-4FO5ZUOK-Dkqtv6MY.js +0 -1
  83. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-Dkqtv6MY.js +0 -1
  84. package/dist/webdist/assets/dialog-XV4KKoeX.js +0 -1
  85. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-DVgN1d61.js +0 -1
package/README.md CHANGED
@@ -194,8 +194,10 @@ the detail:
194
194
  - **Image input** for vision models, and **TurboLLM Expert** — a built-in assistant that knows
195
195
  the app and your hardware for onboarding and troubleshooting without leaving the UI.
196
196
  - **Agentic tools** — built-in `web_search` (Tavily), `fetch_url`, and sandboxed `run_code`, plus
197
- **MCP server support** (stdio / SSE) so any MCP server's tools appear in every chat. A **Research**
198
- persona forces multi-step web search and cites sources inline.
197
+ an **MCP marketplace** in Customize: one-click connect for hosted MCPs (GitHub, Linear, Stripe,
198
+ Atlassian, Neon, Supabase, Cloudflare, Zapier, Apify, Mixpanel) and open-source local MCPs
199
+ (filesystem, git, postgres, playwright, …), plus your own custom servers. Connected tools appear
200
+ in every chat with no restart. A **Research** persona forces multi-step web search and cites sources inline.
199
201
 
200
202
  </details>
201
203
 
package/dist/cli.js CHANGED
@@ -6412,7 +6412,6 @@ function execRunCode(args, requireConfirmation = false) {
6412
6412
 
6413
6413
  // src/tools/mcp-client.ts
6414
6414
  import { spawn as spawn2 } from "child_process";
6415
- import { EventEmitter } from "events";
6416
6415
  var StdioMcpClient = class {
6417
6416
  serverId;
6418
6417
  serverName;
@@ -6433,7 +6432,10 @@ var StdioMcpClient = class {
6433
6432
  }
6434
6433
  async connect() {
6435
6434
  if (this.proc) return;
6436
- this.proc = spawn2(this.command, this.args, {
6435
+ const isWin = process.platform === "win32";
6436
+ const file = isWin ? "cmd.exe" : this.command;
6437
+ const args = isWin ? ["/c", this.command, ...this.args] : this.args;
6438
+ this.proc = spawn2(file, args, {
6437
6439
  env: { ...process.env, ...this.env },
6438
6440
  stdio: ["pipe", "pipe", "pipe"]
6439
6441
  });
@@ -6527,70 +6529,28 @@ var SseMcpClient = class {
6527
6529
  serverId;
6528
6530
  serverName;
6529
6531
  baseUrl;
6532
+ headers;
6530
6533
  msgId = 0;
6531
- sseEndpoint = "";
6532
- sseAbort = null;
6533
- emitter = new EventEmitter();
6534
+ sessionId = null;
6534
6535
  initialized = false;
6535
6536
  constructor(opts) {
6536
6537
  this.serverId = opts.id;
6537
6538
  this.serverName = opts.name;
6538
6539
  this.baseUrl = opts.url.replace(/\/$/, "");
6540
+ this.headers = opts.headers ?? {};
6539
6541
  }
6540
6542
  async connect() {
6541
6543
  if (this.initialized) return;
6542
- this.sseAbort = new AbortController();
6543
- const endpoint = await new Promise((resolve2, reject) => {
6544
- const timer = setTimeout(() => reject(new Error("SSE connect timeout")), 15e3);
6545
- fetch(`${this.baseUrl}/sse`, { signal: this.sseAbort.signal }).then(async (resp) => {
6546
- if (!resp.ok || !resp.body) {
6547
- clearTimeout(timer);
6548
- reject(new Error(`SSE connect failed: ${resp.status}`));
6549
- return;
6550
- }
6551
- const reader = resp.body.getReader();
6552
- const decoder = new TextDecoder();
6553
- let buf = "";
6554
- let resolved = false;
6555
- while (true) {
6556
- const { done, value } = await reader.read();
6557
- if (done) break;
6558
- buf += decoder.decode(value, { stream: true });
6559
- const lines = buf.split("\n");
6560
- buf = lines.pop() ?? "";
6561
- for (const line of lines) {
6562
- if (line.startsWith("data:")) {
6563
- const data = line.slice(5).trim();
6564
- if (!resolved) {
6565
- resolved = true;
6566
- clearTimeout(timer);
6567
- resolve2(data);
6568
- }
6569
- try {
6570
- const msg2 = JSON.parse(data);
6571
- if (msg2.id != null) this.emitter.emit(`rpc:${msg2.id}`, msg2);
6572
- } catch {
6573
- }
6574
- }
6575
- }
6576
- }
6577
- }).catch((e) => {
6578
- clearTimeout(timer);
6579
- if (!this.sseAbort?.signal.aborted) reject(e);
6580
- });
6581
- });
6582
- this.sseEndpoint = endpoint.startsWith("http") ? endpoint : `${this.baseUrl}${endpoint}`;
6583
6544
  await this.sendRequest("initialize", {
6584
6545
  protocolVersion: "2024-11-05",
6585
6546
  capabilities: { tools: {} },
6586
6547
  clientInfo: { name: "turbollm", version: "0.7.0" }
6587
6548
  });
6588
- await this.post({ jsonrpc: "2.0", method: "notifications/initialized", params: {} });
6549
+ void this.postNotification({ jsonrpc: "2.0", method: "notifications/initialized", params: {} });
6589
6550
  this.initialized = true;
6590
6551
  }
6591
6552
  disconnect() {
6592
- this.sseAbort?.abort();
6593
- this.sseAbort = null;
6553
+ this.sessionId = null;
6594
6554
  this.initialized = false;
6595
6555
  }
6596
6556
  async listTools() {
@@ -6607,36 +6567,79 @@ var SseMcpClient = class {
6607
6567
  async sendRequest(method, params) {
6608
6568
  const id = ++this.msgId;
6609
6569
  const req = { jsonrpc: "2.0", id, method, params };
6610
- return new Promise((resolve2, reject) => {
6611
- const timer = setTimeout(() => {
6612
- this.emitter.removeAllListeners(`rpc:${id}`);
6613
- reject(new Error(`MCP SSE request timeout: ${method}`));
6614
- }, 3e4);
6615
- this.emitter.once(`rpc:${id}`, (msg2) => {
6616
- clearTimeout(timer);
6617
- if (msg2.error) reject(new Error(msg2.error.message));
6618
- else resolve2(msg2.result);
6619
- });
6620
- this.post(req).catch((e) => {
6621
- clearTimeout(timer);
6622
- reject(e);
6623
- });
6570
+ const reqHeaders = {
6571
+ "Content-Type": "application/json",
6572
+ "Accept": "application/json, text/event-stream",
6573
+ ...this.headers
6574
+ };
6575
+ if (this.sessionId) reqHeaders["Mcp-Session-Id"] = this.sessionId;
6576
+ const resp = await fetch(this.baseUrl, {
6577
+ method: "POST",
6578
+ headers: reqHeaders,
6579
+ body: JSON.stringify(req),
6580
+ signal: AbortSignal.timeout(3e4)
6624
6581
  });
6582
+ if (!resp.ok) {
6583
+ const txt = await resp.text().catch(() => "");
6584
+ throw new Error(`MCP HTTP ${resp.status}: ${txt.slice(0, 200)}`);
6585
+ }
6586
+ const newSession = resp.headers.get("mcp-session-id");
6587
+ if (newSession) this.sessionId = newSession;
6588
+ const ct = resp.headers.get("content-type") ?? "";
6589
+ if (ct.includes("text/event-stream")) return this.readSSEResult(resp, id);
6590
+ const json = await resp.json();
6591
+ if (json.error) throw new Error(json.error.message);
6592
+ return json.result;
6593
+ }
6594
+ async readSSEResult(resp, targetId) {
6595
+ if (!resp.body) throw new Error("SSE response body is null");
6596
+ const reader = resp.body.getReader();
6597
+ const dec = new TextDecoder();
6598
+ let buf = "";
6599
+ try {
6600
+ while (true) {
6601
+ const { done, value } = await reader.read();
6602
+ if (done) break;
6603
+ buf += dec.decode(value, { stream: true });
6604
+ const lines = buf.split("\n");
6605
+ buf = lines.pop() ?? "";
6606
+ for (const line of lines) {
6607
+ if (!line.startsWith("data:")) continue;
6608
+ let msg2;
6609
+ try {
6610
+ msg2 = JSON.parse(line.slice(5).trim());
6611
+ } catch {
6612
+ continue;
6613
+ }
6614
+ if (msg2.id === targetId) {
6615
+ if (msg2.error) throw new Error(msg2.error.message);
6616
+ return msg2.result;
6617
+ }
6618
+ }
6619
+ }
6620
+ } finally {
6621
+ await reader.cancel().catch(() => {
6622
+ });
6623
+ }
6624
+ throw new Error(`SSE stream ended without response for id ${targetId}`);
6625
6625
  }
6626
- async post(msg2) {
6627
- const url = this.sseEndpoint || `${this.baseUrl}/messages`;
6628
- await fetch(url, {
6626
+ async postNotification(msg2) {
6627
+ const headers = { "Content-Type": "application/json", ...this.headers };
6628
+ if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
6629
+ await fetch(this.baseUrl, {
6629
6630
  method: "POST",
6630
- headers: { "Content-Type": "application/json" },
6631
+ headers,
6631
6632
  body: JSON.stringify(msg2),
6632
6633
  signal: AbortSignal.timeout(1e4)
6634
+ }).catch(() => {
6633
6635
  });
6634
6636
  }
6635
6637
  };
6636
6638
  function createMcpClient(server2) {
6637
6639
  if (server2.transport === "sse") {
6638
6640
  if (!server2.url) throw new Error(`MCP server "${server2.name}" has transport=sse but no url`);
6639
- return new SseMcpClient({ id: server2.id, name: server2.name, url: server2.url });
6641
+ const headers = server2.apiKey ? { Authorization: `Bearer ${server2.apiKey}` } : void 0;
6642
+ return new SseMcpClient({ id: server2.id, name: server2.name, url: server2.url, headers });
6640
6643
  }
6641
6644
  if (!server2.command) throw new Error(`MCP server "${server2.name}" has transport=stdio but no command`);
6642
6645
  return new StdioMcpClient({
@@ -6776,7 +6779,7 @@ var GenerationGate = class {
6776
6779
  };
6777
6780
 
6778
6781
  // src/agents/runner.ts
6779
- import { EventEmitter as EventEmitter2 } from "events";
6782
+ import { EventEmitter } from "events";
6780
6783
 
6781
6784
  // src/chat/parser.ts
6782
6785
  var THINK_OPEN = "<think>";
@@ -7551,7 +7554,7 @@ var AgentRunner = class {
7551
7554
  const pending = this.queue.shift();
7552
7555
  this.active = pending.id;
7553
7556
  const buffer = new RunBuffer();
7554
- const emitter = new EventEmitter2();
7557
+ const emitter = new EventEmitter();
7555
7558
  emitter.setMaxListeners(50);
7556
7559
  const ac = new AbortController();
7557
7560
  this.buffers.set(pending.id, buffer);
@@ -11131,6 +11134,7 @@ function registerApi(app2, d) {
11131
11134
  });
11132
11135
  if (b.tavilyApiKey !== void 0 || b.search !== void 0) {
11133
11136
  d.tools?.updateConfig(d.store.snapshot().tools);
11137
+ await d.tools?.buildToolDefinitions();
11134
11138
  }
11135
11139
  const after = d.store.snapshot().daemon;
11136
11140
  const lanChanged = after.lanBind !== before.lanBind;
@@ -11156,14 +11160,22 @@ function registerApi(app2, d) {
11156
11160
  name: b.name.trim(),
11157
11161
  transport,
11158
11162
  enabled: b.enabled !== false,
11159
- ...transport === "stdio" ? { command: b.command.trim(), args: b.args ?? [], env: b.env ?? {} } : { url: b.url.trim() }
11163
+ ...transport === "stdio" ? { command: b.command.trim(), args: b.args ?? [], env: b.env ?? {} } : { url: b.url.trim(), ...b.apiKey ? { apiKey: b.apiKey } : {} }
11160
11164
  };
11165
+ const isMemoryServer = server2.transport === "stdio" && (server2.command?.includes("server-memory") || server2.args?.some((a) => a.includes("server-memory")));
11166
+ if (isMemoryServer && !server2.env?.MEMORY_FILE_PATH) {
11167
+ server2.env = { ...server2.env, MEMORY_FILE_PATH: join22(homedir3(), ".turbollm", "mcp-memory.jsonl") };
11168
+ }
11161
11169
  d.store.update((cfg2) => {
11162
11170
  cfg2.mcp.servers.push(server2);
11163
11171
  });
11164
- if (server2.enabled) await d.tools?.syncMcpServers(d.store.snapshot().mcp.servers).catch(() => {
11165
- });
11166
- return c.json(server2, 201);
11172
+ if (server2.enabled) {
11173
+ await d.tools?.syncMcpServers(d.store.snapshot().mcp.servers).catch(() => {
11174
+ });
11175
+ await d.tools?.buildToolDefinitions();
11176
+ }
11177
+ const { apiKey: _created, ...safeServer } = server2;
11178
+ return c.json(safeServer, 201);
11167
11179
  });
11168
11180
  app2.put("/api/v1/mcp/servers/:id", async (c) => {
11169
11181
  const id = c.req.param("id");
@@ -11181,12 +11193,16 @@ function registerApi(app2, d) {
11181
11193
  if (b.args !== void 0) s.args = b.args;
11182
11194
  if (b.env !== void 0) s.env = b.env;
11183
11195
  if (b.url !== void 0) s.url = b.url;
11196
+ if (b.apiKey !== void 0) s.apiKey = b.apiKey || void 0;
11184
11197
  });
11185
11198
  await d.tools?.syncMcpServers(d.store.snapshot().mcp.servers).catch(() => {
11186
11199
  });
11187
- return c.json(d.store.snapshot().mcp.servers.find((s) => s.id === id));
11200
+ await d.tools?.buildToolDefinitions();
11201
+ const updated = d.store.snapshot().mcp.servers.find((s) => s.id === id);
11202
+ const { apiKey: _updated, ...safeUpdated } = updated;
11203
+ return c.json(safeUpdated);
11188
11204
  });
11189
- app2.delete("/api/v1/mcp/servers/:id", (c) => {
11205
+ app2.delete("/api/v1/mcp/servers/:id", async (c) => {
11190
11206
  const id = c.req.param("id");
11191
11207
  const cfg2 = d.store.snapshot();
11192
11208
  if (!cfg2.mcp.servers.some((s) => s.id === id)) return err2(c, 404, "not_found", "MCP server not found.");
@@ -11194,6 +11210,7 @@ function registerApi(app2, d) {
11194
11210
  c2.mcp.servers = c2.mcp.servers.filter((s) => s.id !== id);
11195
11211
  });
11196
11212
  void d.tools?.syncMcpServers(d.store.snapshot().mcp.servers);
11213
+ await d.tools?.buildToolDefinitions();
11197
11214
  return c.json({ ok: true });
11198
11215
  });
11199
11216
  app2.get("/api/v1/telemetry/preview", (c) => {
@@ -11446,7 +11463,8 @@ function settingsPayload(d) {
11446
11463
  kagiKeySet: !!cfg2.tools.search?.kagiApiKey,
11447
11464
  searxngUrl: cfg2.tools.search?.searxngUrl ?? ""
11448
11465
  },
11449
- mcp: cfg2.mcp,
11466
+ // apiKey is write-only: strip before echoing so Bearer tokens never reach the frontend.
11467
+ mcp: { ...cfg2.mcp, servers: cfg2.mcp.servers.map(({ apiKey: _k, ...s }) => s) },
11450
11468
  // Build environment (ADR-100): folders prepended to PATH for compile-from-source so a
11451
11469
  // conda-env / custom-path CUDA Toolkit + compiler are found. Not secret — echoed back.
11452
11470
  build: { toolchainDirs: cfg2.build.toolchainDirs }
@@ -12525,6 +12543,7 @@ var toolRegistry = new ToolRegistry(store.snapshot().tools);
12525
12543
  void (async () => {
12526
12544
  const cfg2 = store.snapshot();
12527
12545
  await toolRegistry.syncMcpServers(cfg2.mcp.servers);
12546
+ void toolRegistry.buildToolDefinitions();
12528
12547
  })();
12529
12548
  var startedAt = Date.now();
12530
12549
  var appUpdates = new AppUpdateChecker(version);
@@ -1,2 +1,2 @@
1
- import{j as e}from"./radix-4XwKfE88.js";import{r as u}from"./vendor-CKtmMAvG.js";import{c as w,d as k,A as T,e as v,L as h,f as S,b,g as C,h as q,X as A}from"./index-aRpf26fA.js";import{P as N}from"./plus-CBiMXB28.js";import{C as y}from"./circle-x-ED1-1ibi.js";import{C as O}from"./circle-check-CR9l1s2L.js";import{W as E}from"./wrench-TCdFNEP4.js";const _=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}]],D=w("circle-dot",_);const L=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 6v6l4 2",key:"mmk7yg"}]],P=w("clock",L),p={all:["agent-runs"],list:()=>[...p.all,"list"],detail:t=>[...p.all,"detail",t]};async function g(t,a){const l={Accept:"application/json",...k(),...a?.headers??{}},i=await fetch(t,{...a,headers:l});if(i.status===204)return;const r=await i.text(),o=r?JSON.parse(r):void 0;if(!i.ok){const d=o;throw new T(d?.error?.code??"http_error",d?.error?.message??`Request failed with status ${i.status}.`,i.status)}return o}async function $(){return g("/api/v1/agents/runs")}async function I(t){return g(`/api/v1/agents/runs/${t}`)}async function Q(t){return g("/api/v1/agents/runs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}async function K(t){await g(`/api/v1/agents/runs/${t}`,{method:"DELETE"})}async function*F(t,a=0,l){const i=`/api/v1/agents/runs/${t}/stream?fromSeq=${a}`,r=await fetch(i,{headers:{Accept:"text/event-stream",...k()},signal:l});if(!r.ok||!r.body)throw new Error(`Stream error: ${r.status}`);const o=r.body.getReader(),d=new TextDecoder;let m="";try{for(;;){const{done:s,value:x}=await o.read();if(s)break;m+=d.decode(x,{stream:!0});const c=m.split(`
1
+ import{j as e}from"./radix-4XwKfE88.js";import{r as u}from"./vendor-CKtmMAvG.js";import{c as w,b as k,A as T,u as v,L as h,d as S,a as b,e as C,f as q,X as A}from"./index-CM4wnT7v.js";import{P as N}from"./plus-BLEwDzPR.js";import{C as y}from"./circle-x-CDfcuELr.js";import{C as O}from"./circle-check-iwGQW5-3.js";import{W as E}from"./wrench-C7hemMKG.js";const _=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}]],D=w("circle-dot",_);const L=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 6v6l4 2",key:"mmk7yg"}]],P=w("clock",L),p={all:["agent-runs"],list:()=>[...p.all,"list"],detail:t=>[...p.all,"detail",t]};async function g(t,a){const l={Accept:"application/json",...k(),...a?.headers??{}},i=await fetch(t,{...a,headers:l});if(i.status===204)return;const r=await i.text(),o=r?JSON.parse(r):void 0;if(!i.ok){const d=o;throw new T(d?.error?.code??"http_error",d?.error?.message??`Request failed with status ${i.status}.`,i.status)}return o}async function $(){return g("/api/v1/agents/runs")}async function I(t){return g(`/api/v1/agents/runs/${t}`)}async function Q(t){return g("/api/v1/agents/runs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}async function K(t){await g(`/api/v1/agents/runs/${t}`,{method:"DELETE"})}async function*F(t,a=0,l){const i=`/api/v1/agents/runs/${t}/stream?fromSeq=${a}`,r=await fetch(i,{headers:{Accept:"text/event-stream",...k()},signal:l});if(!r.ok||!r.body)throw new Error(`Stream error: ${r.status}`);const o=r.body.getReader(),d=new TextDecoder;let m="";try{for(;;){const{done:s,value:x}=await o.read();if(s)break;m+=d.decode(x,{stream:!0});const c=m.split(`
2
2
  `);m=c.pop()??"";let n="message";for(const f of c){if(f.startsWith("event: ")){n=f.slice(7).trim();continue}if(f.startsWith("data: ")){const j=f.slice(6).trim();if(j==="[DONE]")return;try{const R=JSON.parse(j);if(yield{event:n,data:R},n==="done"||n==="error")return}catch{}n="message"}}}}finally{o.cancel()}}const J={queued:e.jsx(P,{size:12}),running:e.jsx(h,{size:12,className:"animate-spin"}),done:e.jsx(O,{size:12}),failed:e.jsx(y,{size:12}),cancelled:e.jsx(y,{size:12}),interrupted:e.jsx(y,{size:12})},M={queued:"text-muted",running:"text-accent",done:"text-green-500",failed:"text-red-500",cancelled:"text-muted",interrupted:"text-orange-500"};function z({status:t}){return e.jsxs("span",{className:b("flex items-center gap-1 text-[11px] capitalize",M[t]),children:[J[t],t]})}function W(t){const a=Date.now()-new Date(t).getTime();return a<6e4?"just now":a<36e5?`${Math.floor(a/6e4)}m ago`:a<864e5?`${Math.floor(a/36e5)}h ago`:new Date(t).toLocaleDateString()}const U=[{id:"web_search",label:"Web search",hint:"Search the web (needs a provider configured in Settings)"},{id:"fetch_url",label:"Fetch URL",hint:"Read the contents of a web page"},{id:"run_code",label:"Run code",hint:"Execute JS in a sandbox — no mid-run confirmation, opt in only if you trust the task"}];function B({onClose:t,onCreated:a}){const[l,i]=u.useState(""),[r,o]=u.useState(""),[d,m]=u.useState({web_search:!0,fetch_url:!0,run_code:!1}),s=C(),x=n=>m(f=>({...f,[n]:!f[n]})),c=q({mutationFn:()=>Q({title:l.slice(0,60)||"Agent run",systemPrompt:r||void 0,userMessage:l,allowedTools:Object.entries(d).filter(([,n])=>n).map(([n])=>n)}),onSuccess:n=>{s.invalidateQueries({queryKey:p.list()}),a(n.id),t()}});return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40",onClick:t,children:e.jsxs("div",{className:"w-full max-w-lg rounded-xl border border-border bg-panel p-5 shadow-xl",onClick:n=>n.stopPropagation(),children:[e.jsxs("div",{className:"mb-4 flex items-center justify-between",children:[e.jsx("h2",{className:"text-[15px] font-semibold",children:"New agent run"}),e.jsx("button",{type:"button",onClick:t,className:"text-faint hover:text-ink",children:e.jsx(A,{size:16})})]}),e.jsx("label",{className:"mb-1 block text-[12px] text-muted",children:"Task"}),e.jsx("textarea",{autoFocus:!0,value:l,onChange:n=>i(n.target.value),placeholder:"Describe what the agent should do…",rows:4,className:"mb-3 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsxs("label",{className:"mb-1 block text-[12px] text-muted",children:["System prompt ",e.jsx("span",{className:"text-faint",children:"(optional)"})]}),e.jsx("textarea",{value:r,onChange:n=>o(n.target.value),placeholder:"You are a helpful assistant…",rows:2,className:"mb-4 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsx("label",{className:"mb-1.5 block text-[12px] text-muted",children:"Tools"}),e.jsx("div",{className:"mb-4 space-y-1.5",children:U.map(n=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 rounded-lg border border-border px-2.5 py-2 hover:bg-panel-2",children:[e.jsx("input",{type:"checkbox",checked:d[n.id],onChange:()=>x(n.id),className:"mt-0.5 accent-[var(--accent)]"}),e.jsxs("span",{className:"min-w-0",children:[e.jsx("span",{className:"block text-[13px] font-medium text-ink",children:n.label}),e.jsx("span",{className:"block text-[11px] leading-snug text-faint",children:n.hint})]})]},n.id))}),e.jsxs("div",{className:"flex items-center justify-end gap-2",children:[e.jsx("button",{type:"button",onClick:t,className:"rounded-lg px-3 py-1.5 text-[13px] text-muted hover:text-ink",children:"Cancel"}),e.jsxs("button",{type:"button",disabled:!l.trim()||c.isPending,onClick:()=>c.mutate(),className:"flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white disabled:opacity-50",children:[c.isPending?e.jsx(h,{size:13,className:"animate-spin"}):e.jsx(S,{size:13}),"Run agent"]})]}),c.isError&&e.jsx("p",{className:"mt-2 text-[12px]",style:{color:"var(--err)"},children:c.error.message})]})})}function X({call:t}){const[a,l]=u.useState(!1),i=typeof t.args?.query=="string"?t.args.query:typeof t.args?.url=="string"?t.args.url:JSON.stringify(t.args??{});return e.jsxs("div",{className:"w-full max-w-[85%] rounded-lg border border-border bg-panel px-2.5 py-1.5 text-[12px]",children:[e.jsxs("button",{type:"button",onClick:()=>l(r=>!r),className:"flex w-full items-center gap-1.5 text-left text-muted hover:text-ink",children:[e.jsx(E,{size:12,className:"shrink-0 text-accent"}),e.jsx("span",{className:"font-medium text-ink",children:t.name}),e.jsx("span",{className:"truncate text-faint",children:i})]}),a&&e.jsx("pre",{className:"mt-1.5 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded bg-panel-2 p-2 text-[11px] text-muted",children:t.result})]})}function H({run:t}){const a=C(),l=u.useRef(null),[i,r]=u.useState(""),o=u.useRef(null),d=q({mutationFn:()=>K(t.id),onSuccess:()=>{a.invalidateQueries({queryKey:p.list()})}});u.useEffect(()=>{if(t.status!=="running"&&t.status!=="queued")return;const s=new AbortController;l.current=s;let x="";return(async()=>{try{for await(const c of F(t.id,0,s.signal))if(c.event==="delta")x+=c.data.delta,r(x);else if(c.event==="done"||c.event==="error"){r(""),a.invalidateQueries({queryKey:p.detail(t.id)}),a.invalidateQueries({queryKey:p.list()});break}}catch{}})(),()=>{s.abort(),l.current=null}},[t.id,t.status,a]),u.useEffect(()=>{o.current?.scrollIntoView({behavior:"smooth"})},[i,t.messages?.length]);const m=t.messages??[];return e.jsxs("div",{className:"flex h-full flex-col",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-4 py-2.5",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[14px] font-medium text-ink",children:t.title}),e.jsx(z,{status:t.status})]}),(t.status==="running"||t.status==="queued")&&e.jsx("button",{type:"button",onClick:()=>d.mutate(),className:"rounded-lg border border-border px-3 py-1 text-[12px] text-muted transition-colors hover:border-red-400 hover:text-red-500",children:d.isPending?e.jsx(h,{size:12,className:"inline animate-spin"}):"Cancel"})]}),e.jsxs("div",{className:"flex-1 space-y-3 overflow-y-auto p-4",children:[m.map(s=>e.jsxs("div",{className:b("flex flex-col gap-1.5",s.role==="user"?"items-end":"items-start"),children:[s.toolCalls?.map(x=>e.jsx(X,{call:x},x.id)),(s.content||s.role==="user"||t.status==="running")&&e.jsx("div",{className:b("max-w-[85%] rounded-xl px-3 py-2 text-[13px] leading-relaxed",s.role==="user"?"bg-accent/15 text-ink":"bg-panel-2 text-ink"),children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:s.content||(s.role==="assistant"&&t.status==="running"?"…":"")})})]},s.id)),i&&e.jsx("div",{className:"flex gap-2",children:e.jsx("div",{className:"max-w-[85%] rounded-xl bg-panel-2 px-3 py-2 text-[13px] leading-relaxed text-ink",children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:i})})}),t.error&&e.jsxs("div",{className:"rounded-lg bg-red-500/10 px-3 py-2 text-[12px] text-red-500",children:["Error: ",t.error]}),e.jsx("div",{ref:o})]})]})}function ne(){const[t,a]=u.useState(null),[l,i]=u.useState(!1),r=v({queryKey:p.list(),queryFn:$,refetchInterval:s=>(s.state.data??[]).some(n=>n.status==="queued"||n.status==="running")?3e3:!1}),o=v({queryKey:p.detail(t??""),queryFn:()=>I(t),enabled:!!t,refetchInterval:s=>{const x=s.state.data;return x?.status==="queued"||x?.status==="running"?5e3:!1}}),d=r.data??[],m=o.data;return e.jsxs("div",{className:"flex h-full",children:[e.jsxs("div",{className:"flex w-64 shrink-0 flex-col border-r border-border",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-3 py-2.5",children:[e.jsx("span",{className:"text-[13px] font-semibold text-ink",children:"Agents"}),e.jsxs("button",{type:"button",title:"New agent run",onClick:()=>i(!0),className:"flex items-center gap-1 rounded-lg bg-accent px-2 py-1 text-[11px] font-medium text-white transition-opacity hover:opacity-90",children:[e.jsx(N,{size:11})," New"]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[r.isLoading&&e.jsx("div",{className:"flex h-20 items-center justify-center",children:e.jsx(h,{size:16,className:"animate-spin text-faint"})}),d.length===0&&!r.isLoading&&e.jsxs("div",{className:"px-4 py-8 text-center",children:[e.jsx(S,{size:28,className:"mx-auto mb-2 text-faint"}),e.jsx("p",{className:"text-[12px] text-muted",children:"No agent runs yet."}),e.jsx("p",{className:"mt-0.5 text-[11px] text-faint",children:"Click “New” to start one."})]}),d.map(s=>e.jsxs("button",{type:"button",onClick:()=>a(s.id),className:b("w-full border-b border-border px-3 py-2.5 text-left transition-colors hover:bg-panel-2",t===s.id&&"bg-panel-2"),children:[e.jsx("p",{className:"mb-0.5 truncate text-[13px] font-medium text-ink",children:s.title}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(z,{status:s.status}),e.jsx("span",{className:"text-[10px] text-faint",children:W(s.createdAt)})]})]},s.id))]})]}),e.jsx("div",{className:"flex min-w-0 flex-1 flex-col",children:t?o.isLoading?e.jsx("div",{className:"flex h-full items-center justify-center",children:e.jsx(h,{size:20,className:"animate-spin text-faint"})}):m?e.jsx(H,{run:m}):null:e.jsxs("div",{className:"flex h-full flex-col items-center justify-center text-center",children:[e.jsx(D,{size:32,className:"mb-3 text-faint"}),e.jsx("p",{className:"text-[14px] font-medium text-ink",children:"Select a run"}),e.jsx("p",{className:"mt-1 text-[12px] text-muted",children:"or create a new agent run to get started"}),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:"mt-4 flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white hover:opacity-90",children:[e.jsx(N,{size:13})," New agent run"]})]})}),l&&e.jsx(B,{onClose:()=>i(!1),onCreated:s=>{a(s),r.refetch()}})]})}export{ne as AgentsScreen};