turbollm 1.12.3 → 1.12.4

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 (126) hide show
  1. package/README.md +4 -0
  2. package/dist/cli.js +118 -33
  3. package/dist/webdist/assets/{AgentEditPage-CE4lxy1g.js → AgentEditPage-CdHC3qGU.js} +1 -1
  4. package/dist/webdist/assets/{ChatScreen-BWZFrPkm.js → ChatScreen-CZ1jxorL.js} +1 -1
  5. package/dist/webdist/assets/{CodeComposer-53_HhRBK.js → CodeComposer-DVq1Owms.js} +1 -1
  6. package/dist/webdist/assets/{CodeHomeScreen-D1alRzQa.js → CodeHomeScreen-BcXb_FrC.js} +1 -1
  7. package/dist/webdist/assets/{CodeSessionScreen-p9tPPoVe.js → CodeSessionScreen-jIq1R-Be.js} +1 -1
  8. package/dist/webdist/assets/{CustomizeScreen-CebO4Kif.js → CustomizeScreen-1WEWeS2o.js} +1 -1
  9. package/dist/webdist/assets/{DeveloperScreen-CI5WYmmt.js → DeveloperScreen-CRd6LlbR.js} +1 -1
  10. package/dist/webdist/assets/EnginesScreen-DMpTol2j.js +8 -0
  11. package/dist/webdist/assets/{ModelDetailDialog-CriVmDMu.js → ModelDetailDialog-PesH4Lvj.js} +1 -1
  12. package/dist/webdist/assets/{ModelLoadMenu-yzCvMZFM.js → ModelLoadMenu-Kyk_PCm1.js} +1 -1
  13. package/dist/webdist/assets/{ModelsScreen-BLosszVi.js → ModelsScreen-BrWmAI3S.js} +1 -1
  14. package/dist/webdist/assets/{OnboardingScreen-1Eh4MMsz.js → OnboardingScreen-BqsISOAx.js} +1 -1
  15. package/dist/webdist/assets/{RoutineEditPage-BBptNINr.js → RoutineEditPage-DYyP5bcn.js} +1 -1
  16. package/dist/webdist/assets/{RoutinesPanel-DA1e-FlT.js → RoutinesPanel-CWNAB6XV.js} +1 -1
  17. package/dist/webdist/assets/SettingsScreen-BolAJ4j1.js +2 -0
  18. package/dist/webdist/assets/{SidebarResizeHandle-BS9E0P8t.js → SidebarResizeHandle-CvNSmidY.js} +1 -1
  19. package/dist/webdist/assets/{SkillEditPage-CswptEm0.js → SkillEditPage-Ce5jCKvK.js} +1 -1
  20. package/dist/webdist/assets/{TokensScreen-DqX7R08T.js → TokensScreen-i81av7bn.js} +1 -1
  21. package/dist/webdist/assets/{ToolApprovalBar-Ctxtcdk_.js → ToolApprovalBar-Dos3RNn4.js} +3 -3
  22. package/dist/webdist/assets/WorkspaceScreen-C8o9JVxH.js +1 -0
  23. package/dist/webdist/assets/{abnfDiagram-N423BO3Z-wYX3XzEU.js → abnfDiagram-N423BO3Z-BIBCjVzE.js} +1 -1
  24. package/dist/webdist/assets/{agent-api-CNgD3AG5.js → agent-api-DZ1WgRmZ.js} +1 -1
  25. package/dist/webdist/assets/{alert-dialog-DtFUjjf-.js → alert-dialog-BBENYlWb.js} +1 -1
  26. package/dist/webdist/assets/{arc-CAVYLnrV.js → arc-zf5dBjx2.js} +1 -1
  27. package/dist/webdist/assets/{architectureDiagram-T3A2C74G-B-ep8-Wc.js → architectureDiagram-T3A2C74G-DnThYv0T.js} +1 -1
  28. package/dist/webdist/assets/{arrow-left-BUyjb-eA.js → arrow-left-D_WlfmJO.js} +1 -1
  29. package/dist/webdist/assets/{badge-fTl4lUnO.js → badge-DIC-2P-e.js} +1 -1
  30. package/dist/webdist/assets/{blockDiagram-VBNYF7ZC-C_yOS3Cn.js → blockDiagram-VBNYF7ZC-BNrOo7My.js} +1 -1
  31. package/dist/webdist/assets/{bot-DjnxX6n5.js → bot-D4oi44Nz.js} +1 -1
  32. package/dist/webdist/assets/{c4Diagram-5PPSVZJV-yQy5pOFU.js → c4Diagram-5PPSVZJV-CGWOFS9g.js} +1 -1
  33. package/dist/webdist/assets/channel-CAqIqLwF.js +1 -0
  34. package/dist/webdist/assets/{chat-api-Cm7puych.js → chat-api-fC2sf7Mj.js} +1 -1
  35. package/dist/webdist/assets/{chat-queries-flf3EhI8.js → chat-queries-qpktuERN.js} +1 -1
  36. package/dist/webdist/assets/{chevron-down-BMj_J4Z6.js → chevron-down-ChtnXh37.js} +1 -1
  37. package/dist/webdist/assets/{chevron-left-CcApbuPp.js → chevron-left-DXZuhCTN.js} +1 -1
  38. package/dist/webdist/assets/{chevron-right-DcT_N4lU.js → chevron-right-BSHofHoG.js} +1 -1
  39. package/dist/webdist/assets/{chunk-2GRJ4B5K-DGiFq5-O.js → chunk-2GRJ4B5K-BToCFJph.js} +1 -1
  40. package/dist/webdist/assets/{chunk-2Q5K7J3B-D3EdskXA.js → chunk-2Q5K7J3B-C2E2U_Wt.js} +1 -1
  41. package/dist/webdist/assets/{chunk-5RXB4S5H-BidckIKz.js → chunk-5RXB4S5H-Bt8_ImVV.js} +1 -1
  42. package/dist/webdist/assets/{chunk-5VM5RSS4-DOxfljEH.js → chunk-5VM5RSS4-BEfsXc2S.js} +1 -1
  43. package/dist/webdist/assets/{chunk-6Q2QTUOP-DAYq99TW.js → chunk-6Q2QTUOP-DHZTU_LD.js} +1 -1
  44. package/dist/webdist/assets/{chunk-GF5L2VYU-CRAn0Eq3.js → chunk-GF5L2VYU-DEUAN61L.js} +1 -1
  45. package/dist/webdist/assets/{chunk-JWPE2WC7-DHQMj5yT.js → chunk-JWPE2WC7-Dw2aRPzh.js} +1 -1
  46. package/dist/webdist/assets/{chunk-KBJHAD2P-CVzZvxJP.js → chunk-KBJHAD2P-Cxgw5S4G.js} +1 -1
  47. package/dist/webdist/assets/{chunk-RYQCIY6F-BmrJERxa.js → chunk-RYQCIY6F-Bv3rJwBY.js} +1 -1
  48. package/dist/webdist/assets/{chunk-XXDRQBXY-DEZG0EV3.js → chunk-XXDRQBXY-DOGwIQ-5.js} +1 -1
  49. package/dist/webdist/assets/{circle-check-BNjlHnHh.js → circle-check-PwESlMlu.js} +1 -1
  50. package/dist/webdist/assets/{circle-x-DV1nLTK3.js → circle-x-Cta68_gk.js} +1 -1
  51. package/dist/webdist/assets/classDiagram-JCYQIIEL-D8UwXHdu.js +1 -0
  52. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-D8UwXHdu.js +1 -0
  53. package/dist/webdist/assets/{common-CaQP3vin.js → common-CJJrr1d6.js} +1 -1
  54. package/dist/webdist/assets/{cose-bilkent-JH36ORCC-Bq5B3ICk.js → cose-bilkent-JH36ORCC-DpQxukTC.js} +1 -1
  55. package/dist/webdist/assets/{cynefin-VYW2F7L2-DZle68dV.js → cynefin-VYW2F7L2-C4ao01h7.js} +1 -1
  56. package/dist/webdist/assets/{cynefinDiagram-MW4NZA55-CfM3dACl.js → cynefinDiagram-MW4NZA55-DkkKzRZl.js} +1 -1
  57. package/dist/webdist/assets/{dagre-VZM6K2ZE-DnxnfNsU.js → dagre-VZM6K2ZE-vSbclD6H.js} +1 -1
  58. package/dist/webdist/assets/{diagram-7IWD3JNH-j2-QzJMm.js → diagram-7IWD3JNH-BWofSihb.js} +1 -1
  59. package/dist/webdist/assets/{diagram-B4RE2ZJO-b38UCmx1.js → diagram-B4RE2ZJO-BF6NzW6i.js} +1 -1
  60. package/dist/webdist/assets/{diagram-LBJQPF4R-CdkW-WA-.js → diagram-LBJQPF4R-xr0rYyvY.js} +1 -1
  61. package/dist/webdist/assets/{diagram-Q27KOJAE-B0R9y_o4.js → diagram-Q27KOJAE-DY2EOYz8.js} +1 -1
  62. package/dist/webdist/assets/{diagram-UB23O5K3-ChZYNuxT.js → diagram-UB23O5K3-zrZF27ik.js} +1 -1
  63. package/dist/webdist/assets/{dropdown-menu-CzL84hjo.js → dropdown-menu-C7SX-XbP.js} +1 -1
  64. package/dist/webdist/assets/{ebnfDiagram-BXEA7PRR-Df2n-V3W.js → ebnfDiagram-BXEA7PRR-Cdqx9C3b.js} +1 -1
  65. package/dist/webdist/assets/{erDiagram-JOGREHBK-CJwWpaRB.js → erDiagram-JOGREHBK-DNOLDkDZ.js} +1 -1
  66. package/dist/webdist/assets/{external-link-Bqgcz-GW.js → external-link-MZUdNZic.js} +1 -1
  67. package/dist/webdist/assets/{file-text-CAqmQYG6.js → file-text-DtzC6GM1.js} +1 -1
  68. package/dist/webdist/assets/{flowDiagram-UKHOOZJN-Bd94ldad.js → flowDiagram-UKHOOZJN-ChbrkceS.js} +1 -1
  69. package/dist/webdist/assets/{folder-input-dy5PCMy4.js → folder-input-Czg0RwSd.js} +1 -1
  70. package/dist/webdist/assets/{folder-plus-YR4wA8PT.js → folder-plus-BndMFsaS.js} +1 -1
  71. package/dist/webdist/assets/{ganttDiagram-PKOTCBZU-DYeYOb2A.js → ganttDiagram-PKOTCBZU-BMW0DvB1.js} +1 -1
  72. package/dist/webdist/assets/{git-branch-BZWrq5B1.js → git-branch-CZQe8XJB.js} +1 -1
  73. package/dist/webdist/assets/{gitGraphDiagram-DS77QQ5N-CvZdup4u.js → gitGraphDiagram-DS77QQ5N-D1Wh4tzk.js} +1 -1
  74. package/dist/webdist/assets/{index-4QuGA7c6.js → index-6s1TR3Mg.js} +9 -9
  75. package/dist/webdist/assets/{infoDiagram-6WML65LV-jwAgJ0e9.js → infoDiagram-6WML65LV-VbzCs17L.js} +1 -1
  76. package/dist/webdist/assets/{ishikawaDiagram-WSZJBQD7-BEdaiV3h.js → ishikawaDiagram-WSZJBQD7-NXmrNE4M.js} +1 -1
  77. package/dist/webdist/assets/{journeyDiagram-NVQOT4AX-CdVLYVhh.js → journeyDiagram-NVQOT4AX-CT3eftG5.js} +1 -1
  78. package/dist/webdist/assets/{kanban-definition-27J2QSJJ-DT-DsTQr.js → kanban-definition-27J2QSJJ-CMLz2XNX.js} +1 -1
  79. package/dist/webdist/assets/{linear-BrxtevgR.js → linear-C8w6e5pr.js} +1 -1
  80. package/dist/webdist/assets/{link-queries-C3HTfth1.js → link-queries-CM-8kcuV.js} +1 -1
  81. package/dist/webdist/assets/{lock-zeDOBniP.js → lock-CtCTK4fi.js} +1 -1
  82. package/dist/webdist/assets/{mermaid.core-B9NxJSlE.js → mermaid.core-Dg3gzQXp.js} +4 -4
  83. package/dist/webdist/assets/{mindmap-definition-FAOFIHXS-BPQfqoP2.js → mindmap-definition-FAOFIHXS-CFA-t8Pv.js} +1 -1
  84. package/dist/webdist/assets/{monitor-BDzPa5YN.js → monitor-BnDu1JJe.js} +1 -1
  85. package/dist/webdist/assets/{pegDiagram-VL7TDLO6-CR4mTeP-.js → pegDiagram-VL7TDLO6-Cvrk_yjS.js} +1 -1
  86. package/dist/webdist/assets/{pencil-BtEhdGP-.js → pencil-CFuABcQS.js} +1 -1
  87. package/dist/webdist/assets/{personas-BK5wc_P3.js → personas-CdldzkmX.js} +1 -1
  88. package/dist/webdist/assets/{pieDiagram-7S7Q4E2Y-DPD5scZh.js → pieDiagram-7S7Q4E2Y-DspIfgvH.js} +1 -1
  89. package/dist/webdist/assets/{plus-D3Tv9cim.js → plus-BPDwshww.js} +1 -1
  90. package/dist/webdist/assets/{quadrantDiagram-CIZ2JOQS-DsESOJc6.js → quadrantDiagram-CIZ2JOQS-DlU5s8vy.js} +1 -1
  91. package/dist/webdist/assets/{railroadDiagram-AXF67PYL-U15r6DjT.js → railroadDiagram-AXF67PYL-CNaDw4Jn.js} +1 -1
  92. package/dist/webdist/assets/{refresh-cw-BemgDv3Q.js → refresh-cw-D8RZp8yK.js} +1 -1
  93. package/dist/webdist/assets/{remote-failure-Ddau3S9z.js → remote-failure-DrcbWSOc.js} +1 -1
  94. package/dist/webdist/assets/{requirementDiagram-LRYGKXZP-DDeRZQ__.js → requirementDiagram-LRYGKXZP-DR31pxOu.js} +1 -1
  95. package/dist/webdist/assets/{rocket-Uil5x1E9.js → rocket-CrQF04Ko.js} +1 -1
  96. package/dist/webdist/assets/{rotate-ccw-Bar4GXPb.js → rotate-ccw-Ao2f14qP.js} +1 -1
  97. package/dist/webdist/assets/{sankeyDiagram-W5VNT64P-BMTSd4xI.js → sankeyDiagram-W5VNT64P-CqFjXMeB.js} +1 -1
  98. package/dist/webdist/assets/{sequenceDiagram-SI44F4Z6-DdMCm5jm.js → sequenceDiagram-SI44F4Z6-Bxu2Mhjb.js} +1 -1
  99. package/dist/webdist/assets/{sizeCapture-X5ZJPWSS-2IYIZrFi.js → sizeCapture-X5ZJPWSS-Clu9P7P2.js} +1 -1
  100. package/dist/webdist/assets/{skeleton-MlF2228j.js → skeleton-WkEOMnX2.js} +1 -1
  101. package/dist/webdist/assets/{sliders-horizontal-DFahpoGu.js → sliders-horizontal-BMPLOqS8.js} +1 -1
  102. package/dist/webdist/assets/{sparkles-BYaOifNR.js → sparkles-DUmKhFVc.js} +1 -1
  103. package/dist/webdist/assets/{star-HYST5DWB.js → star-CHanzKtJ.js} +1 -1
  104. package/dist/webdist/assets/{stateDiagram-OKZ733FA-DgPdyEyJ.js → stateDiagram-OKZ733FA-CleTNrVM.js} +1 -1
  105. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-BCsf9FS8.js +1 -0
  106. package/dist/webdist/assets/{swimlanes-SLNWSIFB-CxbQXrF7.js → swimlanes-SLNWSIFB-BOkmWRMZ.js} +2 -2
  107. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-Da27rzM3.js +8 -0
  108. package/dist/webdist/assets/{switch-VYcKekwB.js → switch-DOWmW_HN.js} +1 -1
  109. package/dist/webdist/assets/{terminal-BXEMsYi1.js → terminal-Dsti4UEP.js} +1 -1
  110. package/dist/webdist/assets/{timeline-definition-Z64GVDOM-BN25-Wyy.js → timeline-definition-Z64GVDOM-DldeIhgX.js} +1 -1
  111. package/dist/webdist/assets/{trash-2-BNwq5WtQ.js → trash-2-BS-v4vnu.js} +1 -1
  112. package/dist/webdist/assets/{triangle-alert-DTK5P852.js → triangle-alert-CbnEc6nz.js} +1 -1
  113. package/dist/webdist/assets/{vennDiagram-T6HMQDX7-Cmn8MOEp.js → vennDiagram-T6HMQDX7-BfEil2zW.js} +1 -1
  114. package/dist/webdist/assets/{wardleyDiagram-T6FBY63Y-AS3obg9i.js → wardleyDiagram-T6FBY63Y-C5f7dizL.js} +1 -1
  115. package/dist/webdist/assets/{wrench-B5967UE3.js → wrench-RrT170se.js} +1 -1
  116. package/dist/webdist/assets/{xychartDiagram-ELKLHX3M-puQ_9bb2.js → xychartDiagram-ELKLHX3M-C0Caadm6.js} +1 -1
  117. package/dist/webdist/index.html +1 -1
  118. package/package.json +1 -1
  119. package/dist/webdist/assets/EnginesScreen-DjzBXrbU.js +0 -8
  120. package/dist/webdist/assets/SettingsScreen-CE211J25.js +0 -2
  121. package/dist/webdist/assets/WorkspaceScreen-82kXmjux.js +0 -1
  122. package/dist/webdist/assets/channel-CMLjxq8A.js +0 -1
  123. package/dist/webdist/assets/classDiagram-JCYQIIEL-BaxaaW6h.js +0 -1
  124. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-BaxaaW6h.js +0 -1
  125. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-Bi2zf8yv.js +0 -1
  126. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-DvkgZzhL.js +0 -8
