turbollm 1.6.0 → 1.6.2

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 (101) hide show
  1. package/README.md +7 -4
  2. package/dist/cli.js +206 -44
  3. package/dist/webdist/assets/{AgentsScreen-qd7_yqju.js → AgentsScreen-BviDj463.js} +1 -1
  4. package/dist/webdist/assets/ChatScreen-4a_6ZX6c.js +29 -0
  5. package/dist/webdist/assets/CustomizeScreen-BhZJK7xv.js +4 -0
  6. package/dist/webdist/assets/{DeveloperScreen-CCWBQQ4y.js → DeveloperScreen-8CVekggw.js} +1 -1
  7. package/dist/webdist/assets/EnginesScreen-zHLzht8k.js +7 -0
  8. package/dist/webdist/assets/ModelDetailDialog-DjMSf0rz.js +29 -0
  9. package/dist/webdist/assets/{ModelDirs-E_tFMIjS.js → ModelDirs-CIOcl5qu.js} +1 -1
  10. package/dist/webdist/assets/ModelsScreen-v7kDcWmr.js +2 -0
  11. package/dist/webdist/assets/{SettingsScreen-BFhQBO1d.js → SettingsScreen-DnSgBps7.js} +1 -1
  12. package/dist/webdist/assets/alert-dialog-93rFXd1_.js +1 -0
  13. package/dist/webdist/assets/{arc-820YYJ1l.js → arc-BFc9YP9w.js} +1 -1
  14. package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-BTSJZjgr.js → architectureDiagram-3BPJPVTR-Bd6WFbHz.js} +1 -1
  15. package/dist/webdist/assets/{arrow-left-BJXqu8NS.js → arrow-left--NjoPnor.js} +1 -1
  16. package/dist/webdist/assets/{blockDiagram-GPEHLZMM-E_gowUIA.js → blockDiagram-GPEHLZMM-Dupawk92.js} +1 -1
  17. package/dist/webdist/assets/{c4Diagram-AAUBKEIU-DwJcumW7.js → c4Diagram-AAUBKEIU-BMhAUE8i.js} +1 -1
  18. package/dist/webdist/assets/channel-BCSKocRg.js +1 -0
  19. package/dist/webdist/assets/check-Bu2UFiB0.js +1 -0
  20. package/dist/webdist/assets/chevron-right-kiIQKgde.js +1 -0
  21. package/dist/webdist/assets/{chunk-2J33WTMH-DWJ9qmM_.js → chunk-2J33WTMH-BhxySqcq.js} +1 -1
  22. package/dist/webdist/assets/{chunk-4BX2VUAB-DgyEqU31.js → chunk-4BX2VUAB-B-UGkx88.js} +1 -1
  23. package/dist/webdist/assets/{chunk-55IACEB6-CsI85v4r.js → chunk-55IACEB6-BDvjmk8T.js} +1 -1
  24. package/dist/webdist/assets/{chunk-727SXJPM-YRdSK3U3.js → chunk-727SXJPM-BhyFFQxZ.js} +1 -1
  25. package/dist/webdist/assets/{chunk-AQP2D5EJ-BnTfokPI.js → chunk-AQP2D5EJ-VTSW3fZJ.js} +1 -1
  26. package/dist/webdist/assets/{chunk-FMBD7UC4-Dt8O7iM6.js → chunk-FMBD7UC4-QKx1b_-m.js} +1 -1
  27. package/dist/webdist/assets/{chunk-ND2GUHAM-OsM9VDgQ.js → chunk-ND2GUHAM-DZANzpY0.js} +1 -1
  28. package/dist/webdist/assets/{chunk-QZHKN3VN-CW3L84fX.js → chunk-QZHKN3VN-DVZIy115.js} +1 -1
  29. package/dist/webdist/assets/circle-check-C4aACGyb.js +1 -0
  30. package/dist/webdist/assets/circle-x-koFsrlGA.js +1 -0
  31. package/dist/webdist/assets/classDiagram-4FO5ZUOK-B_Eb_jRj.js +1 -0
  32. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-B_Eb_jRj.js +1 -0
  33. package/dist/webdist/assets/common-CQVogVIs.js +1 -0
  34. package/dist/webdist/assets/{copy-button-DrRexlJn.js → copy-button-3_2Us2zb.js} +1 -1
  35. package/dist/webdist/assets/{cose-bilkent-S5V4N54A-cNqjZyGk.js → cose-bilkent-S5V4N54A-Ci07N3_4.js} +1 -1
  36. package/dist/webdist/assets/{dagre-BM42HDAG-zr_aSu4P.js → dagre-BM42HDAG-45qc0phu.js} +1 -1
  37. package/dist/webdist/assets/{diagram-2AECGRRQ-D8fYvWd0.js → diagram-2AECGRRQ-DA3u3hzE.js} +1 -1
  38. package/dist/webdist/assets/{diagram-5GNKFQAL-Cp2jBV3j.js → diagram-5GNKFQAL-B3wpvPKp.js} +1 -1
  39. package/dist/webdist/assets/{diagram-KO2AKTUF-SdWp_DPO.js → diagram-KO2AKTUF-h1qR3ckq.js} +1 -1
  40. package/dist/webdist/assets/{diagram-LMA3HP47-DHwQz9MP.js → diagram-LMA3HP47-B3uU2roT.js} +1 -1
  41. package/dist/webdist/assets/{diagram-OG6HWLK6-FGejDPqJ.js → diagram-OG6HWLK6-CibVtZrO.js} +1 -1
  42. package/dist/webdist/assets/dialog-Dn3dxmMP.js +1 -0
  43. package/dist/webdist/assets/{erDiagram-TEJ5UH35-Ci0ArYoY.js → erDiagram-TEJ5UH35-VsHZBAOZ.js} +1 -1
  44. package/dist/webdist/assets/{flowDiagram-I6XJVG4X-g96T9cRZ.js → flowDiagram-I6XJVG4X-DhYah8sF.js} +1 -1
  45. package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-CFGWWtss.js → ganttDiagram-6RSMTGT7-RPkNEJnH.js} +1 -1
  46. package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-FFeX8xGw.js → gitGraphDiagram-PVQCEYII-JknfsYQp.js} +1 -1
  47. package/dist/webdist/assets/index-5NYGYU_t.css +1 -0
  48. package/dist/webdist/assets/{index-CM4wnT7v.js → index-K2ZQBdky.js} +4 -4
  49. package/dist/webdist/assets/{infoDiagram-5YYISTIA-CTuVSOzN.js → infoDiagram-5YYISTIA-BeGMPySd.js} +1 -1
  50. package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-BMyIM99h.js → ishikawaDiagram-YF4QCWOH-D38Q1-tz.js} +1 -1
  51. package/dist/webdist/assets/{journeyDiagram-JHISSGLW-CcKLWYl8.js → journeyDiagram-JHISSGLW-CG8TFzp6.js} +1 -1
  52. package/dist/webdist/assets/{kanban-definition-UN3LZRKU-CuzVk9Q_.js → kanban-definition-UN3LZRKU-CNgUZ1rd.js} +1 -1
  53. package/dist/webdist/assets/{linear-DvYrxcXL.js → linear-DYIxmEeo.js} +1 -1
  54. package/dist/webdist/assets/{mermaid.core-DIGvaW9x.js → mermaid.core-CZhJJFjL.js} +4 -4
  55. package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-Dh_7fzN2.js → mindmap-definition-RKZ34NQL-BIVYSbvh.js} +1 -1
  56. package/dist/webdist/assets/pencil-iVnKKeCp.js +1 -0
  57. package/dist/webdist/assets/{personas-DdUgL1dH.js → personas-DslX_qdt.js} +4 -4
  58. package/dist/webdist/assets/{pieDiagram-4H26LBE5-G_BsYb0k.js → pieDiagram-4H26LBE5-CU9qALXo.js} +1 -1
  59. package/dist/webdist/assets/plus-mSm32jpN.js +1 -0
  60. package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-B1DekSy3.js → quadrantDiagram-W4KKPZXB-AE50YmvR.js} +1 -1
  61. package/dist/webdist/assets/{radix-4XwKfE88.js → radix-9WP-OkMY.js} +2 -2
  62. package/dist/webdist/assets/refresh-cw-C04dn1r1.js +1 -0
  63. package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-DPYkFc77.js → requirementDiagram-4Y6WPE33-Crymka5P.js} +1 -1
  64. package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-HfCQTOkK.js → sankeyDiagram-5OEKKPKP-DRviwCen.js} +1 -1
  65. package/dist/webdist/assets/save-BT2nzRB6.js +1 -0
  66. package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-C71xQD9L.js → sequenceDiagram-3UESZ5HK-DuBllsfJ.js} +1 -1
  67. package/dist/webdist/assets/skeleton-BfMJjCGZ.js +1 -0
  68. package/dist/webdist/assets/{stateDiagram-AJRCARHV-C0m1fR_s.js → stateDiagram-AJRCARHV-Cj4BwRLl.js} +1 -1
  69. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-Y25sKBZm.js +1 -0
  70. package/dist/webdist/assets/{timeline-definition-PNZ67QCA-B5SB89Kk.js → timeline-definition-PNZ67QCA-BWnH2-RY.js} +1 -1
  71. package/dist/webdist/assets/trash-2-Ce39UFy5.js +1 -0
  72. package/dist/webdist/assets/{vendor-CKtmMAvG.js → vendor-W5GIuFcv.js} +1 -1
  73. package/dist/webdist/assets/{vennDiagram-CIIHVFJN-Bs3E_UnU.js → vennDiagram-CIIHVFJN-GgdSx9c7.js} +1 -1
  74. package/dist/webdist/assets/{wardley-L42UT6IY-D-lZEV6Q.js → wardley-L42UT6IY-C9XKbMb6.js} +1 -1
  75. package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-DIiAQmgm.js → wardleyDiagram-YWT4CUSO-ZxceNtWm.js} +1 -1
  76. package/dist/webdist/assets/{wrench-C7hemMKG.js → wrench-Ceqe74n2.js} +1 -1
  77. package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-DViSTAkf.js → xychartDiagram-2RQKCTM6-C-mAbbHW.js} +1 -1
  78. package/dist/webdist/index.html +4 -4
  79. package/package.json +1 -1
  80. package/dist/webdist/assets/ChatScreen-DYeJQoLV.js +0 -57
  81. package/dist/webdist/assets/CustomizeScreen-DBXBuQPm.js +0 -4
  82. package/dist/webdist/assets/EnginesScreen-DQQLI0Qv.js +0 -7
  83. package/dist/webdist/assets/ModelDetailDialog-C-dRTp-J.js +0 -1
  84. package/dist/webdist/assets/ModelsScreen-JeCIZIdr.js +0 -1
  85. package/dist/webdist/assets/channel-6NEG4b4S.js +0 -1
  86. package/dist/webdist/assets/check-GYT4Bnhm.js +0 -1
  87. package/dist/webdist/assets/chevron-right-BTg0-ylt.js +0 -1
  88. package/dist/webdist/assets/circle-check-iwGQW5-3.js +0 -1
  89. package/dist/webdist/assets/circle-x-CDfcuELr.js +0 -1
  90. package/dist/webdist/assets/classDiagram-4FO5ZUOK-jpAzq28n.js +0 -1
  91. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-jpAzq28n.js +0 -1
  92. package/dist/webdist/assets/common-BstanhE0.js +0 -1
  93. package/dist/webdist/assets/dialog-Cpwjl0SL.js +0 -1
  94. package/dist/webdist/assets/index-CJj8sGhF.css +0 -1
  95. package/dist/webdist/assets/pencil-veezqsms.js +0 -1
  96. package/dist/webdist/assets/plus-BLEwDzPR.js +0 -1
  97. package/dist/webdist/assets/refresh-cw-ByeAabM7.js +0 -1
  98. package/dist/webdist/assets/save-CuyovDOw.js +0 -1
  99. package/dist/webdist/assets/skeleton-XeFkC2WF.js +0 -1
  100. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-C1sHKz6d.js +0 -1
  101. package/dist/webdist/assets/trash-2-MMGQet84.js +0 -1