package/README.md CHANGED
@@ -507,6 +507,10 @@ above.
507
507
  llama.cpp-compatible fork's git URL (+ optional branch, defaults to the repo's own default) and
508
508
  build it in-app with the same 1-click flow, no separate "point at a folder" step needed.
509
509
 
510
+ Branch pickers and engine update checks both read GitHub's API, which allows 60 anonymous
511
+ requests an hour. If you run into that ceiling, add a **GitHub token** in Settings → Models &
512
+ loading to raise it to 5,000/hour — it is write-only and never leaves your machine.
513
+
510
514
  **Auto-provisioned default.** Don't want to fetch anything? On first run TurboLLM downloads
511
515
  the right upstream prebuilt for your GPU automatically — and a **backend picker** lets you
512
516
  switch between CUDA / ROCm / Metal / SYCL / Vulkan / CPU at any time (it downloads the variant
package/dist/cli.js CHANGED
@@ -130,6 +130,7 @@ function defaultConfig() {
130
130
  selectedRemoteModel: "",
131
131
  autoLoadOnStart: false,
132
132
  hf: { token: "" },
133
+ gh: { token: "" },
133
134
  modelDefaults: { ctx: 8192, ngl: 99, imageMaxTokens: 0, maxTokens: 0 },
134
135
  featuredOverrideUrl: "",
135
136
  comfyui: { enabled: false, gatePath: "", url: "", reverseGate: false, cachePersist: false },
@@ -261,6 +262,7 @@ function normalize(c) {
261
262
  c.daemon = { ...d.daemon, ...c.daemon ?? {} };
262
263
  c.telemetry = { ...d.telemetry, ...c.telemetry ?? {} };
263
264
  c.hf = { ...d.hf, ...c.hf ?? {} };
265
+ c.gh = { ...d.gh, ...c.gh ?? {} };
264
266
  c.modelDefaults = { ...d.modelDefaults, ...c.modelDefaults ?? {} };
265
267
  c.lastLoaded = { ...d.lastLoaded, ...c.lastLoaded ?? {} };
266
268
  if (typeof c.selectedRemoteModel !== "string") c.selectedRemoteModel = "";
@@ -688,18 +690,6 @@ function clampMaxTokens(requested, limit) {
688
690
  return Math.min(requested, limit);
689
691
  }
690
692
 
691
- // src/engines/manager.ts
692
- import { execFile as execFile7, spawn, spawnSync } from "child_process";
693
- import { createWriteStream as createWriteStream2, existsSync as existsSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync3, readFileSync as readFileSync2, rmSync as rmSync6, writeFileSync as writeFileSync3 } from "fs";
694
- import { createConnection, createServer } from "net";
695
- import { dirname as dirname2, join as join11 } from "path";
696
-
697
- // src/engines/mlx.ts
698
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, rmSync as rmSync2 } from "fs";
699
- import { execFile as execFile2 } from "child_process";
700
- import { join as join3 } from "path";
701
- import { promisify as promisify2 } from "util";
702
-
703
693
  // src/engines/download.ts
704
694
  import { createWriteStream, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
705
695
  import { execFile, execFileSync } from "child_process";
@@ -708,12 +698,29 @@ import { Readable } from "stream";
708
698
  import { pipeline } from "stream/promises";
709
699
  import { promisify } from "util";
710
700
  var execFileP = promisify(execFile);
711
- function githubHeaders(extra) {
701
+ var ambientTokenFn = null;
702
+ function setGithubTokenProvider(fn) {
703
+ ambientTokenFn = fn;
704
+ }
705
+ function githubHeaders(extra, tokenFn) {
712
706
  const base2 = {
713
707
  Accept: "application/vnd.github+json",
714
708
  "User-Agent": "turbollm"
715
709
  };
716
- const token = process.env.GITHUB_TOKEN?.trim();
710
+ let token = "";
711
+ try {
712
+ token = tokenFn?.() ?? "";
713
+ } catch {
714
+ token = "";
715
+ }
716
+ if (!token) {
717
+ try {
718
+ token = ambientTokenFn?.() ?? "";
719
+ } catch {
720
+ token = "";
721
+ }
722
+ }
723
+ if (!token) token = process.env.GITHUB_TOKEN?.trim() ?? "";
717
724
  if (token) base2["Authorization"] = `Bearer ${token}`;
718
725
  return { ...base2, ...extra };
719
726
  }
@@ -982,12 +989,22 @@ function pickReleaseAsset(assets, platform = process.platform, archStr = process
982
989
  }
983
990
  return best;
984
991
  }
992
+ var GithubRateLimitError = class extends Error {
993
+ constructor(repo) {
994
+ super(`GitHub API rate limit exhausted while querying ${repo}`);
995
+ this.name = "GithubRateLimitError";
996
+ }
997
+ };
998
+ function isRateLimited(res) {
999
+ return (res.status === 403 || res.status === 429) && res.headers.get("x-ratelimit-remaining") === "0";
1000
+ }
985
1001
  async function latestGithubRelease(repo, signal) {
986
1002
  const apiUrl = `https://api.github.com/repos/${repo}/releases/latest`;
987
1003
  const res = await fetch(apiUrl, {
988
1004
  headers: githubHeaders(),
989
1005
  signal
990
1006
  });
1007
+ if (isRateLimited(res)) throw new GithubRateLimitError(repo);
991
1008
  if (!res.ok) throw new Error(`could not query ${repo} releases: HTTP ${res.status}`);
992
1009
  return await res.json();
993
1010
  }
@@ -995,12 +1012,35 @@ async function latestReleaseTag(repo, signal) {
995
1012
  const rel = await latestGithubRelease(repo, signal);
996
1013
  return rel.tag_name ?? "";
997
1014
  }
1015
+ async function latestBuildTagRelease(repo, signal) {
1016
+ const res = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=100`, {
1017
+ headers: githubHeaders(),
1018
+ signal
1019
+ });
1020
+ if (isRateLimited(res)) throw new GithubRateLimitError(repo);
1021
+ if (!res.ok) throw new Error(`could not list ${repo} releases: HTTP ${res.status}`);
1022
+ const data = await res.json();
1023
+ let best = "";
1024
+ let bestNum = -1;
1025
+ for (const rel of data) {
1026
+ const tag = (rel.tag_name ?? "").trim();
1027
+ const m = /^v?b(\d+)$/i.exec(tag);
1028
+ if (!m) continue;
1029
+ const n = Number(m[1]);
1030
+ if (Number.isInteger(n) && n > bestNum) {
1031
+ bestNum = n;
1032
+ best = tag;
1033
+ }
1034
+ }
1035
+ return best;
1036
+ }
998
1037
  async function latestCommitSha(repo, branch = "", signal) {
999
1038
  const ref = branch.trim() ? encodeURIComponent(branch.trim()) : "HEAD";
1000
1039
  const res = await fetch(`https://api.github.com/repos/${repo}/commits/${ref}`, {
1001
1040
  headers: githubHeaders(),
1002
1041
  signal
1003
1042
  });
1043
+ if (isRateLimited(res)) throw new GithubRateLimitError(repo);
1004
1044
  if (!res.ok) throw new Error(`could not query ${repo} commits: HTTP ${res.status}`);
1005
1045
  const data = await res.json();
1006
1046
  return data.sha ?? "";
@@ -1051,7 +1091,17 @@ async function provisionTurboquant(enginesRoot, repo, onProgress, signal) {
1051
1091
  return bin;
1052
1092
  }
1053
1093
 
1094
+ // src/engines/manager.ts
1095
+ import { execFile as execFile7, spawn, spawnSync } from "child_process";
1096
+ import { createWriteStream as createWriteStream2, existsSync as existsSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync3, readFileSync as readFileSync2, rmSync as rmSync6, writeFileSync as writeFileSync3 } from "fs";
1097
+ import { createConnection, createServer } from "net";
1098
+ import { dirname as dirname2, join as join11 } from "path";
1099
+
1054
1100
  // src/engines/mlx.ts
1101
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, rmSync as rmSync2 } from "fs";
1102
+ import { execFile as execFile2 } from "child_process";
1103
+ import { join as join3 } from "path";
1104
+ import { promisify as promisify2 } from "util";
1055
1105
  var execFileP2 = promisify2(execFile2);
1056
1106
  var UV_VERSION = "0.11.21";
1057
1107
  var UV_REPO = "astral-sh/uv";
@@ -4405,6 +4455,13 @@ function resolveUpdateSource(engine) {
4405
4455
  }
4406
4456
  return null;
4407
4457
  }