package/README.md CHANGED
@@ -136,9 +136,10 @@ the detail:
136
136
  - **Use the folders you already have.** Point TurboLLM at any directory of GGUFs — your
137
137
  existing LM Studio / Ollama / manual downloads — **no re-downloading.** It parses GGUF
138
138
  metadata (arch, params, quant, context, vision) for every file.
139
- - **Browse & download from Hugging Face**, in-app: search, see the file tree, pick a quant,
140
- and download with **resume + SHA-256 verification**. Gated models (Llama, Gemma) work via
141
- your own HF token, which **never leaves your machine**.
139
+ - **Browse & download from Hugging Face**, in-app: a live, sortable list (trending / downloads
140
+ / likes / recently updated / newest) alongside a permanent detail pane — pick a quant, read
141
+ the **rendered model card**, and download with **resume + SHA-256 verification**. Gated
142
+ models (Llama, Gemma) work via your own HF token, which **never leaves your machine**.
142
143
  - **Import from any URL** — not just Hugging Face. Paste a direct `.gguf` link (model-author
143
144
  sites, mirrors, private servers); it disk-space-checks and downloads through the same manager.
144
145
  - **Quant recommendation per GPU** and a **VRAM-fit verdict** so you pick a quant that
@@ -319,7 +320,9 @@ the engine as a swappable component.
319
320
  support are hidden; ones it adds (e.g. low-bit KV cache, NextN) light up.
320
321
 
321
322
  No prebuilt for your OS? The **build-from-source guide** checks your toolchain (git / CMake /
322
- CUDA / MSVC), hands you the exact build commands, then drops you into the folder scan above.
323
+ CUDA / a compiler — MSVC on Windows, gcc/clang on Linux), hands you the exact build commands
324
+ (or a 1-click **"Build it for me"** on Windows and Linux), then drops you into the folder scan
325
+ above.
323
326
 
324
327
  **Auto-provisioned default.** Don't want to fetch anything? On first run TurboLLM downloads
325
328
  the right upstream prebuilt for your GPU automatically — and a **backend picker** lets you
package/dist/cli.js CHANGED
@@ -1427,7 +1427,12 @@ function readinessTimeoutMs(kind) {
1427
1427
  return kind === "vllm" || kind === "sglang" ? 6e5 : 12e4;
1428
1428
  }
1429
1429
  function pyEngineEnv(kind, dataDir, binPath) {
1430
- if (kind !== "mlx" && kind !== "vllm" && kind !== "sglang") return void 0;
1430
+ if (kind !== "mlx" && kind !== "vllm" && kind !== "sglang") {
1431
+ if (process.platform === "win32") return void 0;
1432
+ const dir = dirname2(binPath);
1433
+ const existing = process.env.LD_LIBRARY_PATH;
1434
+ return { ...process.env, LD_LIBRARY_PATH: existing ? `${dir}:${existing}` : dir };
1435
+ }
1431
1436
  const hfHome = join9(dataDir, "hf-cache");
1432
1437
  const hubCache = join9(hfHome, "hub");
1433
1438
  mkdirSync6(hubCache, { recursive: true });
@@ -3746,7 +3751,15 @@ function resolveProfile(m, sys, saved, overrides, defaults) {
3746
3751
  // missing some fields) keeps the rest of the defaults instead of going undefined.
3747
3752
  gpu: { ...base2.gpu, ...saved?.gpu ?? {}, ...overrides?.gpu ?? {} },
3748
3753
  // vllm deep-merged for the same reason — old/partial profiles keep the defaults.
3749
- vllm: { ...base2.vllm, ...saved?.vllm ?? {}, ...overrides?.vllm ?? {} }
3754
+ vllm: { ...base2.vllm, ...saved?.vllm ?? {}, ...overrides?.vllm ?? {} },
3755
+ // useMmproj has no UI control (only mmprojGpu — GPU-vs-CPU placement — is user-facing;
3756
+ // see ModelDetailDialog's "Vision encoder on GPU" toggle), so there is no legitimate way
3757
+ // for a saved profile or draft override to carry a meaningful `false` here. Forcing it to
3758
+ // track the model's own vision capability makes it self-healing: any profile saved with a
3759
+ // stale/incorrect useMmproj (e.g. auto-tune runs prior to the bench.ts fix that persisted
3760
+ // false) is corrected on the very next resolve, for every user, with no reset/migration
3761
+ // needed and no other tuned setting touched.
3762
+ useMmproj: m.vision
3750
3763
  };
3751
3764
  }