4458
+ async function latestTagForInstalled(repo, installed2, signal) {
4459
+ if (parseBuildTag(installed2) !== null) {
4460
+ const buildTag = await latestBuildTagRelease(repo, signal);
4461
+ if (buildTag) return buildTag;
4462
+ }
4463
+ return latestReleaseTag(repo, signal);
4464
+ }
4408
4465
  async function fetchLatest(src, signal) {
4409
4466
  if (src.source === "pip") {
4410
4467
  const res = await fetch(`https://pypi.org/pypi/${src.ref}/json`, {
@@ -4416,7 +4473,7 @@ async function fetchLatest(src, signal) {
4416
4473
  return data.info?.version ?? "";
4417
4474
  }
4418
4475
  if (src.source === "source") return latestCommitSha(src.ref, src.branch ?? "", signal);
4419
- return latestReleaseTag(src.ref, signal);
4476
+ return latestTagForInstalled(src.ref, src.installed, signal);
4420
4477
  }
4421
4478
  async function computeUpdateStatus(engine, fetcher = fetchLatest, signal) {
4422
4479
  const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -4427,8 +4484,9 @@ async function computeUpdateStatus(engine, fetcher = fetchLatest, signal) {
4427
4484
  let latest2;
4428
4485
  try {
4429
4486
  latest2 = await fetcher(src, signal);
4430
- } catch {
4431
- return { installed: src.installed, latest: null, hasUpdate: false, checkedAt, error: "offline", comparable: false };
4487
+ } catch (e) {
4488
+ const error = e instanceof GithubRateLimitError ? "rate_limited" : "offline";
4489
+ return { installed: src.installed, latest: null, hasUpdate: false, checkedAt, error, comparable: false };
4432
4490
  }
4433
4491
  if (!latest2) {
4434
4492
  return { installed: src.installed, latest: null, hasUpdate: false, checkedAt, error: "offline", comparable: false };
@@ -13553,7 +13611,7 @@ async function applyLlamaCppUpdate(d, engine, root, signal) {
13553
13611
  const backendId = backendIdFromBinPath(engine.binPath);
13554
13612
  if (!backendId) throw new Error("Could not determine the GPU backend of this build.");
13555
13613
  const installedTag = tagFromManagedBinPath(engine.binPath);
13556
- const latestTag = await latestReleaseTag(OFFICIAL_LLAMA_REPO2, signal);
13614
+ const latestTag = await latestTagForInstalled(OFFICIAL_LLAMA_REPO2, installedTag, signal);
13557
13615
  if (!latestTag) throw new Error("GitHub did not report a latest build.");
13558
13616
  if (installedTag && installedTag === latestTag) return;
13559
13617
  const newDef = backendDefAt(backendId, latestTag);
@@ -22845,7 +22903,8 @@ var ALL = [
22845
22903
  installEndpoint: "",
22846
22904
  // Its installed state comes from the separate backend-build detection (LlamaCppBackendRows),
22847
22905
  // not the generic sourceRepo match below — see the field's own doc comment (ADR-388).
22848
- excludeFromSourceMatch: true
22906
+ excludeFromSourceMatch: true,
22907
+ defaultBranch: "master"
22849
22908
  },
22850
22909
  {
22851
22910
  // Upstream publishes NO Linux CUDA prebuilt (download.ts availableBackends() — Linux
@@ -22865,6 +22924,7 @@ var ALL = [
22865
22924
  support: "experimental",
22866
22925
  installEndpoint: "",
22867
22926
  note: "Builds the official repo with -DGGML_CUDA=ON and bundles its CUDA runtime libraries next to the binary, so it runs standalone. Manages like any other engine afterward (rebuild on a newer commit, disable, delete).",
22927
+ defaultBranch: "master",
22868
22928
  variants: [
22869
22929
  {
22870
22930
  id: "llama.cpp-cuda-source",
@@ -22897,6 +22957,7 @@ var ALL = [
22897
22957
  support: "experimental",
22898
22958
  installEndpoint: "",
22899
22959
  note: "Run inside Termux (not the Android app store \u2014 get Termux from F-Droid or its GitHub releases). First install the toolchain: `pkg install git cmake clang`. Then use the guided build below. Real-device verified: toolchain install + partial compile confirmed live on a real Android 14 (4KB-page) emulator; a full clean build was not timed to completion in that session (GitHub #52 item 6).",
22960
+ defaultBranch: "master",
22900
22961
  variants: [
22901
22962
  {
22902
22963
  id: "llama.cpp-android-source-cpu",
@@ -22927,6 +22988,7 @@ var ALL = [
22927
22988
  support: "experimental",
22928
22989
  installEndpoint: "",
22929
22990
  note: "No prebuilt binary. Select a branch below and TurboLLM clones + compiles it for you. Each branch you build becomes a separate engine (e.g. Llama-main, Llama-my-feature).",
22991
+ defaultBranch: "master",
22930
22992
  variants: [
22931
22993
  {
22932
22994
  id: "llama.cpp-source-branch",
@@ -22953,6 +23015,7 @@ var ALL = [
22953
23015
  support: "experimental",
22954
23016
  installEndpoint: "/api/v1/engines/vllm",
22955
23017
  note: "Officially supported on Linux + NVIDIA/CUDA. macOS is CPU-only experimental; Windows is unsupported upstream. Installs a multi-GB Python environment.",
23018
+ defaultBranch: "main",
22956
23019
  // Classification-only variant (its pip install path is unchanged). Lets the
22957
23020
  // matcher/recommender reason about vLLM's fit on this box: Linux + NVIDIA.
22958
23021
  variants: [
@@ -22979,6 +23042,7 @@ var ALL = [
22979
23042
  support: "stable",
22980
23043
  installEndpoint: "/api/v1/engines/mlx",
22981
23044
  note: "macOS (Apple Silicon) only.",
23045
+ defaultBranch: "main",
22982
23046
  // Classification-only variant (its pip install path is unchanged). Lets the
22983
23047
  // matcher/recommender reason about MLX's fit on this box: macOS + Apple GPU.
22984
23048
  variants: [
@@ -23005,6 +23069,7 @@ var ALL = [
23005
23069
  support: "experimental",
23006
23070
  installEndpoint: "/api/v1/engines/rapid-mlx",
23007
23071
  note: "macOS (Apple Silicon) only. Loads the same MLX-format model directories as the MLX engine.",
23072
+ defaultBranch: "main",
23008
23073
  variants: [
23009
23074
  {
23010
23075
  id: "rapid-mlx",
@@ -23029,6 +23094,7 @@ var ALL = [
23029
23094
  support: "experimental",
23030
23095
  installEndpoint: "/api/v1/engines/mlx-vlm",
23031
23096
  note: 'macOS (Apple Silicon) only. Loads the same MLX-format model directories as the MLX engine. Some architectures (e.g. Qwen-VL, SmolVLM) additionally need torch + torchvision, which are not installed automatically. If a model fails to load with a "missing image processor" error, locate the bootstrapped uv binary (find ~/.turbollm/engines/uv -name uv -type f) and run: <that uv path> pip install --python <this engine\'s venv>/bin/python torch torchvision. Tool/function calling is architecture-dependent: mlx-vlm only turns a `tools` array into real tool calls for the handful of chat templates it has a matching parser for. Other models accept the request without error but never call a tool (confirmed live on Qwen2.5-VL: its own /health reports "loaded_tool_parser": null).',
23097
+ defaultBranch: "main",
23032
23098
  variants: [
23033
23099
  {
23034
23100
  id: "mlx-vlm",
@@ -23053,6 +23119,7 @@ var ALL = [
23053
23119
  support: "experimental",
23054
23120
  installEndpoint: "/api/v1/engines/sglang",
23055
23121
  note: "Officially supported on Linux + NVIDIA/CUDA 12+. macOS and Windows are unsupported upstream. Installs a multi-GB Python environment.",
23122
+ defaultBranch: "main",
23056
23123
  variants: [
23057
23124
  {
23058
23125
  id: "sglang-cuda",
@@ -23081,6 +23148,7 @@ var ALL = [
23081
23148
  support: "experimental",
23082
23149
  installEndpoint: "",
23083
23150
  note: 'No prebuilt binaries are published. Build llama-server from the fork, then use "Add your own engine" to point TurboLLM at it \u2014 it runs on the standard llama-server path.',
23151
+ defaultBranch: "main",
23084
23152
  variants: [
23085
23153
  {
23086
23154
  id: "ik_llama.cpp-source",
@@ -23108,6 +23176,7 @@ var ALL = [
23108
23176
  support: "experimental",
23109
23177
  installEndpoint: "/api/v1/engines/llamafile",
23110
23178
  note: "Downloads one portable executable that bundles llama.cpp. GPU acceleration depends on your platform/drivers; falls back to CPU.",
23179
+ defaultBranch: "main",
23111
23180
  variants: [
23112
23181
  {
23113
23182
  id: "llamafile",
@@ -23134,6 +23203,7 @@ var ALL = [
23134
23203
  support: "experimental",
23135
23204
  installEndpoint: "/api/v1/engines/koboldcpp",
23136
23205
  note: "Downloads a single KoboldCpp binary. The CUDA build is used on NVIDIA GPUs; the portable build (Vulkan/CPU) elsewhere. Windows/Linux are x64-only; macOS is Apple Silicon only.",
23206
+ defaultBranch: "concedo",
23137
23207
  variants: [
23138
23208
  {
23139
23209
  id: "koboldcpp-cuda",
@@ -23183,6 +23253,7 @@ var ALL = [
23183
23253
  support: "experimental",
23184
23254
  installEndpoint: "/api/v1/engines/turboquant",
23185
23255
  note: 'Prebuilt: macOS (Apple Silicon), Linux x64 (Vulkan). On Windows, build llama-server from the fork, then use "Add your own engine".',
23256
+ defaultBranch: "master",
23186
23257
  variants: [
23187
23258
  {
23188
23259
  id: "turboquant-macos-metal",
@@ -23230,6 +23301,7 @@ var ALL = [
23230
23301
  support: "experimental",
23231
23302
  installEndpoint: "",
23232
23303
  note: "Upstream publishes prebuilt binaries, but TurboLLM builds from source here (guided walkthrough) rather than resolving a specific release asset. Proven on a ternary/Bonsai-class model at 200K context (Q2_0 + q8 KV).",
23304
+ defaultBranch: "prism",
23233
23305
  variants: [
23234
23306
  {
23235
23307
  id: "prism-source",
@@ -23257,6 +23329,7 @@ var ALL = [
23257
23329
  support: "experimental",
23258
23330
  installEndpoint: "",
23259
23331
  note: "Upstream publishes prebuilt binaries, but TurboLLM builds from source here. The KVarN cache types (kvarn2...kvarn8, set via --cache-type-k/-v) show up directly in the KV cache type dropdown, but aren't in the auto-tune sweep yet \u2014 pick one manually.",
23332
+ defaultBranch: "main",
23260
23333
  variants: [
23261
23334
  {
23262
23335
  id: "beellama-source",
@@ -24737,14 +24810,14 @@ function registerApi(app2, d) {
24737
24810
  const busy = engineWorkBusy(d);
24738
24811
  if (busy) return err5(c, 409, "engine_already_running", busy);
24739
24812
  }
24813
+ const installedTag = tagFromManagedBinPath(oldBin);
24740
24814
  let latestTag;
24741
24815
  try {
24742
- latestTag = await latestReleaseTag("ggml-org/llama.cpp", AbortSignal.timeout(15e3));
24816
+ latestTag = await latestTagForInstalled("ggml-org/llama.cpp", installedTag, AbortSignal.timeout(15e3));
24743
24817
  } catch {
24744
24818
  return err5(c, 503, "offline", "Couldn't reach GitHub to check for a newer build. Try again when online.");
24745
24819
  }
24746
24820
  if (!latestTag) return err5(c, 503, "offline", "GitHub didn't report a latest build. Try again later.");
24747
- const installedTag = tagFromManagedBinPath(oldBin);
24748
24821
  if (installedTag && installedTag === latestTag) {
24749
24822
  return c.json({ accepted: false, alreadyLatest: true, build: latestTag });
24750
24823
  }
@@ -24951,18 +25024,29 @@ function registerApi(app2, d) {
24951
25024
  if (!raw) return err5(c, 400, "invalid_input", "repo is required.");
24952
25025
  const repo = normRepoUrl(raw);
24953
25026
  if (!repo) return err5(c, 400, "invalid_input", "repo must be a valid GitHub repo identifier.");
24954
- const limit = Math.min(parseInt(c.req.query("limit") ?? "50", 10) || 50, 100);
24955
- const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
25027
+ const tokenSet = (d.store.snapshot().gh?.token ?? "").length > 0;
24956
25028
  try {
24957
- const res = await fetch(`https://api.github.com/repos/${repo}/branches?per_page=100`, {
24958
- headers: githubHeaders()
24959
- });
24960
- if (!res.ok) throw new Error(`GitHub API returned HTTP ${res.status}`);
24961
- const data = await res.json();
25029
+ const names = [];
25030
+ for (let page = 1; page <= 20; page++) {
25031
+ const res = await fetch(`https://api.github.com/repos/${repo}/branches?per_page=100&page=${page}`, {
25032
+ headers: githubHeaders({}, () => d.store.snapshot().gh?.token ?? "")
25033
+ });
25034
+ if (isRateLimited(res))
25035
+ return err5(
25036
+ c,
25037
+ 403,
25038
+ "github_rate_limited",
25039
+ tokenSet ? "The configured GitHub token has exhausted its API rate limit (5,000 requests/hour). Branch lookups will work again after the limit resets." : "GitHub\u2019s unauthenticated API rate limit (60 requests/hour) is exhausted. Add a GitHub token in Settings to raise it to 5,000 requests/hour."
25040
+ );
25041
+ if (!res.ok) throw new Error(`GitHub API returned HTTP ${res.status}`);
25042
+ const data = await res.json();
25043
+ names.push(...data.map((b) => b.name));
25044
+ if (data.length < 100) break;
25045
+ }
24962
25046
  let defaultBranch = "";
24963
25047
  try {
24964
25048
  const info = await fetch(`https://api.github.com/repos/${repo}`, {
24965
- headers: githubHeaders()
25049
+ headers: githubHeaders({}, () => d.store.snapshot().gh?.token ?? "")
24966
25050
  });
24967
25051
  if (info.ok) {
24968
25052
  const j = await info.json();
@@ -24970,11 +25054,8 @@ function registerApi(app2, d) {
24970
25054
  }
24971
25055
  } catch {
24972
25056
  }
24973
- const names = data.map((b) => b.name);
24974
25057
  const sorted = defaultBranch ? [defaultBranch, ...names.filter((n) => n !== defaultBranch)] : names;
24975
- const total = names.length;
24976
- const page = sorted.slice(offset, offset + limit);
24977
- return c.json({ total, branches: page });
25058
+ return c.json({ total: sorted.length, branches: sorted });
24978
25059
  } catch (e) {
24979
25060
  return err5(c, 503, "offline", `Could not fetch branches for ${repo}: ${e instanceof Error ? e.message : String(e)}`);
24980
25061
  }
@@ -25844,6 +25925,7 @@ function registerApi(app2, d) {
25844
25925
  if (b.experimental?.routines !== void 0) cfg2.daemon.experimental.routines = !!b.experimental.routines;
25845
25926
  if (b.experimental?.turboLink !== void 0) cfg2.daemon.experimental.turboLink = !!b.experimental.turboLink;
25846
25927
  if (b.hfToken !== void 0) cfg2.hf.token = String(b.hfToken).trim();
25928
+ if (b.ghToken !== void 0) (cfg2.gh ??= { token: "" }).token = String(b.ghToken).trim();
25847
25929
  cfg2.tools.search ??= { provider: "tavily" };
25848
25930
  const s = cfg2.tools.search;
25849
25931
  if (b.search?.provider === "tavily" || b.search?.provider === "kagi" || b.search?.provider === "searxng") {
@@ -26225,6 +26307,8 @@ function settingsPayload(d) {
26225
26307
  // The HF token is write-only over the wire (spec 10 §4): we never echo it back,
26226
26308
  // only whether one is set, so the UI can show "configured" without leaking it.
26227
26309
  hfTokenSet: cfg2.hf.token.length > 0,
26310
+ // GitHub token is write-only: expose only whether it is set.
26311
+ ghTokenSet: (cfg2.gh?.token ?? "").length > 0,
26228
26312
  // Tavily API key is write-only: expose only whether it is set (legacy field, kept for compat).
26229
26313
  tavilyKeySet: !!(cfg2.tools.search?.tavilyApiKey ?? cfg2.tools.tavily?.apiKey),
26230
26314
  // Search provider config (F-020): provider + which credentials are set. Keys are write-only
@@ -34242,6 +34326,7 @@ var store = ConfigStore.load(argValue("--config", defaultConfigPath()));
34242
34326
  if (store.brokenBackup()) {
34243
34327
  console.warn(`config was reset; previous file backed up at ${store.brokenBackup()}`);
34244
34328
  }
34329
+ setGithubTokenProvider(() => store.snapshot().gh?.token ?? "");
34245
34330
  if (hasFlag("--stop")) {
34246
34331
  const result = await stopDaemon(store.dir());
34247
34332
  process.stdout.write(result.message + "\n");
@@ -1 +1 @@
1
- import{j as e}from"./radix-DW4bWMkB.js";import{b as W,u as $,r as b}from"./vendor-C1I6euDx.js";import{u as G,d as H,e as Y,f as J,g as U,a as z,B as C,t as j,c as q,b as h,A as _}from"./index-4QuGA7c6.js";import{f as V}from"./chat-api-Cm7puych.js";import{f as X,a as Z}from"./agent-api-CNgD3AG5.js";import{P as B}from"./personas-BK5wc_P3.js";import{C as ee}from"./chevron-left-CcApbuPp.js";import{R as te}from"./rotate-ccw-Bar4GXPb.js";import{T as se}from"./trash-2-BNwq5WtQ.js";import{C as ne}from"./chevron-right-DcT_N4lU.js";import"./dropdown-menu-CzL84hjo.js";const ae=()=>({name:"",description:"",systemPrompt:"",skillIds:[],tools:[]});function oe(s){const d=[],x=new Map;for(const n of s){const a=/^mcp__([^_]+(?:_[^_]+)*)__/.exec(n.name);if(!a){d.push(n);continue}const i=a[1],p=/^\[([^\]]+)\]/.exec(n.description),c=p?p[1]:i.replace(/_/g," ");x.has(i)||x.set(i,{key:i,label:c,tools:[]}),x.get(i).tools.push(n)}return{builtin:d,groups:[...x.values()].sort((n,a)=>n.label.localeCompare(a.label))}}function le(s){return s.replace(/^mcp__[^_]+(?:_[^_]+)*__/,"")}function re(s){return s.replace(/^\[[^\]]+\]\s*/,"")}function D({tool:s,checked:d,onToggle:x}){return e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[12px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:d,onChange:x}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"font-mono text-ink",children:le(s.name)}),s.description&&e.jsx("span",{className:"text-faint",children:re(s.description)})]})]})}function ie({group:s,selected:d,onToggleAll:x,onToggleOne:n}){const[a,i]=b.useState(!1),p=s.tools.map(l=>l.name),c=p.filter(l=>d.has(l)).length,o=c===p.length,g=c>0&&!o,u=b.useRef(null);return b.useEffect(()=>{u.current&&(u.current.indeterminate=g)},[g]),e.jsxs("div",{className:"rounded-md border border-border",children:[e.jsxs("div",{className:"flex items-center gap-2 px-2.5 py-2",children:[e.jsx("input",{ref:u,type:"checkbox",checked:o,onChange:()=>x(p,!o)}),e.jsxs("button",{type:"button",onClick:()=>{j("agents","toggle_agent_tool_group"),i(l=>!l)},className:"flex flex-1 items-center gap-1.5 text-left text-[13px] text-ink",children:[e.jsx(ne,{size:12,className:q("shrink-0 text-faint transition-transform",a&&"rotate-90")}),s.label,e.jsxs("span",{className:"text-[11px] text-faint",children:["(",c,"/",p.length,")"]})]})]}),a&&e.jsx("div",{className:"flex flex-col gap-1.5 border-t border-border px-3 py-2 pl-9",children:s.tools.map(l=>e.jsx(D,{tool:l,checked:d.has(l.name),onToggle:()=>n(l.name)},l.name))})]})}function ce({tools:s,selected:d,onChange:x}){const n=new Set(d),{builtin:a,groups:i}=oe(s),p=o=>x(n.has(o)?d.filter(g=>g!==o):[...d,o]),c=(o,g)=>{const u=d.filter(l=>!o.includes(l));x(g?[...u,...o]:u)};return s.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No tools available yet."}):e.jsxs("div",{className:"flex flex-col gap-2",children:[a.length>0&&e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:a.map(o=>e.jsx(D,{tool:o,checked:n.has(o.name),onToggle:()=>p(o.name)},o.name))}),i.map(o=>e.jsx(ie,{group:o,selected:n,onToggleAll:c,onToggleOne:p},o.key))]})}function je(){const{agentId:s="new"}=W(),d=$(),x=G(),n=s==="new",a=!n&&B.some(t=>t.id===s),i=a?B.find(t=>t.id===s):void 0,p=H(),c=!n&&!a?(p.data??[]).find(t=>t.id===s):void 0,o=Y(),g=J(),u=U(),l=a?g.data?.[s]:void 0,I=z({queryKey:Z.list(),queryFn:X,staleTime:0}),k=I.data??[],w=z({queryKey:["available-tools"],queryFn:V}),N=w.data??[],[m,f]=b.useState(ae()),[S,T]=b.useState(!1),[A,P]=b.useState(!1),[L,R]=b.useState(!1),[E,Q]=b.useState("skills");n&&!S&&(k.length||N.length)&&(f(t=>({...t,skillIds:k.map(r=>r.id),tools:N.map(r=>r.name)})),T(!0)),!n&&!a&&!S&&c&&(f({name:c.name,description:c.description,systemPrompt:c.systemPrompt,skillIds:c.skillIds,tools:c.tools}),T(!0)),a&&!S&&i&&g.isSuccess&&(k.length||N.length)&&(f({name:l?.name??i.name,description:l?.description??i.description,systemPrompt:l?.systemPrompt??i.systemPrompt,skillIds:l?.skillIds??k.map(t=>t.id),tools:l?.tools??N.map(t=>t.name)}),T(!0));const y=()=>{j("agents","back_to_agents"),d("/customize")},O=t=>f(r=>({...r,skillIds:r.skillIds.includes(t)?r.skillIds.filter(v=>v!==t):[...r.skillIds,t]})),F=()=>{if(!m.name.trim()){h.error("Name is required.");return}P(!0);const t={name:m.name.trim(),description:m.description.trim(),systemPrompt:m.systemPrompt,skillIds:m.skillIds,tools:m.tools};if(a){u.save.mutate({id:s,override:t},{onSuccess:()=>{h.success("Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}});return}const r={onSuccess:()=>{x.invalidateQueries({queryKey:["chat-agents"]}),h.success(n?"Agent created.":"Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}};n?o.add.mutate(t,r):o.update.mutate({id:s,patch:t},r)},K=()=>{u.reset.mutate(s,{onSuccess:()=>{h.success("Reset to default."),i&&f({name:i.name,description:i.description,systemPrompt:i.systemPrompt,skillIds:k.map(t=>t.id),tools:N.map(t=>t.name)})},onError:t=>h.error(t instanceof _?t.message:"Could not reset agent.")})},M=()=>{o.remove.mutate(s,{onSuccess:()=>{h.success("Agent deleted."),y()},onError:t=>h.error(t instanceof _?t.message:"Could not delete agent.")})};return!n&&!a&&p.isSuccess&&!c?e.jsxs("div",{className:"flex w-full flex-col items-center gap-3 px-8 py-16",children:[e.jsx("p",{className:"text-[14px] text-muted",children:"That agent no longer exists."}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Back to agents"})]}):e.jsxs("div",{className:"flex w-full max-w-4xl flex-col gap-5 px-4 py-6 md:px-8",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("button",{type:"button",onClick:y,className:"text-faint hover:text-ink",title:"Back to agents",children:e.jsx(ee,{size:18})}),e.jsx("span",{className:"text-[15px] font-medium text-ink",children:n?"New agent":a?l?"Edit agent (modified built-in)":"Edit agent (built-in)":"Edit agent"}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[a&&e.jsxs(C,{size:"sm",variant:"outline",onClick:()=>{j("agents","reset_agent_to_default"),K()},disabled:u.reset.isPending,children:[e.jsx(te,{size:13})," Reset to default"]}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Cancel"}),e.jsx(C,{size:"sm",onClick:()=>{j("agents","save_agent"),F()},disabled:A||!m.name.trim(),children:A?"Saving…":"Save"})]})]}),a&&e.jsx("p",{className:"rounded-md border border-border bg-panel px-3 py-2 text-[12px] text-muted",children:"Built-in agents can be edited in place — Reset to default discards your changes and restores TurboLLM's original prompt and access."}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Name"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"Release Notes Writer",value:m.name,onChange:t=>f(r=>({...r,name:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Description"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"What this agent is for, in one line",value:m.description,onChange:t=>f(r=>({...r,description:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"System prompt"}),e.jsx("textarea",{className:"min-h-[160px] resize-y rounded-md border border-border bg-bg px-3 py-2 font-mono text-[12px] leading-relaxed text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"You are…",value:m.systemPrompt,onChange:t=>f(r=>({...r,systemPrompt:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-2.5",children:[e.jsx("div",{className:"inline-flex w-fit rounded-md border border-border p-0.5",children:["skills","tools"].map(t=>e.jsx("button",{type:"button",onClick:()=>{j("agents","switch_agent_form_tab"),Q(t)},className:q("rounded px-3 py-1.5 text-[13px] font-medium transition-colors",E===t?"bg-accent/12 text-accent":"text-muted hover:text-ink"),children:t==="skills"?`Skills (${m.skillIds.length})`:`Tools (${m.tools.length})`},t))}),E==="skills"?I.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):k.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No skills yet — create one under Customize → Skills."}):e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:k.map(t=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:m.skillIds.includes(t.id),onChange:()=>O(t.id)}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"text-ink",children:t.name}),t.description&&e.jsx("span",{className:"text-[12px] text-muted",children:t.description})]})]},t.id))}):w.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):e.jsx(ce,{tools:N,selected:m.tools,onChange:t=>f(r=>({...r,tools:t}))})]}),!n&&!a&&(L?e.jsxs("div",{className:"flex items-center gap-2 self-start rounded-md border border-border bg-panel p-2 text-[12px]",children:[e.jsx("span",{className:"flex-1 text-muted",children:"Delete this agent?"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","delete_agent"),M()},className:"rounded px-2 py-1 text-[color:var(--err)] hover:bg-[color:color-mix(in_srgb,var(--err)_12%,transparent)]",children:"Delete"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","cancel_delete_agent"),R(!1)},className:"rounded px-2 py-1 text-faint hover:text-ink",children:"Cancel"})]}):e.jsxs("button",{type:"button",onClick:()=>R(!0),className:"flex items-center gap-1.5 self-start text-[12px] text-faint hover:text-[color:var(--err)]",children:[e.jsx(se,{size:13})," Delete agent"]}))]})}export{je as AgentEditPage};
1
+ import{j as e}from"./radix-DW4bWMkB.js";import{b as W,u as $,r as b}from"./vendor-C1I6euDx.js";import{u as G,d as H,e as Y,f as J,g as U,a as z,B as C,t as j,c as q,b as h,A as _}from"./index-6s1TR3Mg.js";import{f as V}from"./chat-api-fC2sf7Mj.js";import{f as X,a as Z}from"./agent-api-DZ1WgRmZ.js";import{P as B}from"./personas-CdldzkmX.js";import{C as ee}from"./chevron-left-DXZuhCTN.js";import{R as te}from"./rotate-ccw-Ao2f14qP.js";import{T as se}from"./trash-2-BS-v4vnu.js";import{C as ne}from"./chevron-right-BSHofHoG.js";import"./dropdown-menu-C7SX-XbP.js";const ae=()=>({name:"",description:"",systemPrompt:"",skillIds:[],tools:[]});function oe(s){const d=[],x=new Map;for(const n of s){const a=/^mcp__([^_]+(?:_[^_]+)*)__/.exec(n.name);if(!a){d.push(n);continue}const i=a[1],p=/^\[([^\]]+)\]/.exec(n.description),c=p?p[1]:i.replace(/_/g," ");x.has(i)||x.set(i,{key:i,label:c,tools:[]}),x.get(i).tools.push(n)}return{builtin:d,groups:[...x.values()].sort((n,a)=>n.label.localeCompare(a.label))}}function le(s){return s.replace(/^mcp__[^_]+(?:_[^_]+)*__/,"")}function re(s){return s.replace(/^\[[^\]]+\]\s*/,"")}function D({tool:s,checked:d,onToggle:x}){return e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[12px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:d,onChange:x}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"font-mono text-ink",children:le(s.name)}),s.description&&e.jsx("span",{className:"text-faint",children:re(s.description)})]})]})}function ie({group:s,selected:d,onToggleAll:x,onToggleOne:n}){const[a,i]=b.useState(!1),p=s.tools.map(l=>l.name),c=p.filter(l=>d.has(l)).length,o=c===p.length,g=c>0&&!o,u=b.useRef(null);return b.useEffect(()=>{u.current&&(u.current.indeterminate=g)},[g]),e.jsxs("div",{className:"rounded-md border border-border",children:[e.jsxs("div",{className:"flex items-center gap-2 px-2.5 py-2",children:[e.jsx("input",{ref:u,type:"checkbox",checked:o,onChange:()=>x(p,!o)}),e.jsxs("button",{type:"button",onClick:()=>{j("agents","toggle_agent_tool_group"),i(l=>!l)},className:"flex flex-1 items-center gap-1.5 text-left text-[13px] text-ink",children:[e.jsx(ne,{size:12,className:q("shrink-0 text-faint transition-transform",a&&"rotate-90")}),s.label,e.jsxs("span",{className:"text-[11px] text-faint",children:["(",c,"/",p.length,")"]})]})]}),a&&e.jsx("div",{className:"flex flex-col gap-1.5 border-t border-border px-3 py-2 pl-9",children:s.tools.map(l=>e.jsx(D,{tool:l,checked:d.has(l.name),onToggle:()=>n(l.name)},l.name))})]})}function ce({tools:s,selected:d,onChange:x}){const n=new Set(d),{builtin:a,groups:i}=oe(s),p=o=>x(n.has(o)?d.filter(g=>g!==o):[...d,o]),c=(o,g)=>{const u=d.filter(l=>!o.includes(l));x(g?[...u,...o]:u)};return s.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No tools available yet."}):e.jsxs("div",{className:"flex flex-col gap-2",children:[a.length>0&&e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:a.map(o=>e.jsx(D,{tool:o,checked:n.has(o.name),onToggle:()=>p(o.name)},o.name))}),i.map(o=>e.jsx(ie,{group:o,selected:n,onToggleAll:c,onToggleOne:p},o.key))]})}function je(){const{agentId:s="new"}=W(),d=$(),x=G(),n=s==="new",a=!n&&B.some(t=>t.id===s),i=a?B.find(t=>t.id===s):void 0,p=H(),c=!n&&!a?(p.data??[]).find(t=>t.id===s):void 0,o=Y(),g=J(),u=U(),l=a?g.data?.[s]:void 0,I=z({queryKey:Z.list(),queryFn:X,staleTime:0}),k=I.data??[],w=z({queryKey:["available-tools"],queryFn:V}),N=w.data??[],[m,f]=b.useState(ae()),[S,T]=b.useState(!1),[A,P]=b.useState(!1),[L,R]=b.useState(!1),[E,Q]=b.useState("skills");n&&!S&&(k.length||N.length)&&(f(t=>({...t,skillIds:k.map(r=>r.id),tools:N.map(r=>r.name)})),T(!0)),!n&&!a&&!S&&c&&(f({name:c.name,description:c.description,systemPrompt:c.systemPrompt,skillIds:c.skillIds,tools:c.tools}),T(!0)),a&&!S&&i&&g.isSuccess&&(k.length||N.length)&&(f({name:l?.name??i.name,description:l?.description??i.description,systemPrompt:l?.systemPrompt??i.systemPrompt,skillIds:l?.skillIds??k.map(t=>t.id),tools:l?.tools??N.map(t=>t.name)}),T(!0));const y=()=>{j("agents","back_to_agents"),d("/customize")},O=t=>f(r=>({...r,skillIds:r.skillIds.includes(t)?r.skillIds.filter(v=>v!==t):[...r.skillIds,t]})),F=()=>{if(!m.name.trim()){h.error("Name is required.");return}P(!0);const t={name:m.name.trim(),description:m.description.trim(),systemPrompt:m.systemPrompt,skillIds:m.skillIds,tools:m.tools};if(a){u.save.mutate({id:s,override:t},{onSuccess:()=>{h.success("Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}});return}const r={onSuccess:()=>{x.invalidateQueries({queryKey:["chat-agents"]}),h.success(n?"Agent created.":"Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}};n?o.add.mutate(t,r):o.update.mutate({id:s,patch:t},r)},K=()=>{u.reset.mutate(s,{onSuccess:()=>{h.success("Reset to default."),i&&f({name:i.name,description:i.description,systemPrompt:i.systemPrompt,skillIds:k.map(t=>t.id),tools:N.map(t=>t.name)})},onError:t=>h.error(t instanceof _?t.message:"Could not reset agent.")})},M=()=>{o.remove.mutate(s,{onSuccess:()=>{h.success("Agent deleted."),y()},onError:t=>h.error(t instanceof _?t.message:"Could not delete agent.")})};return!n&&!a&&p.isSuccess&&!c?e.jsxs("div",{className:"flex w-full flex-col items-center gap-3 px-8 py-16",children:[e.jsx("p",{className:"text-[14px] text-muted",children:"That agent no longer exists."}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Back to agents"})]}):e.jsxs("div",{className:"flex w-full max-w-4xl flex-col gap-5 px-4 py-6 md:px-8",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("button",{type:"button",onClick:y,className:"text-faint hover:text-ink",title:"Back to agents",children:e.jsx(ee,{size:18})}),e.jsx("span",{className:"text-[15px] font-medium text-ink",children:n?"New agent":a?l?"Edit agent (modified built-in)":"Edit agent (built-in)":"Edit agent"}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[a&&e.jsxs(C,{size:"sm",variant:"outline",onClick:()=>{j("agents","reset_agent_to_default"),K()},disabled:u.reset.isPending,children:[e.jsx(te,{size:13})," Reset to default"]}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Cancel"}),e.jsx(C,{size:"sm",onClick:()=>{j("agents","save_agent"),F()},disabled:A||!m.name.trim(),children:A?"Saving…":"Save"})]})]}),a&&e.jsx("p",{className:"rounded-md border border-border bg-panel px-3 py-2 text-[12px] text-muted",children:"Built-in agents can be edited in place — Reset to default discards your changes and restores TurboLLM's original prompt and access."}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Name"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"Release Notes Writer",value:m.name,onChange:t=>f(r=>({...r,name:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Description"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"What this agent is for, in one line",value:m.description,onChange:t=>f(r=>({...r,description:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"System prompt"}),e.jsx("textarea",{className:"min-h-[160px] resize-y rounded-md border border-border bg-bg px-3 py-2 font-mono text-[12px] leading-relaxed text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"You are…",value:m.systemPrompt,onChange:t=>f(r=>({...r,systemPrompt:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-2.5",children:[e.jsx("div",{className:"inline-flex w-fit rounded-md border border-border p-0.5",children:["skills","tools"].map(t=>e.jsx("button",{type:"button",onClick:()=>{j("agents","switch_agent_form_tab"),Q(t)},className:q("rounded px-3 py-1.5 text-[13px] font-medium transition-colors",E===t?"bg-accent/12 text-accent":"text-muted hover:text-ink"),children:t==="skills"?`Skills (${m.skillIds.length})`:`Tools (${m.tools.length})`},t))}),E==="skills"?I.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):k.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No skills yet — create one under Customize → Skills."}):e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:k.map(t=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:m.skillIds.includes(t.id),onChange:()=>O(t.id)}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"text-ink",children:t.name}),t.description&&e.jsx("span",{className:"text-[12px] text-muted",children:t.description})]})]},t.id))}):w.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):e.jsx(ce,{tools:N,selected:m.tools,onChange:t=>f(r=>({...r,tools:t}))})]}),!n&&!a&&(L?e.jsxs("div",{className:"flex items-center gap-2 self-start rounded-md border border-border bg-panel p-2 text-[12px]",children:[e.jsx("span",{className:"flex-1 text-muted",children:"Delete this agent?"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","delete_agent"),M()},className:"rounded px-2 py-1 text-[color:var(--err)] hover:bg-[color:color-mix(in_srgb,var(--err)_12%,transparent)]",children:"Delete"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","cancel_delete_agent"),R(!1)},className:"rounded px-2 py-1 text-faint hover:text-ink",children:"Cancel"})]}):e.jsxs("button",{type:"button",onClick:()=>R(!0),className:"flex items-center gap-1.5 self-start text-[12px] text-faint hover:text-[color:var(--err)]",children:[e.jsx(se,{size:13})," Delete agent"]}))]})}export{je as AgentEditPage};