3752
3765
  function profileToArgs(p, m, caps, cores = 0) {
@@ -4140,6 +4153,28 @@ var HfError = class extends Error {
4140
4153
  }
4141
4154
  code;
4142
4155
  };
4156
+ var SORT_PARAM = {
4157
+ trending: "trendingScore",
4158
+ downloads: "downloads",
4159
+ likes: "likes",
4160
+ modified: "lastModified",
4161
+ created: "createdAt"
4162
+ };
4163
+ function libraryFilterFor(engineKind) {
4164
+ if (engineKind === "mlx") return "filter=mlx&";
4165
+ if (engineKind === "vllm") return "";
4166
+ return "filter=gguf&";
4167
+ }
4168
+ function toSearchItem(m) {
4169
+ return {
4170
+ repo: m.id ?? m.modelId ?? "",
4171
+ downloads: m.downloads ?? 0,
4172
+ likes: m.likes ?? 0,
4173
+ updatedAt: m.lastModified ?? m.createdAt ?? "",
4174
+ gated: m.gated === true || m.gated === "auto" || m.gated === "manual",
4175
+ tags: Array.isArray(m.tags) ? m.tags : []
4176
+ };
4177
+ }
4143
4178
  var SPLIT_RE2 = /^(.*)-(\d{5})-of-(\d{5})\.gguf$/i;
4144
4179
  var HfClient = class {
4145
4180
  constructor(tokenFn, version2) {
@@ -4149,24 +4184,26 @@ var HfClient = class {
4149
4184
  tokenFn;
4150
4185
  version;
4151
4186
  cache = /* @__PURE__ */ new Map();
4152
- /** Search repos (spec 10 §2). Returns up to 30 rows sorted by downloads.
4153
- * Format filter adapts to the active engine kind:
4154
- * - llama-server / TurboQuant filter=gguf
4155
- * - mlx → filter=mlx (HF library tag)
4156
- * - vllm → no format filter (searches all HF repos) */
4157
- async searchModels(query, engineKind) {
4187
+ /** Search repos by text (spec 10 §2). Returns up to 30 rows. `sort` defaults to HF's own
4188
+ * relevance ranking ('best-match' omits `sort=` entirely, a genuinely different order
4189
+ * from `sort=downloads`, not just an alias for it). Format filter adapts to the active
4190
+ * engine kind (never hardcoded to GGUF) — see {@link libraryFilterFor}. */
4191
+ async searchModels(query, engineKind, sort = "best-match") {
4158
4192
  const q = query.trim();
4159
- const formatFilter = engineKind === "mlx" ? "&filter=mlx" : engineKind === "vllm" ? "" : "&filter=gguf";
4160
- const url = `${BASE}/api/models?search=${encodeURIComponent(q)}${formatFilter}&sort=downloads&direction=-1&limit=30&full=false`;
4193
+ const sortParam = sort === "best-match" ? "" : `sort=${SORT_PARAM[sort]}&direction=-1&`;
4194
+ const url = `${BASE}/api/models?search=${encodeURIComponent(q)}&${libraryFilterFor(engineKind)}${sortParam}limit=30&full=false`;
4161
4195
  const raw = await this.getJson(url);
4162
- return raw.map((m) => ({
4163
- repo: m.id ?? m.modelId ?? "",
4164
- downloads: m.downloads ?? 0,
4165
- likes: m.likes ?? 0,
4166
- updatedAt: m.lastModified ?? m.createdAt ?? "",
4167
- gated: m.gated === true || m.gated === "auto" || m.gated === "manual",
4168
- tags: Array.isArray(m.tags) ? m.tags : []
4169
- }));
4196
+ return raw.map(toSearchItem);
4197
+ }
4198
+ /** Browse repos with no search term (spec 10 §7 rewrite) — the live equivalent of
4199
+ * https://huggingface.co/models?library=gguf&sort=trending, replacing what used to be a
4200
+ * hardcoded "Featured" list. 'best-match' has no meaning without a query, so it falls
4201
+ * back to 'trending'. Format filter adapts to the active engine kind, same as search. */
4202
+ async browseModels(sort, engineKind, limit = 30) {
4203
+ const effective = sort === "best-match" ? "trending" : sort;
4204
+ const url = `${BASE}/api/models?${libraryFilterFor(engineKind)}sort=${SORT_PARAM[effective]}&direction=-1&limit=${limit}&full=false`;
4205
+ const raw = await this.getJson(url);
4206
+ return raw.map(toSearchItem);
4170
4207
  }
4171
4208
  /** Repo detail (spec 10 §3): card data + the GGUF file tree, with split parts
4172
4209
  * grouped and quant/mmproj detected per file. */
@@ -4991,7 +5028,7 @@ var BenchRunner = class {
4991
5028
  const saved = this.store.snapshot().modelProfiles[modelKey];
4992
5029
  const defaults = this.store.snapshot().modelDefaults;
4993
5030
  const resolved = resolveProfile(entry, sys, saved, base2, defaults);
4994
- const baseProfile = { ...resolved, speculative: "off", mtpHeadPath: "", draftModelPath: "", useMmproj: false };
5031
+ const baseProfile = { ...resolved, speculative: "off", mtpHeadPath: "", draftModelPath: "" };
4995
5032
  const results = [];
4996
5033
  let best = null;
4997
5034
  const kvCandidates = pickKvQuants(baseProfile.kvTypeK, caps.kvTypes);
@@ -9115,6 +9152,36 @@ var ALL = [
9115
9152
  // install endpoint of its own.
9116
9153
  installEndpoint: ""
9117
9154
  },
9155
+ {
9156
+ // Upstream publishes NO Linux CUDA prebuilt (download.ts availableBackends() — Linux
9157
+ // NVIDIA users get Vulkan from the main llama.cpp entry above). Now that the guided
9158
+ // build (ADR-100/build-runner.ts) runs on Linux too, offer official llama.cpp + CUDA
9159
+ // there as its own "build from source" card — same guided-build flow as ik_llama.cpp/
9160
+ // TurboQuant below, just pointed at the official repo. Windows/macOS never see this
9161
+ // (platforms:['linux']): Windows already gets a CUDA prebuilt, and macOS has no CUDA.
9162
+ id: "llama.cpp-cuda-linux",
9163
+ name: "llama.cpp (CUDA)",
9164
+ kind: "llama-server",
9165
+ description: "Official llama.cpp compiled with CUDA for NVIDIA GPUs on Linux \u2014 no prebuilt is published upstream, so this uses the guided in-app build.",
9166
+ provision: "github-release",
9167
+ homepage: "https://github.com/ggml-org/llama.cpp",
9168
+ repo: "ggml-org/llama.cpp",
9169
+ platforms: ["linux"],
9170
+ support: "experimental",
9171
+ installEndpoint: "",
9172
+ 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).",
9173
+ variants: [
9174
+ {
9175
+ id: "llama.cpp-cuda-source",
9176
+ label: "CUDA (NVIDIA) \u2014 build from source",
9177
+ repo: "ggml-org/llama.cpp",
9178
+ requires: { platform: ["linux"], gpuVendor: ["nvidia"] },
9179
+ stability: "experimental",
9180
+ speed: "fast",
9181
+ hasPrebuilt: false
9182
+ }
9183
+ ]
9184
+ },
9118
9185
  {
9119
9186
  id: "vllm",
9120
9187
  name: "vLLM",
@@ -9369,7 +9436,8 @@ var INSTALL_URLS = {
9369
9436
  cmake: "https://cmake.org/download/",
9370
9437
  cuda: "https://developer.nvidia.com/cuda-downloads",
9371
9438
  // The "Desktop development with C++" workload from the VS Build Tools installer.
9372
- msvc: "https://visualstudio.microsoft.com/downloads/"
9439
+ msvc: "https://visualstudio.microsoft.com/downloads/",
9440
+ gcc: "https://gcc.gnu.org/install/"
9373
9441
  };
9374
9442
  async function runVersion(cmd, args, env) {
9375
9443
  const { stdout, stderr } = await execFileP5(cmd, args, {
@@ -9392,6 +9460,10 @@ function parseNvccVersion(out) {
9392
9460
  const m = out.match(/release\s+([\d.]+)/i);
9393
9461
  return m ? m[1] : "";
9394
9462
  }
9463
+ function parseCompilerVersion(out) {
9464
+ const m = out.match(/(?:g\+\+|gcc|clang(?:\+\+)?)[^\d]*(\d+(?:\.\d+)*)/i);
9465
+ return m ? m[1] : "";
9466
+ }
9395
9467
  async function checkGit(env) {
9396
9468
  let found = false;
9397
9469
  let version2;
@@ -9453,13 +9525,30 @@ async function checkMsvc(env) {
9453
9525
  installUrl: INSTALL_URLS.msvc
9454
9526
  };
9455
9527
  }
9528
+ async function checkGcc(env) {
9529
+ let found = false;
9530
+ let version2;
9531
+ try {
9532
+ version2 = parseCompilerVersion(await runVersion("g++", ["--version"], env)) || void 0;
9533
+ found = true;
9534
+ } catch {
9535
+ try {
9536
+ version2 = parseCompilerVersion(await runVersion("clang++", ["--version"], env)) || void 0;
9537
+ found = true;
9538
+ } catch {
9539
+ found = false;
9540
+ }
9541
+ }
9542
+ return { id: "gcc", name: "C++ compiler (g++/clang++)", found, version: version2, installUrl: INSTALL_URLS.gcc };
9543
+ }
9456
9544
  async function checkBuildPrereqs(toolchainDirs = []) {
9457
- if (process.platform !== "win32") {
9458
- return { supported: false, tools: [] };
9545
+ const platform = process.platform;
9546
+ if (platform !== "win32" && platform !== "linux") {
9547
+ return { supported: false, os: "other", tools: [] };
9459
9548
  }
9460
9549
  const env = buildEnv(toolchainDirs);
9461
- const tools = await Promise.all([checkGit(env), checkCmake(env), checkCuda(env), checkMsvc(env)]);
9462
- return { supported: true, tools };
9550
+ const tools = platform === "win32" ? await Promise.all([checkGit(env), checkCmake(env), checkCuda(env), checkMsvc(env)]) : await Promise.all([checkGit(env), checkCmake(env), checkCuda(env), checkGcc(env)]);
9551
+ return { supported: true, os: platform === "win32" ? "windows" : "linux", tools };
9463
9552
  }
9464
9553
 
9465
9554
  // src/engines/build-runner.ts
@@ -9489,8 +9578,9 @@ function sourceBuildDirOf(binPath, enginesRoot) {
9489
9578
  if (!m) return null;
9490
9579
  return join19(enginesRoot, "build", m[1]);
9491
9580
  }
9492
- function pickGenerator(hasNinja) {
9493
- return hasNinja ? "Ninja" : "NMake Makefiles";
9581
+ function pickGenerator(hasNinja, isWindows) {
9582
+ if (hasNinja) return "Ninja";
9583
+ return isWindows ? "NMake Makefiles" : "Unix Makefiles";
9494
9584
  }
9495
9585
  var CMAKE_CONFIGURE_ARGS = ["-DGGML_CUDA=ON", "-DCMAKE_BUILD_TYPE=Release"];
9496
9586
  function vcvarsBatch(vcvars, cmakeArgs) {
@@ -9565,6 +9655,56 @@ function copyCudaRuntimeDlls(env, destDir, log) {
9565
9655
  if (copied > 0) log(`Bundled ${copied} CUDA runtime DLL(s) next to the binary so the engine is self-contained.`);
9566
9656
  return copied;
9567
9657
  }
9658
+ var CUDA_RUNTIME_SO_PREFIXES = ["libcudart.so", "libcublas.so", "libcublasLt.so", "libnvrtc.so", "libnvrtc-builtins.so", "libnvJitLink.so"];
9659
+ function cudaSoSourceDirs(env) {
9660
+ const key = Object.keys(env).find((k) => k.toLowerCase() === "path") ?? "PATH";
9661
+ const pathDirs = (env[key] ?? "").split(delimiter2).filter(Boolean);
9662
+ const nvccDir = pathDirs.find((dir) => {
9663
+ try {
9664
+ return existsSync17(join19(dir, "nvcc"));
9665
+ } catch {
9666
+ return false;
9667
+ }
9668
+ });
9669
+ if (!nvccDir) return [];
9670
+ const toolkitRoot = dirname6(nvccDir);
9671
+ return [join19(toolkitRoot, "lib64"), join19(toolkitRoot, "lib"), join19(toolkitRoot, "targets", "x86_64-linux", "lib")].filter((d) => {
9672
+ try {
9673
+ return existsSync17(d);
9674
+ } catch {
9675
+ return false;
9676
+ }
9677
+ });
9678
+ }
9679
+ function copyCudaRuntimeLibs(env, destDir, log) {
9680
+ const sources = cudaSoSourceDirs(env);
9681
+ if (sources.length === 0) {
9682
+ log("Note: could not find the CUDA toolkit shared libraries to bundle \u2014 the engine may need LD_LIBRARY_PATH set to run.");
9683
+ return 0;
9684
+ }
9685
+ const seen = /* @__PURE__ */ new Set();
9686
+ let copied = 0;
9687
+ for (const dir of sources) {
9688
+ let names;
9689
+ try {
9690
+ names = readdirSync5(dir);
9691
+ } catch {
9692
+ continue;
9693
+ }
9694
+ for (const name of names) {
9695
+ if (seen.has(name)) continue;
9696
+ if (!CUDA_RUNTIME_SO_PREFIXES.some((p) => name.startsWith(p))) continue;
9697
+ try {
9698
+ copyFileSync(join19(dir, name), join19(destDir, name));
9699
+ seen.add(name);
9700
+ copied++;
9701
+ } catch {
9702
+ }
9703
+ }
9704
+ }
9705
+ if (copied > 0) log(`Bundled ${copied} CUDA runtime librar${copied === 1 ? "y" : "ies"} next to the binary so the engine is self-contained.`);
9706
+ return copied;
9707
+ }
9568
9708
  async function findVcvarsall() {
9569
9709
  try {
9570
9710
  const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
@@ -9683,10 +9823,11 @@ function neutralizeGratuitousAsm(srcDir, log) {
9683
9823
  }
9684
9824
  }
9685
9825
  async function runBuild(req, hooks, signal) {
9826
+ const isWindows = process.platform === "win32";
9686
9827
  const env = { ...buildEnv(req.toolchainDirs), GIT_TERMINAL_PROMPT: "0", GCM_INTERACTIVE: "never" };
9687
9828
  hooks.phase("preparing");
9688
9829
  const prereqs = await checkBuildPrereqs(req.toolchainDirs);
9689
- if (!prereqs.supported) throw new Error("In-app build is currently Windows + CUDA only.");
9830
+ if (!prereqs.supported) throw new Error("In-app build is currently Windows or Linux (with CUDA) only.");
9690
9831
  const missing = prereqs.tools.filter((t) => (t.id === "git" || t.id === "cmake" || t.id === "cuda") && !t.found);
9691
9832
  if (missing.length > 0) {
9692
9833
  const names = missing.map((t) => t.name).join(", ");
@@ -9694,10 +9835,17 @@ async function runBuild(req, hooks, signal) {
9694
9835
  `Missing build prerequisite(s): ${names}. Install them, or \u2014 if they live in a conda env / custom path \u2014 add that folder under Build environment so TurboLLM can find them.`
9695
9836
  );
9696
9837
  }
9697
- const vcvars = await findVcvarsall();
9698
- if (!vcvars) {
9838
+ let vcvars = null;
9839
+ if (isWindows) {
9840
+ vcvars = await findVcvarsall();
9841
+ if (!vcvars) {
9842
+ throw new Error(
9843
+ 'Could not locate the Visual Studio C++ build environment (vcvarsall.bat). Install the "Desktop development with C++" workload from the Visual Studio Build Tools.'
9844
+ );
9845
+ }
9846
+ } else if (!prereqs.tools.some((t) => t.id === "gcc" && t.found)) {
9699
9847
  throw new Error(
9700
- 'Could not locate the Visual Studio C++ build environment (vcvarsall.bat). Install the "Desktop development with C++" workload from the Visual Studio Build Tools.'
9848
+ "Could not find a C++ compiler. Install one (e.g. `sudo apt install build-essential` on Debian/Ubuntu) and retry."
9701
9849
  );
9702
9850
  }
9703
9851
  const buildRoot = join19(req.enginesRoot, "build", buildDirName(req.repoUrl, req.branch));
@@ -9713,24 +9861,35 @@ async function runBuild(req, hooks, signal) {
9713
9861
  const commit = (await runStep("git", ["-C", srcDir, "rev-parse", "HEAD"], { env, signal, onLine: () => {
9714
9862
  } })).trim();
9715
9863
  neutralizeGratuitousAsm(srcDir, hooks.log);
9716
- const generator = pickGenerator(onPath(env, "ninja.exe"));
9864
+ const generator = pickGenerator(onPath(env, isWindows ? "ninja.exe" : "ninja"), isWindows);
9717
9865
  hooks.log(
9718
- generator === "Ninja" ? "Using the Ninja generator (drives nvcc directly \u2014 no Visual Studio CUDA integration needed)." : "Ninja not found on PATH \u2014 using the NMake generator (works, but single-threaded and slower; add a folder containing ninja.exe under Build environment for much faster builds)."
9866
+ generator === "Ninja" ? "Using the Ninja generator (drives nvcc directly \u2014 no Visual Studio CUDA integration needed)." : isWindows ? "Ninja not found on PATH \u2014 using the NMake generator (works, but single-threaded and slower; add a folder containing ninja.exe under Build environment for much faster builds)." : "Ninja not found on PATH \u2014 using Unix Makefiles (works, but single-threaded and slower; add a folder containing ninja under Build environment for much faster builds)."
9719
9867
  );
9720
9868
  hooks.phase("configuring");
9721
- const configureBat = join19(buildRoot, "_tllm_configure.bat");
9722
- writeFileSync8(configureBat, vcvarsBatch(vcvars, ["-G", generator, "-B", buildSubdir, "-S", srcDir, ...CMAKE_CONFIGURE_ARGS]));
9723
- await runStep("cmd.exe", ["/c", configureBat], { cwd: buildRoot, env, signal, onLine: hooks.log });
9869
+ const configureArgs = ["-G", generator, "-B", buildSubdir, "-S", srcDir, ...CMAKE_CONFIGURE_ARGS];
9870
+ if (isWindows) {
9871
+ const configureBat = join19(buildRoot, "_tllm_configure.bat");
9872
+ writeFileSync8(configureBat, vcvarsBatch(vcvars, configureArgs));
9873
+ await runStep("cmd.exe", ["/c", configureBat], { cwd: buildRoot, env, signal, onLine: hooks.log });
9874
+ } else {
9875
+ await runStep("cmake", configureArgs, { cwd: buildRoot, env, signal, onLine: hooks.log });
9876
+ }
9724
9877
  hooks.phase("compiling");
9725
- const compileBat = join19(buildRoot, "_tllm_build.bat");
9726
- writeFileSync8(compileBat, vcvarsBatch(vcvars, ["--build", buildSubdir, "-j", "--target", "llama-server"]));
9727
- await runStep("cmd.exe", ["/c", compileBat], { cwd: buildRoot, env, signal, onLine: hooks.log });
9878
+ const compileArgs = ["--build", buildSubdir, "-j", "--target", "llama-server"];
9879
+ if (isWindows) {
9880
+ const compileBat = join19(buildRoot, "_tllm_build.bat");
9881
+ writeFileSync8(compileBat, vcvarsBatch(vcvars, compileArgs));
9882
+ await runStep("cmd.exe", ["/c", compileBat], { cwd: buildRoot, env, signal, onLine: hooks.log });
9883
+ } else {
9884
+ await runStep("cmake", compileArgs, { cwd: buildRoot, env, signal, onLine: hooks.log });
9885
+ }
9728
9886
  hooks.phase("registering");
9729
9887
  const binPath = resolveServerBinary(buildSubdir) ?? resolveServerBinary(buildRoot);
9730
9888
  if (!binPath) {
9731
9889
  throw new Error("Build finished but no llama-server binary was found in the output \u2014 see the log above.");
9732
9890
  }
9733
- copyCudaRuntimeDlls(env, dirname6(binPath), hooks.log);
9891
+ if (isWindows) copyCudaRuntimeDlls(env, dirname6(binPath), hooks.log);
9892
+ else copyCudaRuntimeLibs(env, dirname6(binPath), hooks.log);
9734
9893
  return { binPath, commit, buildRoot };
9735
9894
  }
9736
9895
 
@@ -10345,8 +10504,8 @@ function registerApi(app2, d) {
10345
10504
  app2.post("/api/v1/build/run", async (c) => {
10346
10505
  if (!isLocalRequest(c, d))
10347
10506
  return err2(c, 403, "forbidden", "Building an engine is only available on the machine running TurboLLM.");
10348
- if (process.platform !== "win32")
10349
- return err2(c, 409, "unsupported_platform", "In-app build is currently Windows + CUDA only.");
10507
+ if (process.platform !== "win32" && process.platform !== "linux")
10508
+ return err2(c, 409, "unsupported_platform", "In-app build is currently Windows or Linux (with CUDA) only.");
10350
10509
  const b = await body2(c);
10351
10510
  const repoUrl = (b.repoUrl ?? "").trim();
10352
10511
  if (!/^https?:\/\//i.test(repoUrl))
@@ -11262,11 +11421,13 @@ function registerApi(app2, d) {
11262
11421
  });
11263
11422
  return c.json(modelDirsPayload(d));
11264
11423
  });
11424
+ const SORT_OPTIONS = ["best-match", "trending", "downloads", "likes", "modified", "created"];
11265
11425
  app2.get("/api/v1/hf/search", async (c) => {
11266
11426
  const q = (c.req.query("q") ?? "").trim();
11267
- if (!q) return c.json({ results: [] });
11427
+ const rawSort = c.req.query("sort") ?? "";
11428
+ const sort = SORT_OPTIONS.includes(rawSort) ? rawSort : "best-match";
11268
11429
  try {
11269
- const results = await d.hf.searchModels(q, d.registry.active()?.kind);
11430
+ const results = q ? await d.hf.searchModels(q, d.registry.active()?.kind, sort) : await d.hf.browseModels(sort, d.registry.active()?.kind);
11270
11431
  const withLocal = results.map((r) => ({ ...r, localCount: localCountFor(d, r.repo) }));
11271
11432
  return c.json({ results: withLocal });
11272
11433
  } catch (e) {
@@ -12068,9 +12229,10 @@ async function* streamToAnthropic(oaiStream, modelName, msgId, onUsage, onLive)
12068
12229
  yield cbStop(blockIdx);
12069
12230
  blockIdx++;
12070
12231
  }
12232
+ const nonCachedInput = Math.max(0, inputTokens - cacheReadTokens);
12071
12233
  yield sse("message_delta", {
12072
12234
  delta: { stop_reason: stopReason, stop_sequence: null },
12073
- usage: { input_tokens: inputTokens, output_tokens: outputTokens, cache_read_input_tokens: cacheReadTokens }
12235
+ usage: { input_tokens: nonCachedInput, output_tokens: outputTokens, cache_read_input_tokens: cacheReadTokens }
12074
12236
  });
12075
12237
  yield sse("message_stop", {});
12076
12238
  if (onUsage) {
@@ -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,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(`
1
+ import{j as e}from"./radix-9WP-OkMY.js";import{r as u}from"./vendor-W5GIuFcv.js";import{a as w,d as k,A as T,u as v,L as h,e as S,c as b,f as C,g as q,X as A}from"./index-K2ZQBdky.js";import{P as N}from"./plus-mSm32jpN.js";import{C as y}from"./circle-x-koFsrlGA.js";import{C as O}from"./circle-check-C4aACGyb.js";import{W as E}from"./wrench-Ceqe74n2.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};