turbollm 1.13.5 → 1.13.7

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 (117) hide show
  1. package/dist/cli.js +266 -125
  2. package/dist/webdist/assets/{AgentEditPage-DQSQhhdM.js → AgentEditPage-E7HtOsup.js} +1 -1
  3. package/dist/webdist/assets/{ChatScreen-CtFHffeH.js → ChatScreen-dEouSpAv.js} +1 -1
  4. package/dist/webdist/assets/{CodeComposer-v8HSPbTb.js → CodeComposer-CkqctDdp.js} +1 -1
  5. package/dist/webdist/assets/{CodeHomeScreen-BRjo9YC3.js → CodeHomeScreen-W_lZHAT2.js} +1 -1
  6. package/dist/webdist/assets/{CodeSessionScreen-nXHx5Rkf.js → CodeSessionScreen-BX1Eaqga.js} +1 -1
  7. package/dist/webdist/assets/{CustomizeScreen-bSsL1Dj0.js → CustomizeScreen-BszOveS0.js} +1 -1
  8. package/dist/webdist/assets/EnginesScreen-C3Tr39oE.js +10 -0
  9. package/dist/webdist/assets/{HardwareSection-a31KOorO.js → HardwareSection-Cvzgs-RM.js} +1 -1
  10. package/dist/webdist/assets/{ModelDetailDialog-k3r2SJWx.js → ModelDetailDialog-CQ_x0n0p.js} +1 -1
  11. package/dist/webdist/assets/{ModelLoadMenu-DgXJo77N.js → ModelLoadMenu-Lir_ZeaD.js} +1 -1
  12. package/dist/webdist/assets/{ModelsScreen-DUTDnwez.js → ModelsScreen-BwzqukhW.js} +1 -1
  13. package/dist/webdist/assets/{OnboardingScreen-Hgz6i2ot.js → OnboardingScreen-Df_1EYOW.js} +1 -1
  14. package/dist/webdist/assets/{RoutineEditPage-ItERtPTI.js → RoutineEditPage-CdZnyBEw.js} +1 -1
  15. package/dist/webdist/assets/{RoutinesPanel-BjclCgWV.js → RoutinesPanel-CBOpHq9E.js} +1 -1
  16. package/dist/webdist/assets/{SettingsScreen-BtlVJafk.js → SettingsScreen-Bhih9go0.js} +1 -1
  17. package/dist/webdist/assets/{SidebarResizeHandle-DBtMs1un.js → SidebarResizeHandle-CNnIPZEp.js} +1 -1
  18. package/dist/webdist/assets/{SkillEditPage-BJHoMA87.js → SkillEditPage-CCtOQTmq.js} +1 -1
  19. package/dist/webdist/assets/{ToolApprovalBar-pMCAf9oT.js → ToolApprovalBar-PlYgG25-.js} +3 -3
  20. package/dist/webdist/assets/WorkspaceScreen-CqmBBbxz.js +1 -0
  21. package/dist/webdist/assets/{abnfDiagram-N423BO3Z-C_g2c5t9.js → abnfDiagram-N423BO3Z-DsZ93mAM.js} +1 -1
  22. package/dist/webdist/assets/{agent-api-DZpsdnDb.js → agent-api-BQn5nZNX.js} +1 -1
  23. package/dist/webdist/assets/{alert-dialog-Be0J8mqx.js → alert-dialog-D9Z7ppH_.js} +1 -1
  24. package/dist/webdist/assets/{arc-YFjxy_2h.js → arc-BGrHHnrw.js} +1 -1
  25. package/dist/webdist/assets/{architectureDiagram-T3A2C74G-1eWQR9Yo.js → architectureDiagram-T3A2C74G-Ey1UrzU-.js} +1 -1
  26. package/dist/webdist/assets/{blockDiagram-VBNYF7ZC-DCkxfK2C.js → blockDiagram-VBNYF7ZC-D78LM23z.js} +1 -1
  27. package/dist/webdist/assets/{c4Diagram-5PPSVZJV-CvkqNRPH.js → c4Diagram-5PPSVZJV-CvPfXzDr.js} +1 -1
  28. package/dist/webdist/assets/channel-DCuz_wdC.js +1 -0
  29. package/dist/webdist/assets/{chat-api-C84sO1C9.js → chat-api-BKO4BTWe.js} +1 -1
  30. package/dist/webdist/assets/{chat-queries--trWZzdu.js → chat-queries-CqiPDqGV.js} +1 -1
  31. package/dist/webdist/assets/{chevron-down-BvP7K1yU.js → chevron-down-SLGilXKT.js} +1 -1
  32. package/dist/webdist/assets/{chevron-left-wCh6MmEl.js → chevron-left-CU5U4gIb.js} +1 -1
  33. package/dist/webdist/assets/{chevron-right-BC1tvE2N.js → chevron-right-BY4anLEq.js} +1 -1
  34. package/dist/webdist/assets/{chunk-2GRJ4B5K-MwqLqeQo.js → chunk-2GRJ4B5K-P3cu6Bc0.js} +1 -1
  35. package/dist/webdist/assets/{chunk-2Q5K7J3B-B1JqT0Ms.js → chunk-2Q5K7J3B-BgP7AimG.js} +1 -1
  36. package/dist/webdist/assets/{chunk-5RXB4S5H-BT__I3gy.js → chunk-5RXB4S5H-B-FXWALA.js} +1 -1
  37. package/dist/webdist/assets/{chunk-5VM5RSS4-CCCHTmAY.js → chunk-5VM5RSS4-C-lQ75_C.js} +1 -1
  38. package/dist/webdist/assets/{chunk-6Q2QTUOP-DpdhRs5e.js → chunk-6Q2QTUOP-Cv-tXjKv.js} +1 -1
  39. package/dist/webdist/assets/{chunk-GF5L2VYU-CqvwSdm0.js → chunk-GF5L2VYU-C3sxcn3E.js} +1 -1
  40. package/dist/webdist/assets/{chunk-JWPE2WC7-XlQN0MBh.js → chunk-JWPE2WC7-C-AvRcoC.js} +1 -1
  41. package/dist/webdist/assets/{chunk-KBJHAD2P-BtIxJswZ.js → chunk-KBJHAD2P-DMDEB7Sy.js} +1 -1
  42. package/dist/webdist/assets/{chunk-RYQCIY6F-deUgJS0Q.js → chunk-RYQCIY6F-DX_4YbYG.js} +1 -1
  43. package/dist/webdist/assets/{chunk-XXDRQBXY-38CMPJj8.js → chunk-XXDRQBXY-C-C12qlP.js} +1 -1
  44. package/dist/webdist/assets/{circle-check-BIhBTR1T.js → circle-check-D0IMfrYq.js} +1 -1
  45. package/dist/webdist/assets/{circle-x-DM6fYSRv.js → circle-x-pqL2JhJm.js} +1 -1
  46. package/dist/webdist/assets/classDiagram-JCYQIIEL-DFsr_nh-.js +1 -0
  47. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-DFsr_nh-.js +1 -0
  48. package/dist/webdist/assets/{collapsible-Dej2BQVn.js → collapsible-Bwm3DfUf.js} +1 -1
  49. package/dist/webdist/assets/{cose-bilkent-JH36ORCC-B_BQCrmS.js → cose-bilkent-JH36ORCC-0_jV9L5K.js} +1 -1
  50. package/dist/webdist/assets/{cynefin-VYW2F7L2-lGt_QLfe.js → cynefin-VYW2F7L2-D-cQNJqC.js} +1 -1
  51. package/dist/webdist/assets/{cynefinDiagram-MW4NZA55-B172ktgP.js → cynefinDiagram-MW4NZA55-QRp_3yKa.js} +1 -1
  52. package/dist/webdist/assets/{dagre-VZM6K2ZE-De4fl1iF.js → dagre-VZM6K2ZE-DYHS_FuW.js} +1 -1
  53. package/dist/webdist/assets/{diagram-7IWD3JNH-DtUW-Boo.js → diagram-7IWD3JNH-js1EUUk6.js} +1 -1
  54. package/dist/webdist/assets/{diagram-B4RE2ZJO-B6_p4VKc.js → diagram-B4RE2ZJO-BHVEhFez.js} +1 -1
  55. package/dist/webdist/assets/{diagram-LBJQPF4R-DC_wtD7r.js → diagram-LBJQPF4R-Cc-5wAwq.js} +1 -1
  56. package/dist/webdist/assets/{diagram-Q27KOJAE-6oF2_OjQ.js → diagram-Q27KOJAE-jTxFyyeN.js} +1 -1
  57. package/dist/webdist/assets/{diagram-UB23O5K3-BLh1k_fo.js → diagram-UB23O5K3-LP8zrNnQ.js} +1 -1
  58. package/dist/webdist/assets/{dropdown-menu-Bhu8YtbI.js → dropdown-menu-IOTY3_-B.js} +1 -1
  59. package/dist/webdist/assets/{ebnfDiagram-BXEA7PRR-ehMxmR4l.js → ebnfDiagram-BXEA7PRR-qDNqlger.js} +1 -1
  60. package/dist/webdist/assets/{erDiagram-JOGREHBK-96Mu4CA5.js → erDiagram-JOGREHBK-owRtaAi_.js} +1 -1
  61. package/dist/webdist/assets/{file-text-Dn3ZCp31.js → file-text-BotUNsIW.js} +1 -1
  62. package/dist/webdist/assets/{flowDiagram-UKHOOZJN-CRRZZa0_.js → flowDiagram-UKHOOZJN-D26LSAka.js} +1 -1
  63. package/dist/webdist/assets/{folder-input-DF1gn4vH.js → folder-input-D3qEcRaK.js} +1 -1
  64. package/dist/webdist/assets/{folder-plus-C3110N2s.js → folder-plus-4hzyN--0.js} +1 -1
  65. package/dist/webdist/assets/{ganttDiagram-PKOTCBZU-CTAHvUdG.js → ganttDiagram-PKOTCBZU-FOBYQpFD.js} +1 -1
  66. package/dist/webdist/assets/{git-branch-ClzWfV5B.js → git-branch-CSkwfOC2.js} +1 -1
  67. package/dist/webdist/assets/{gitGraphDiagram-DS77QQ5N-BwUcUxwZ.js → gitGraphDiagram-DS77QQ5N-D1pwlyQx.js} +1 -1
  68. package/dist/webdist/assets/{globe-UxyIyIxr.js → globe-BvQjzBKb.js} +1 -1
  69. package/dist/webdist/assets/{index-D5gUIqLa.js → index-CXLOA4-7.js} +2 -2
  70. package/dist/webdist/assets/{infoDiagram-6WML65LV-KXXRYeq1.js → infoDiagram-6WML65LV-DAxebGLC.js} +1 -1
  71. package/dist/webdist/assets/{ishikawaDiagram-WSZJBQD7-F9eL-DH9.js → ishikawaDiagram-WSZJBQD7-DIdOYDDr.js} +1 -1
  72. package/dist/webdist/assets/{journeyDiagram-NVQOT4AX-Br82xXB7.js → journeyDiagram-NVQOT4AX-BLvkNn5k.js} +1 -1
  73. package/dist/webdist/assets/{kanban-definition-27J2QSJJ-TxuwPPK2.js → kanban-definition-27J2QSJJ-DKyJQb6a.js} +1 -1
  74. package/dist/webdist/assets/{linear-Dnua3zVN.js → linear-sR1aOS-1.js} +1 -1
  75. package/dist/webdist/assets/{lock-CIkAh19m.js → lock-K-a91AFa.js} +1 -1
  76. package/dist/webdist/assets/{mermaid.core-3DYUUSdD.js → mermaid.core-sVxlfu4j.js} +4 -4
  77. package/dist/webdist/assets/{mindmap-definition-FAOFIHXS-C2Je1XD3.js → mindmap-definition-FAOFIHXS-Yo_cYEEu.js} +1 -1
  78. package/dist/webdist/assets/{monitor-Dao9gtZQ.js → monitor-_13VL1du.js} +1 -1
  79. package/dist/webdist/assets/{pegDiagram-VL7TDLO6-Imiwx_Mi.js → pegDiagram-VL7TDLO6-BSAx7r44.js} +1 -1
  80. package/dist/webdist/assets/{pencil-DLPQKbE9.js → pencil-CshqlsN6.js} +1 -1
  81. package/dist/webdist/assets/{pieDiagram-7S7Q4E2Y-DaWNxcI-.js → pieDiagram-7S7Q4E2Y-DJDpIi_G.js} +1 -1
  82. package/dist/webdist/assets/{quadrantDiagram-CIZ2JOQS-DeQ1njXO.js → quadrantDiagram-CIZ2JOQS-BYUOd1Ld.js} +1 -1
  83. package/dist/webdist/assets/{railroadDiagram-AXF67PYL-BvJgh6Z_.js → railroadDiagram-AXF67PYL-Cp7ActLQ.js} +1 -1
  84. package/dist/webdist/assets/{refresh-cw-BE_VKWCq.js → refresh-cw-BgvgHNs_.js} +1 -1
  85. package/dist/webdist/assets/{remote-failure-z1LSyZOB.js → remote-failure-CU6FQAv2.js} +1 -1
  86. package/dist/webdist/assets/{requirementDiagram-LRYGKXZP-BiZx2AHw.js → requirementDiagram-LRYGKXZP-BRIn9Bao.js} +1 -1
  87. package/dist/webdist/assets/{rocket-CxCRl5WL.js → rocket-Bdyspsau.js} +1 -1
  88. package/dist/webdist/assets/{rotate-ccw-CUHjjSni.js → rotate-ccw-CXchjxoU.js} +1 -1
  89. package/dist/webdist/assets/{sankeyDiagram-W5VNT64P-DbDt5_xz.js → sankeyDiagram-W5VNT64P-DrfFnEda.js} +1 -1
  90. package/dist/webdist/assets/{sequenceDiagram-SI44F4Z6-Ya9d9pJx.js → sequenceDiagram-SI44F4Z6-BRvCwGTa.js} +1 -1
  91. package/dist/webdist/assets/{sheet-uLkeKdTj.js → sheet-BEHvWodM.js} +1 -1
  92. package/dist/webdist/assets/{sizeCapture-X5ZJPWSS-CKcIKwbb.js → sizeCapture-X5ZJPWSS-BLSbFiEe.js} +1 -1
  93. package/dist/webdist/assets/{skeleton-C4DVQSnO.js → skeleton-_WE_l7Ss.js} +1 -1
  94. package/dist/webdist/assets/{sliders-horizontal-DVdVp0P2.js → sliders-horizontal-X0H751ey.js} +1 -1
  95. package/dist/webdist/assets/{sparkles-daM-8UXC.js → sparkles-B57ip_p2.js} +1 -1
  96. package/dist/webdist/assets/{star-CpCXu8tE.js → star-DFChWJ1H.js} +1 -1
  97. package/dist/webdist/assets/{stateDiagram-OKZ733FA-CqU-1wd8.js → stateDiagram-OKZ733FA-dWf7zqCN.js} +1 -1
  98. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-CQPsdlPu.js +1 -0
  99. package/dist/webdist/assets/{swimlanes-SLNWSIFB-nzSF-_JW.js → swimlanes-SLNWSIFB-lCDiXKRw.js} +2 -2
  100. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-CzicNSYv.js +8 -0
  101. package/dist/webdist/assets/{switch-BmXyQvbr.js → switch-1qn4bGLW.js} +1 -1
  102. package/dist/webdist/assets/{terminal-BFz2js-b.js → terminal-BiLS4W9p.js} +1 -1
  103. package/dist/webdist/assets/{timeline-definition-Z64GVDOM-BFB3jSJV.js → timeline-definition-Z64GVDOM-DSxynxhf.js} +1 -1
  104. package/dist/webdist/assets/{trash-2-DmBQACGe.js → trash-2-JXgZx0Gm.js} +1 -1
  105. package/dist/webdist/assets/{vennDiagram-T6HMQDX7-cdR5hz8k.js → vennDiagram-T6HMQDX7-AFtVyBx_.js} +1 -1
  106. package/dist/webdist/assets/{wardleyDiagram-T6FBY63Y-1Z-jUDaw.js → wardleyDiagram-T6FBY63Y-Bi_TjYlH.js} +1 -1
  107. package/dist/webdist/assets/{wrench-BV5hCWSo.js → wrench-C6y9CDpZ.js} +1 -1
  108. package/dist/webdist/assets/{xychartDiagram-ELKLHX3M-DfGwd0jU.js → xychartDiagram-ELKLHX3M-BaREW8xI.js} +1 -1
  109. package/dist/webdist/index.html +1 -1
  110. package/package.json +1 -1
  111. package/dist/webdist/assets/EnginesScreen-DKIeTAXe.js +0 -10
  112. package/dist/webdist/assets/WorkspaceScreen-GL1Nw-SI.js +0 -1
  113. package/dist/webdist/assets/channel-BLq3w_Ai.js +0 -1
  114. package/dist/webdist/assets/classDiagram-JCYQIIEL-Bsb3bMvc.js +0 -1
  115. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-Bsb3bMvc.js +0 -1
  116. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-Erur5nvV.js +0 -1
  117. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-Ds2N1ZDj.js +0 -8
package/dist/cli.js CHANGED
@@ -788,7 +788,7 @@ function clampMaxTokens(requested, limit) {
788
788
  }
789
789
 
790
790
  // src/engines/manager.ts
791
- import { execFile as execFile6, spawn, spawnSync } from "child_process";
791
+ import { execFile as execFile7, spawn, spawnSync } from "child_process";
792
792
  import { createWriteStream, existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync5, writeFileSync as writeFileSync2 } from "fs";
793
793
  import { createConnection, createServer } from "net";
794
794
  import { dirname as dirname2, join as join10 } from "path";
@@ -1213,10 +1213,99 @@ async function restoreSlot(p) {
1213
1213
 
1214
1214
  // src/engines/vllm.ts
1215
1215
  import { existsSync as existsSync8 } from "fs";
1216
- import { execFile as execFile4 } from "child_process";
1216
+ import { execFile as execFile5 } from "child_process";
1217
1217
  import { join as join8 } from "path";
1218
+ import { promisify as promisify5 } from "util";
1219
+
1220
+ // src/engines/py-engine-blocker.ts
1221
+ import { execFile as execFile4 } from "child_process";
1222
+ import { homedir as homedir2 } from "os";
1218
1223
  import { promisify as promisify4 } from "util";
1219
1224
  var execFileP4 = promisify4(execFile4);
1225
+ var DETAIL_LIMIT = 200;
1226
+ var UvloopPreflight = class {
1227
+ constructor(engine, homeDirectory = homedir2) {
1228
+ this.engine = engine;
1229
+ this.homeDirectory = homeDirectory;
1230
+ }
1231
+ engine;
1232
+ homeDirectory;
1233
+ async blockerFor(python) {
1234
+ try {
1235
+ await execFileP4(python, ["-c", "import uvloop"], { timeout: 2e4 });
1236
+ return null;
1237
+ } catch (error) {
1238
+ return this.classify(process.platform, error);
1239
+ }
1240
+ }
1241
+ // uvloop publishes manylinux *and* macOS wheels, so only Windows is a real platform wall —
1242
+ // anywhere else a failed import is a broken local environment the user can reinstall.
1243
+ classify(platform, error) {
1244
+ if (platform === "win32") return this.unsupportedPlatform();
1245
+ const failure = error;
1246
+ if (failure?.code === "ENOENT") return this.missingInterpreter();
1247
+ return this.brokenEnvironment(platform, failure);
1248
+ }
1249
+ unsupportedPlatform() {
1250
+ return `${this.engine} cannot run on Windows: its server requires uvloop (and other Linux-only components such as NCCL/Triton), which have no Windows build. Use the llama.cpp / TurboQuant engine for GGUF models here, or run ${this.engine} under WSL2 / Linux.`;
1251
+ }
1252
+ missingInterpreter() {
1253
+ return `${this.engine}'s environment looks missing or broken (interpreter not found). Reinstall the ${this.engine} engine from the Engines page.`;
1254
+ }
1255
+ brokenEnvironment(platform, failure) {
1256
+ return `${this.engine}'s environment looks broken or incomplete (uvloop failed to import: ${failureDetail(failure, this.homeOrEmpty())}). ${this.platformStanding(platform)}`;
1257
+ }
1258
+ // homedir() throws (uv_os_homedir ENOENT) for a uid with no passwd entry and no HOME, e.g. a container.
1259
+ homeOrEmpty() {
1260
+ try {
1261
+ return this.homeDirectory();
1262
+ } catch {
1263
+ return "";
1264
+ }
1265
+ }
1266
+ // Only what is true is claimed: Linux is where these engines are officially supported, while on
1267
+ // macOS the catalog rates them experimental / unsupported upstream, so only uvloop's own support
1268
+ // may be asserted there.
1269
+ platformStanding(platform) {
1270
+ const plat = platformDisplay(platform);
1271
+ if (platform === "linux") {
1272
+ return `${plat} is a supported platform for ${this.engine} \u2014 reinstall the ${this.engine} engine from the Engines page rather than switching engines.`;
1273
+ }
1274
+ if (platform === "darwin") {
1275
+ return `uvloop itself supports ${plat}, so reinstall the ${this.engine} engine from the Engines page first \u2014 though ${plat} is not a first-class platform for ${this.engine} upstream.`;
1276
+ }
1277
+ return `${this.engine} support on ${plat} is unverified \u2014 reinstall the ${this.engine} engine from the Engines page; if the import keeps failing, ${this.engine} may have no working build here.`;
1278
+ }
1279
+ };
1280
+ function platformDisplay(platform) {
1281
+ if (platform === "win32") return "Windows";
1282
+ if (platform === "darwin") return "macOS";
1283
+ if (platform === "linux") return "Linux";
1284
+ return platform;
1285
+ }
1286
+ function failureDetail(failure, home) {
1287
+ const raw = failure?.stderr ? String(failure.stderr) : failure?.message ?? "";
1288
+ return capped(redactHome(lastMeaningfulLine(raw), home));
1289
+ }
1290
+ function lastMeaningfulLine(text) {
1291
+ const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1292
+ return lines.at(-1) ?? "unknown error";
1293
+ }
1294
+ function redactHome(text, home) {
1295
+ if (home.length < 2) return text;
1296
+ const spellings = /* @__PURE__ */ new Set([home, home.split("\\").join("/"), home.split("/").join("\\")]);
1297
+ const anySpelling = new RegExp([...spellings].map(escapeRegExp).join("|"), "gi");
1298
+ return text.replace(anySpelling, "~");
1299
+ }
1300
+ function escapeRegExp(text) {
1301
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1302
+ }
1303
+ function capped(detail) {
1304
+ return detail.length <= DETAIL_LIMIT ? detail : `${detail.slice(0, DETAIL_LIMIT)}\u2026`;
1305
+ }
1306
+
1307
+ // src/engines/vllm.ts
1308
+ var execFileP5 = promisify5(execFile5);
1220
1309
  var VLLM_PYTHON = "3.12";
1221
1310
  function venvPython4(envDir) {
1222
1311
  return process.platform === "win32" ? join8(envDir, "Scripts", "python.exe") : join8(envDir, "bin", "python");
@@ -1227,43 +1316,23 @@ async function ensureVllmEnv(root, onProgress, upgrade = false) {
1227
1316
  const py = venvPython4(envDir);
1228
1317
  if (!existsSync8(py)) {
1229
1318
  onProgress?.({ phase: "extracting", pct: -1 });
1230
- await execFileP4(uv, ["venv", "--python", VLLM_PYTHON, envDir], { cwd: root });
1319
+ await execFileP5(uv, ["venv", "--python", VLLM_PYTHON, envDir], { cwd: root });
1231
1320
  }
1232
1321
  onProgress?.({ phase: "extracting", pct: -1 });
1233
1322
  const installArgs = ["pip", "install", "--python", py, ...upgrade ? ["-U"] : [], "vllm"];
1234
- await execFileP4(uv, installArgs, {
1323
+ await execFileP5(uv, installArgs, {
1235
1324
  cwd: root,
1236
1325
  maxBuffer: 64 * 1024 * 1024
1237
1326
  });
1238
1327
  const version2 = await probeVllm(py);
1239
1328
  return { python: py, version: version2 };
1240
1329
  }
1241
- function platformDisplay(platform) {
1242
- return platform === "win32" ? "Windows" : platform === "darwin" ? "macOS" : platform;
1243
- }
1244
- function classifyVllmBlocker(platform, error) {
1245
- const plat = platformDisplay(platform);
1246
- if (platform === "win32" || platform === "darwin") {
1247
- return `vLLM cannot run on ${plat}: its OpenAI server requires uvloop (and other Linux-only components such as NCCL/Triton), which have no ${plat} build. Use the llama.cpp / TurboQuant engine for GGUF models here, or run vLLM under WSL2 / Linux.`;
1248
- }
1249
- const err9 = error;
1250
- if (err9?.code === "ENOENT") {
1251
- return `vLLM's environment looks missing or broken (interpreter not found). Reinstall the vLLM engine from the Engines page.`;
1252
- }
1253
- const raw = err9?.stderr ? String(err9.stderr) : err9?.message ?? "unknown error";
1254
- const detail = raw.trim().split("\n").filter(Boolean).slice(-1)[0] ?? raw.trim();
1255
- return `vLLM's environment looks broken or incomplete (uvloop failed to import: ${detail}). ${plat} is a supported platform for vLLM \u2014 reinstall the vLLM engine from the Engines page rather than switching engines.`;
1256
- }
1257
- async function vllmServeBlocker(python) {
1258
- try {
1259
- await execFileP4(python, ["-c", "import uvloop"], { timeout: 2e4 });
1260
- return null;
1261
- } catch (error) {
1262
- return classifyVllmBlocker(process.platform, error);
1263
- }
1330
+ var uvloopPreflight = new UvloopPreflight("vLLM");
1331
+ function vllmServeBlocker(python) {
1332
+ return uvloopPreflight.blockerFor(python);
1264
1333
  }
1265
1334
  async function probeVllm(python) {
1266
- const { stdout } = await execFileP4(
1335
+ const { stdout } = await execFileP5(
1267
1336
  python,
1268
1337
  ["-c", 'import importlib.metadata as m; print(m.version("vllm"))'],
1269
1338
  { timeout: 3e4 }
@@ -1293,10 +1362,10 @@ function vllmServerCommand(python, model, port2, host2, tensorParallelSize = 1,
1293
1362
 
1294
1363
  // src/engines/sglang.ts
1295
1364
  import { existsSync as existsSync9 } from "fs";
1296
- import { execFile as execFile5 } from "child_process";
1365
+ import { execFile as execFile6 } from "child_process";
1297
1366
  import { join as join9 } from "path";
1298
- import { promisify as promisify5 } from "util";
1299
- var execFileP5 = promisify5(execFile5);
1367
+ import { promisify as promisify6 } from "util";
1368
+ var execFileP6 = promisify6(execFile6);
1300
1369
  var SGLANG_PYTHON = "3.12";
1301
1370
  function venvPython5(envDir) {
1302
1371
  return process.platform === "win32" ? join9(envDir, "Scripts", "python.exe") : join9(envDir, "bin", "python");
@@ -1307,40 +1376,20 @@ async function ensureSglangEnv(root, onProgress, upgrade = false) {
1307
1376
  const py = venvPython5(envDir);
1308
1377
  if (!existsSync9(py)) {
1309
1378
  onProgress?.({ phase: "extracting", pct: -1 });
1310
- await execFileP5(uv, ["venv", "--python", SGLANG_PYTHON, envDir], { cwd: root });
1379
+ await execFileP6(uv, ["venv", "--python", SGLANG_PYTHON, envDir], { cwd: root });
1311
1380
  }
1312
1381
  onProgress?.({ phase: "extracting", pct: -1 });
1313
1382
  const installArgs = ["pip", "install", "--python", py, ...upgrade ? ["-U"] : [], "sglang[all]"];
1314
- await execFileP5(uv, installArgs, { cwd: root, maxBuffer: 64 * 1024 * 1024 });
1383
+ await execFileP6(uv, installArgs, { cwd: root, maxBuffer: 64 * 1024 * 1024 });
1315
1384
  const version2 = await probeSglang(py);
1316
1385
  return { python: py, version: version2 };
1317
1386
  }
1318
- function platformDisplay2(platform) {
1319
- return platform === "win32" ? "Windows" : platform === "darwin" ? "macOS" : platform;
1320
- }
1321
- function classifySglangBlocker(platform, error) {
1322
- const plat = platformDisplay2(platform);
1323
- if (platform === "win32" || platform === "darwin") {
1324
- return `SGLang cannot run on ${plat}: its server requires uvloop (and other Linux-only components), which have no ${plat} build. Use the llama.cpp / TurboQuant engine for GGUF models here, or run SGLang under WSL2 / Linux.`;
1325
- }
1326
- const err9 = error;
1327
- if (err9?.code === "ENOENT") {
1328
- return `SGLang's environment looks missing or broken (interpreter not found). Reinstall the SGLang engine from the Engines page.`;
1329
- }
1330
- const raw = err9?.stderr ? String(err9.stderr) : err9?.message ?? "unknown error";
1331
- const detail = raw.trim().split("\n").filter(Boolean).slice(-1)[0] ?? raw.trim();
1332
- return `SGLang's environment looks broken or incomplete (uvloop failed to import: ${detail}). ${plat} is a supported platform for SGLang \u2014 reinstall the SGLang engine from the Engines page rather than switching engines.`;
1333
- }
1334
- async function sgLangServeBlocker(python) {
1335
- try {
1336
- await execFileP5(python, ["-c", "import uvloop"], { timeout: 2e4 });
1337
- return null;
1338
- } catch (error) {
1339
- return classifySglangBlocker(process.platform, error);
1340
- }
1387
+ var uvloopPreflight2 = new UvloopPreflight("SGLang");
1388
+ function sgLangServeBlocker(python) {
1389
+ return uvloopPreflight2.blockerFor(python);
1341
1390
  }
1342
1391
  async function probeSglang(python) {
1343
- const { stdout } = await execFileP5(
1392
+ const { stdout } = await execFileP6(
1344
1393
  python,
1345
1394
  ["-c", 'import importlib.metadata as m; print(m.version("sglang"))'],
1346
1395
  { timeout: 3e4 }
@@ -1952,7 +2001,7 @@ async function gracefulStop(child, exited) {
1952
2001
  function signalTerm(child) {
1953
2002
  if (!child.pid) return;
1954
2003
  if (process.platform === "win32") {
1955
- execFile6("taskkill", ["/PID", String(child.pid), "/T"], () => {
2004
+ execFile7("taskkill", ["/PID", String(child.pid), "/T"], () => {
1956
2005
  });
1957
2006
  } else {
1958
2007
  child.kill("SIGTERM");
@@ -1961,7 +2010,7 @@ function signalTerm(child) {
1961
2010
  function forceKill(child) {
1962
2011
  if (!child.pid) return;
1963
2012
  if (process.platform === "win32") {
1964
- execFile6("taskkill", ["/PID", String(child.pid), "/F", "/T"], () => {
2013
+ execFile7("taskkill", ["/PID", String(child.pid), "/F", "/T"], () => {
1965
2014
  });
1966
2015
  } else {
1967
2016
  child.kill("SIGKILL");
@@ -2066,7 +2115,7 @@ function killTrackedEnginesSync(dataDir) {
2066
2115
  }
2067
2116
  function killPidTree(pid) {
2068
2117
  if (process.platform === "win32") {
2069
- execFile6("taskkill", ["/PID", String(pid), "/F", "/T"], () => {
2118
+ execFile7("taskkill", ["/PID", String(pid), "/F", "/T"], () => {
2070
2119
  });
2071
2120
  } else {
2072
2121
  try {
@@ -2321,7 +2370,7 @@ import { existsSync as existsSync14 } from "fs";
2321
2370
  import { randomUUID as randomUUID2 } from "crypto";
2322
2371
 
2323
2372
  // src/engines/probe.ts
2324
- import { execFile as execFile7 } from "child_process";
2373
+ import { execFile as execFile8 } from "child_process";
2325
2374
  import { closeSync, existsSync as existsSync11, openSync, readSync, statSync as statSync2 } from "fs";
2326
2375
  import { dirname as dirname3 } from "path";
2327
2376
  function probeEnv(bin) {
@@ -2350,7 +2399,7 @@ function runCaptured(bin, arg) {
2350
2399
  return new Promise((resolve6) => {
2351
2400
  try {
2352
2401
  const timeoutMs = process.platform === "darwin" ? 6e4 : 15e3;
2353
- execFile7(bin, [arg], { cwd: dirname3(bin), env: probeEnv(bin), timeout: timeoutMs, windowsHide: true, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
2402
+ execFile8(bin, [arg], { cwd: dirname3(bin), env: probeEnv(bin), timeout: timeoutMs, windowsHide: true, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
2354
2403
  resolve6({ out: (stdout || "") + (stderr || ""), err: error });
2355
2404
  });
2356
2405
  } catch (e) {
@@ -2507,17 +2556,17 @@ function trimLen(s, n) {
2507
2556
  }
2508
2557
 
2509
2558
  // src/engines/build-runner.ts
2510
- import { execFile as execFile9, spawn as spawn2 } from "child_process";
2559
+ import { execFile as execFile10, spawn as spawn2 } from "child_process";
2511
2560
  import { createHash as createHash2 } from "crypto";
2512
2561
  import { copyFileSync, existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync3, readdirSync as readdirSync3, rmSync as rmSync6, writeFileSync as writeFileSync3 } from "fs";
2513
2562
  import { delimiter as delimiter2, dirname as dirname5, join as join12 } from "path";
2514
- import { promisify as promisify7 } from "util";
2563
+ import { promisify as promisify8 } from "util";
2515
2564
 
2516
2565
  // src/engines/build-prereqs.ts
2517
- import { execFile as execFile8 } from "child_process";
2566
+ import { execFile as execFile9 } from "child_process";
2518
2567
  import { delimiter, join as join11 } from "path";
2519
- import { promisify as promisify6 } from "util";
2520
- var execFileP6 = promisify6(execFile8);
2568
+ import { promisify as promisify7 } from "util";
2569
+ var execFileP7 = promisify7(execFile9);
2521
2570
  function buildEnv(toolchainDirs = []) {
2522
2571
  const dirs = toolchainDirs.map((d) => d.trim()).filter(Boolean);
2523
2572
  const env = { ...process.env };
@@ -2579,7 +2628,7 @@ var INSTALL_URLS = {
2579
2628
  gcc: "https://gcc.gnu.org/install/"
2580
2629
  };
2581
2630
  async function runVersion(cmd, args, env) {
2582
- const { stdout, stderr } = await execFileP6(cmd, args, {
2631
+ const { stdout, stderr } = await execFileP7(cmd, args, {
2583
2632
  timeout: 8e3,
2584
2633
  windowsHide: true,
2585
2634
  maxBuffer: 1024 * 1024,
@@ -2777,10 +2826,10 @@ function suggestEngineName(binPath, version2) {
2777
2826
  }
2778
2827
 
2779
2828
  // src/engines/build-runner.ts
2780
- var execFileP7 = promisify7(execFile9);
2829
+ var execFileP8 = promisify8(execFile10);
2781
2830
  async function findLibcudaDir(signal) {
2782
2831
  try {
2783
- const { stdout } = await execFileP7("ldconfig", ["-p"], {
2832
+ const { stdout } = await execFileP8("ldconfig", ["-p"], {
2784
2833
  timeout: 5e3,
2785
2834
  maxBuffer: 4 * 1024 * 1024,
2786
2835
  signal
@@ -2796,16 +2845,86 @@ function chooseEngineName(name, priorName) {
2796
2845
  return name ?? priorName ?? "";
2797
2846
  }
2798
2847
  function findPriorEngine(engines, build2) {
2799
- return engines.find((e) => e.binPath === build2.binPath) ?? engines.find(
2800
- (e) => sameRepo(e.sourceRepo, build2.sourceRepo) && (e.sourceBranch ?? "") === (build2.sourceBranch ?? "") && (e.sourceCommit ?? "") === (build2.sourceCommit ?? "")
2848
+ const byBinPath = engines.find((e) => e.binPath === build2.binPath);
2849
+ if (byBinPath) return byBinPath;
2850
+ const sameTarget = engines.filter(
2851
+ (e) => sameRepo(e.sourceRepo, build2.sourceRepo) && (e.sourceCommit ?? "") === (build2.sourceCommit ?? "")
2852
+ );
2853
+ return pickByBranch(sameTarget, (build2.sourceBranch ?? "").trim(), effectiveBranch(build2.sourceBranch, build2.defaultBranch), build2.defaultBranch);
2854
+ }
2855
+ function effectiveBranch(branch, defaultBranch) {
2856
+ return (branch ?? "").trim() || (defaultBranch ?? "").trim();
2857
+ }
2858
+ function pickByBranch(candidates, exactBranch, effective, defaultBranch) {
2859
+ return candidates.find((e) => (e.sourceBranch ?? "").trim() === exactBranch) ?? candidates.find((e) => effectiveBranch(e.sourceBranch, defaultBranch) === effective);
2860
+ }
2861
+ var isPinnedEntry = (entry) => !!(entry.sourceCommit || entry.patchUrl);
2862
+ function findEngineForCatalogEntry(engines, entry, requestedBranch) {
2863
+ const sameBuild = engines.filter(
2864
+ (e) => sameRepo(e.sourceRepo, entry.homepage) && (e.sourceCommit ?? "") === (entry.sourceCommit ?? "") && (e.sourcePatchUrl ?? "") === (entry.patchUrl ?? "")
2801
2865
  );
2866
+ if (isPinnedEntry(entry)) return sameBuild[0];
2867
+ const wanted = effectiveBranch(requestedBranch, entry.defaultBranch);
2868
+ return pickByBranch(sameBuild, wanted, wanted, entry.defaultBranch);
2869
+ }
2870
+ function catalogBranchesToScan(entry, requestedBranch) {
2871
+ if (isPinnedEntry(entry)) return [void 0];
2872
+ const wanted = effectiveBranch(requestedBranch, entry.defaultBranch);
2873
+ if (!wanted) return [void 0];
2874
+ return wanted === (entry.defaultBranch ?? "").trim() ? [wanted, void 0] : [wanted];
2875
+ }
2876
+ function legacyBuildDirName(repoUrl, branch, commit) {
2877
+ const last = repoUrl.trim().replace(/\/+$/, "").split(/[\\/]/).pop() ?? "";
2878
+ const repo = last.replace(/\.git$/i, "").trim() || "engine";
2879
+ const b = (branch ?? "").trim();
2880
+ const sha = (commit ?? "").trim();
2881
+ return asDirName(sha ? `${repo}-${sha.slice(0, 12)}` : b ? `${repo}-${b}` : repo);
2882
+ }
2883
+ function serverBinaryIn(enginesRoot, dirName) {
2884
+ const root = join12(enginesRoot, "build", dirName);
2885
+ return resolveServerBinary(join12(root, "build")) ?? resolveServerBinary(root);
2886
+ }
2887
+ function findCatalogBuildOnDisk(enginesRoot, entry, requestedBranch) {
2888
+ for (const branch of catalogBranchesToScan(entry, requestedBranch)) {
2889
+ const binPath = sourceBuildBinary(enginesRoot, entry.homepage, branch, entry.sourceCommit);
2890
+ if (binPath) return { binPath, branch: branch ?? "" };
2891
+ }
2892
+ if (!entry.sourceCommit) return void 0;
2893
+ const legacy = serverBinaryIn(enginesRoot, legacyBuildDirName(entry.homepage, void 0, entry.sourceCommit));
2894
+ return legacy ? { binPath: legacy, branch: "" } : void 0;
2895
+ }
2896
+ var slashed = (path) => path.replace(/\\/g, "/").toLowerCase();
2897
+ function isEngineInBuildDir(binPath, buildRoot) {
2898
+ return slashed(binPath).startsWith(`${slashed(buildRoot).replace(/\/+$/, "")}/`);
2899
+ }
2900
+ var branchesProvablyDiffer = (a, b) => {
2901
+ const x = (a ?? "").trim();
2902
+ const y = (b ?? "").trim();
2903
+ return !!x && !!y && x !== y;
2904
+ };
2905
+ function findNameConflict(engines, name, build2) {
2906
+ const wanted = (name ?? "").trim().toLowerCase();
2907
+ if (!wanted) return void 0;
2908
+ const holder = engines.find((e) => e.name.trim().toLowerCase() === wanted);
2909
+ if (!holder) return void 0;
2910
+ const replaced = isEngineInBuildDir(holder.binPath, build2.buildRoot) || sameRepo(holder.sourceRepo, build2.sourceRepo) && (holder.sourceCommit ?? "") === (build2.sourceCommit ?? "") && !branchesProvablyDiffer(holder.sourceBranch, build2.sourceBranch);
2911
+ return replaced ? void 0 : holder;
2912
+ }
2913
+ function parseDefaultBranch(lsRemoteOutput) {
2914
+ return /^ref:\s+refs\/heads\/(\S+)\s+HEAD\s*$/m.exec(lsRemoteOutput)?.[1];
2802
2915
  }
2803
2916
  function buildDirName(repoUrl, branch, commit) {
2804
2917
  const repo = normRepoUrl(repoUrl) || "engine";
2805
2918
  const b = (branch ?? "").trim();
2806
2919
  const sha = (commit ?? "").trim();
2807
- const raw = sha ? `${repo}-${sha.slice(0, 12)}` : b ? `${repo}-${b}` : repo;
2808
- return raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "engine";
2920
+ return asDirName(sha ? `${repo}-${sha.slice(0, 12)}` : b ? `${repo}-${b}` : repo);
2921
+ }
2922
+ function asDirName(raw) {
2923
+ const slug = raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
2924
+ return isPathStep(slug) ? "engine" : slug;
2925
+ }
2926
+ function isPathStep(name) {
2927
+ return /^\.*$/.test(name);
2809
2928
  }
2810
2929
  function sameRepo(a, b) {
2811
2930
  const na = normRepoUrl(a);
@@ -2815,12 +2934,11 @@ function normRepoUrl(s) {
2815
2934
  return (s ?? "").trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^github\.com\//, "").replace(/\/+$/, "").replace(/\.git$/, "");
2816
2935
  }
2817
2936
  function sourceBuildBinary(enginesRoot, repoUrl, branch, commit) {
2818
- const root = join12(enginesRoot, "build", buildDirName(repoUrl, branch, commit));
2819
- return resolveServerBinary(join12(root, "build")) ?? resolveServerBinary(root);
2937
+ return serverBinaryIn(enginesRoot, buildDirName(repoUrl, branch, commit));
2820
2938
  }
2821
2939
  function sourceBuildDirOf(binPath, enginesRoot) {
2822
2940
  const m = binPath.replace(/\\/g, "/").match(/\/engines\/build\/([^/]+)(?:\/|$)/i);
2823
- if (!m) return null;
2941
+ if (!m || isPathStep(m[1])) return null;
2824
2942
  return join12(enginesRoot, "build", m[1]);
2825
2943
  }
2826
2944
  function pickGenerator(hasNinja, isWindows) {
@@ -2972,7 +3090,7 @@ async function findVcvarsall() {
2972
3090
  try {
2973
3091
  const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
2974
3092
  const vswhere = join12(programFilesX86, "Microsoft Visual Studio", "Installer", "vswhere.exe");
2975
- const { stdout } = await execFileP7(
3093
+ const { stdout } = await execFileP8(
2976
3094
  vswhere,
2977
3095
  ["-latest", "-products", "*", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-find", "VC\\Auxiliary\\Build\\vcvarsall.bat"],
2978
3096
  { timeout: 8e3, windowsHide: true, maxBuffer: 1024 * 1024 }
@@ -3168,6 +3286,15 @@ async function runBuild(req, hooks, signal) {
3168
3286
  }
3169
3287
  const commit = (await runStep("git", ["-C", srcDir, "rev-parse", "HEAD"], { env, signal, onLine: () => {
3170
3288
  } })).trim();
3289
+ let defaultBranch;
3290
+ try {
3291
+ const lsRemoteArgs = ["-c", "http.lowSpeedLimit=1000", "-c", "http.lowSpeedTime=15", "ls-remote", "--symref", req.repoUrl, "HEAD"];
3292
+ const lsRemote = await runStep("git", lsRemoteArgs, { env, signal, onLine: () => {
3293
+ } });
3294
+ defaultBranch = parseDefaultBranch(lsRemote);
3295
+ } catch (e) {
3296
+ if (e?.name === "AbortError") throw e;
3297
+ }
3171
3298
  const patchUrl = (req.patchUrl ?? "").trim();
3172
3299
  if (patchUrl) {
3173
3300
  const expectedSha = (req.patchSha256 ?? "").trim();
@@ -3253,7 +3380,7 @@ async function runBuild(req, hooks, signal) {
3253
3380
  }
3254
3381
  if (isWindows) copyCudaRuntimeDlls(env, dirname5(binPath), hooks.log);
3255
3382
  else if (!isMac && !isAndroid2) copyCudaRuntimeLibs(env, dirname5(binPath), hooks.log);
3256
- return { binPath, commit, buildRoot };
3383
+ return { binPath, commit, buildRoot, defaultBranch };
3257
3384
  }
3258
3385
 
3259
3386
  // src/engines/registry.ts
@@ -3265,11 +3392,19 @@ var NotFoundError = class extends Error {
3265
3392
  }
3266
3393
  };
3267
3394
  var NameTakenError = class extends Error {
3268
- constructor() {
3269
- super("Name already in use \u2014 choose a different name.");
3395
+ constructor(holder, incoming) {
3396
+ super(nameTakenMessage(holder, incoming));
3270
3397
  this.name = "NameTakenError";
3271
3398
  }
3272
3399
  };
3400
+ function nameTakenMessage(holder, incoming) {
3401
+ if (!holder) return "Name already in use \u2014 choose a different name.";
3402
+ const base2 = `Name already in use by "${holder.name}" \u2014 choose a different name.`;
3403
+ const held = holder.sourceBranch?.trim();
3404
+ const wanted = incoming?.sourceBranch?.trim();
3405
+ const anotherBranchOfTheSameRepo = !!held && !!wanted && held !== wanted && sameRepo(holder.sourceRepo, incoming?.sourceRepo);
3406
+ return anotherBranchOfTheSameRepo ? `${base2} It is a build of branch "${held}"; a build of "${wanted}" is a separate engine and needs its own name.` : base2;
3407
+ }
3273
3408
  var Registry = class {
3274
3409
  constructor(store2) {
3275
3410
  this.store = store2;
@@ -3288,7 +3423,7 @@ var Registry = class {
3288
3423
  }
3289
3424
  async add(name, binPath, source) {
3290
3425
  const finalName = name.trim() || "llama-server";
3291
- this.assertNameFree(finalName);
3426
+ this.assertNameFree(finalName, source);
3292
3427
  const pr = await probe(binPath);
3293
3428
  const sourceRepo = source?.sourceRepo?.trim() || void 0;
3294
3429
  const sourceBranch = source?.sourceBranch?.trim() || void 0;
@@ -3308,19 +3443,21 @@ var Registry = class {
3308
3443
  ...sourcePatchUrl ? { sourcePatchUrl } : {}
3309
3444
  };
3310
3445
  this.store.update((c) => {
3311
- if (this.nameClash(c.engines, finalName)) throw new NameTakenError();
3446
+ const holder = this.nameHolder(c.engines, finalName);
3447
+ if (holder) throw new NameTakenError(holder, source);
3312
3448
  c.engines.push(eng);
3313
3449
  if (!c.activeEngineId) c.activeEngineId = eng.id;
3314
3450
  });
3315
3451
  return { engine: eng, warning: pr.version === "unknown" ? "no_version" : void 0 };
3316
3452
  }
3317
- /** True if any registered engine already uses `name` (case-insensitive, trimmed). */
3318
- nameClash(engines, name) {
3453
+ /** The registered engine that already uses `name` (case-insensitive, trimmed), if any. */
3454
+ nameHolder(engines, name) {
3319
3455
  const n = name.trim().toLowerCase();
3320
- return engines.some((e) => e.name.trim().toLowerCase() === n);
3456
+ return engines.find((e) => e.name.trim().toLowerCase() === n);
3321
3457
  }
3322
- assertNameFree(name) {
3323
- if (this.nameClash(this.store.snapshot().engines, name)) throw new NameTakenError();
3458
+ assertNameFree(name, incoming) {
3459
+ const holder = this.nameHolder(this.store.snapshot().engines, name);
3460
+ if (holder) throw new NameTakenError(holder, incoming);
3324
3461
  }
3325
3462
  /** Register an MLX engine (kind='mlx'). No llama-server probe — the binPath is
3326
3463
  * a venv python, not a llama-server, so capabilities/flags don't apply. */
@@ -5142,9 +5279,9 @@ ${e.message}`, timedOut });
5142
5279
  resolve6({ exitCode, stdout, stderr, timedOut });
5143
5280
  };
5144
5281
  const armGrace = (ms) => {
5145
- const capped = hardDeadlineAt === void 0 ? ms : Math.max(0, Math.min(ms, hardDeadlineAt - Date.now()));
5282
+ const capped2 = hardDeadlineAt === void 0 ? ms : Math.max(0, Math.min(ms, hardDeadlineAt - Date.now()));
5146
5283
  if (graceTimer) clearTimeout(graceTimer);
5147
- graceTimer = setTimeout(finish, capped);
5284
+ graceTimer = setTimeout(finish, capped2);
5148
5285
  };
5149
5286
  const finishIfDrained = () => {
5150
5287
  if (!exited || settled) return;
@@ -5257,7 +5394,7 @@ function parseClaudeCliStreamJson(stdout) {
5257
5394
 
5258
5395
  // src/cli-launch.ts
5259
5396
  import { spawn as spawn4 } from "child_process";
5260
- import { homedir as homedir2 } from "os";
5397
+ import { homedir as homedir3 } from "os";
5261
5398
  import { readFile, writeFile, mkdir } from "fs/promises";
5262
5399
  import { join as join15, dirname as dirname7 } from "path";
5263
5400
 
@@ -5430,7 +5567,7 @@ function resolveModelKey(models, input) {
5430
5567
  return null;
5431
5568
  }
5432
5569
  var realFs = {
5433
- home: homedir2(),
5570
+ home: homedir3(),
5434
5571
  readFile: (p) => readFile(p, "utf8"),
5435
5572
  writeFile: (p, data) => writeFile(p, data, "utf8"),
5436
5573
  mkdir: async (p) => {
@@ -7840,7 +7977,7 @@ var AppUpdateChecker = class {
7840
7977
  };
7841
7978
 
7842
7979
  // src/app-update-apply.ts
7843
- import { execFile as execFile10, spawn as spawn6 } from "child_process";
7980
+ import { execFile as execFile11, spawn as spawn6 } from "child_process";
7844
7981
  import { existsSync as existsSync17, openSync as openSync2, readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
7845
7982
  import { dirname as dirname8, join as join18, resolve as resolve2 } from "path";
7846
7983
  import { fileURLToPath } from "url";
@@ -7937,7 +8074,7 @@ async function npmGlobalRoot() {
7937
8074
  if (npmRootCache) return npmRootCache.value;
7938
8075
  const value = await new Promise((res) => {
7939
8076
  const cmd = process.platform === "win32" ? "npm.cmd" : "npm";
7940
- execFile10(cmd, ["root", "-g"], { timeout: 1e4, windowsHide: true }, (e, stdout) => {
8077
+ execFile11(cmd, ["root", "-g"], { timeout: 1e4, windowsHide: true }, (e, stdout) => {
7941
8078
  if (e) return res(null);
7942
8079
  const out = String(stdout).trim();
7943
8080
  res(out || null);
@@ -10133,9 +10270,9 @@ async function loadPrimaryModel(opts, { manager: manager2, comfy: comfy2, modelR
10133
10270
 
10134
10271
  // src/models/hf-cache.ts
10135
10272
  import { existsSync as existsSync20, mkdirSync as mkdirSync8 } from "fs";
10136
- import { homedir as homedir3 } from "os";
10273
+ import { homedir as homedir4 } from "os";
10137
10274
  import { join as join22 } from "path";
10138
- function hfHubCacheDir(env = process.env, home = homedir3()) {
10275
+ function hfHubCacheDir(env = process.env, home = homedir4()) {
10139
10276
  const explicit = env.HUGGINGFACE_HUB_CACHE;
10140
10277
  if (explicit && explicit.trim()) return explicit;
10141
10278
  const hfHome = env.HF_HOME;
@@ -13668,7 +13805,7 @@ function repoSubdir(repo) {
13668
13805
  }
13669
13806
 
13670
13807
  // src/bench/bench.ts
13671
- import { execFile as execFile11 } from "child_process";
13808
+ import { execFile as execFile12 } from "child_process";
13672
13809
  import { readFileSync as readFileSync11 } from "fs";
13673
13810
  import { randomUUID as randomUUID8 } from "crypto";
13674
13811
 
@@ -15977,7 +16114,7 @@ function kvSpeedAdvisory(tps, currentKv, supportedKvTypes) {
15977
16114
  function readNvidiaVramMb() {
15978
16115
  return new Promise((resolve6) => {
15979
16116
  try {
15980
- execFile11(
16117
+ execFile12(
15981
16118
  "nvidia-smi",
15982
16119
  ["--query-gpu=memory.used", "--format=csv,noheader,nounits"],
15983
16120
  { timeout: 8e3, windowsHide: true },
@@ -16010,7 +16147,7 @@ function parseRocmVramUsed(memJson) {
16010
16147
  function readRocmVramMb() {
16011
16148
  return new Promise((resolve6) => {
16012
16149
  try {
16013
- execFile11("rocm-smi", ["--showmeminfo", "vram", "--json"], { timeout: 8e3, windowsHide: true }, (err9, stdout) => {
16150
+ execFile12("rocm-smi", ["--showmeminfo", "vram", "--json"], { timeout: 8e3, windowsHide: true }, (err9, stdout) => {
16014
16151
  resolve6(err9 || !stdout ? null : parseRocmVramUsed(stdout));
16015
16152
  });
16016
16153
  } catch {
@@ -16030,7 +16167,7 @@ function readWddmSharedMb() {
16030
16167
  if (process.platform !== "win32" || wddmSharedUnsupported) return Promise.resolve(null);
16031
16168
  return new Promise((resolve6) => {
16032
16169
  try {
16033
- execFile11(
16170
+ execFile12(
16034
16171
  "powershell",
16035
16172
  ["-NoProfile", "-NonInteractive", "-Command", WDDM_SHARED_PS],
16036
16173
  { timeout: 1e4, windowsHide: true },
@@ -16069,7 +16206,7 @@ function parseRocmGttUsed(memJson) {
16069
16206
  function readRocmGttMb() {
16070
16207
  return new Promise((resolve6) => {
16071
16208
  try {
16072
- execFile11("rocm-smi", ["--showmeminfo", "gtt", "--json"], { timeout: 8e3, windowsHide: true }, (err9, stdout) => {
16209
+ execFile12("rocm-smi", ["--showmeminfo", "gtt", "--json"], { timeout: 8e3, windowsHide: true }, (err9, stdout) => {
16073
16210
  if (err9 || !stdout) return resolve6(null);
16074
16211
  resolve6(parseRocmGttUsed(stdout));
16075
16212
  });
@@ -19327,7 +19464,7 @@ __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
19327
19464
 
19328
19465
  // src/api/routes.ts
19329
19466
  import { randomUUID as randomUUID12 } from "crypto";
19330
- import { homedir as homedir4 } from "os";
19467
+ import { homedir as homedir5 } from "os";
19331
19468
 
19332
19469
  // src/config/config-bounds.ts
19333
19470
  var CONFIG_BOUNDS = {
@@ -22065,12 +22202,12 @@ function catalogEngine(id) {
22065
22202
  }
22066
22203
 
22067
22204
  // src/engines/cuda-provision.ts
22068
- import { execFile as execFile12 } from "child_process";
22205
+ import { execFile as execFile13 } from "child_process";
22069
22206
  import { cpSync as cpSync2, existsSync as existsSync24, mkdirSync as mkdirSync14, readdirSync as readdirSync8, rmSync as rmSync13 } from "fs";
22070
22207
  import { tmpdir } from "os";
22071
22208
  import { join as join30 } from "path";
22072
- import { promisify as promisify8 } from "util";
22073
- var execFileP8 = promisify8(execFile12);
22209
+ import { promisify as promisify9 } from "util";
22210
+ var execFileP9 = promisify9(execFile13);
22074
22211
  var REDIST_BASE = "https://developer.download.nvidia.com/compute/cuda/redist/";
22075
22212
  var CUDA_REQUIRED_COMPONENTS = ["cuda_nvcc", "cuda_cudart", "libcublas", "cuda_cccl"];
22076
22213
  var CUDA_OPTIONAL_COMPONENTS = ["libnvvm", "cuda_crt"];
@@ -22086,7 +22223,7 @@ function cmpMajorMinor(a, b) {
22086
22223
  }
22087
22224
  async function driverMaxCuda() {
22088
22225
  try {
22089
- const { stdout } = await execFileP8("nvidia-smi", [], { timeout: 8e3, windowsHide: true, maxBuffer: 1024 * 1024 });
22226
+ const { stdout } = await execFileP9("nvidia-smi", [], { timeout: 8e3, windowsHide: true, maxBuffer: 1024 * 1024 });
22090
22227
  const m = stdout.match(/CUDA Version:\s*([\d.]+)/i);
22091
22228
  return m ? m[1] : null;
22092
22229
  } catch {
@@ -22248,7 +22385,7 @@ function recommendEngines(p, engines) {
22248
22385
  }
22249
22386
 
22250
22387
  // src/sysinfo/usage.ts
22251
- import { execFile as execFile13, spawn as spawn8 } from "child_process";
22388
+ import { execFile as execFile14, spawn as spawn8 } from "child_process";
22252
22389
  import { existsSync as existsSync25, readFileSync as readFileSync15, readdirSync as readdirSync9 } from "fs";
22253
22390
  import os2 from "os";
22254
22391
 
@@ -22498,7 +22635,7 @@ var MAX_CONSECUTIVE_FAILURES = 3;
22498
22635
  function run(cmd, args) {
22499
22636
  return new Promise((resolve6, reject) => {
22500
22637
  try {
22501
- execFile13(cmd, args, { timeout: READER_TIMEOUT_MS, windowsHide: true, maxBuffer: 4 * 1024 * 1024 }, (err9, stdout) => {
22638
+ execFile14(cmd, args, { timeout: READER_TIMEOUT_MS, windowsHide: true, maxBuffer: 4 * 1024 * 1024 }, (err9, stdout) => {
22502
22639
  if (err9) reject(err9);
22503
22640
  else resolve6(String(stdout ?? ""));
22504
22641
  });
@@ -23815,13 +23952,9 @@ function registerApi(app2, d) {
23815
23952
  installed2 = existsSync27(llamafileBinPath(enginesRoot));
23816
23953
  enabled = regEngines.some((x) => x.kind === "llamafile");
23817
23954
  }
23818
- const isBranchCapable = !e.sourceCommit && !e.patchUrl;
23819
- const matchBranch = isBranchCapable ? branchParam ?? "" : void 0;
23820
- const srcEng = e.excludeFromSourceMatch ? void 0 : regEngines.find(
23821
- (x) => sameRepo(x.sourceRepo, e.homepage) && (x.sourceCommit ?? "") === (e.sourceCommit ?? "") && (x.sourcePatchUrl ?? "") === (e.patchUrl ?? "") && (x.sourceBranch ?? "") === (matchBranch ?? "")
23822
- );
23823
- let sourceBinPath = srcEng?.binPath;
23824
- if (!srcEng && !e.excludeFromSourceMatch) sourceBinPath = sourceBuildBinary(enginesRoot, e.homepage, matchBranch, e.sourceCommit) ?? void 0;
23955
+ const srcEng = e.excludeFromSourceMatch ? void 0 : findEngineForCatalogEntry(regEngines, e, branchParam);
23956
+ const onDisk = !srcEng && !e.excludeFromSourceMatch ? findCatalogBuildOnDisk(enginesRoot, e, branchParam) : void 0;
23957
+ const sourceBinPath = srcEng?.binPath ?? onDisk?.binPath;
23825
23958
  const sourceBuilt = !!srcEng || !!sourceBinPath;
23826
23959
  if (srcEng) {
23827
23960
  installed2 = true;
@@ -23836,7 +23969,7 @@ function registerApi(app2, d) {
23836
23969
  enabled,
23837
23970
  sourceBuilt,
23838
23971
  sourceEngineId: srcEng?.id,
23839
- sourceBranch: srcEng?.sourceBranch ?? "",
23972
+ sourceBranch: srcEng?.sourceBranch ?? onDisk?.branch ?? "",
23840
23973
  sourceBinPath: sourceBinPath ?? ""
23841
23974
  };
23842
23975
  });
@@ -23921,6 +24054,12 @@ function registerApi(app2, d) {
23921
24054
  const enginesRoot = join34(d.store.dir(), "engines");
23922
24055
  const toolchainDirs = d.store.snapshot().build.toolchainDirs;
23923
24056
  const buildRoot = join34(enginesRoot, "build", buildDirName(repoUrl, branch, commit));
24057
+ const engines = d.registry.list().engines;
24058
+ const conflict = findNameConflict(engines, name, { buildRoot, sourceRepo: repoUrl, sourceBranch: branch, sourceCommit: commit });
24059
+ if (conflict) return err5(c, 400, "name_already_taken", nameTakenMessage(conflict, { sourceRepo: repoUrl, sourceBranch: branch }));
24060
+ const running2 = d.registry.active();
24061
+ if (running2 && engineBusy(d) && isEngineInBuildDir(running2.binPath, buildRoot))
24062
+ return err5(c, 409, "engine_in_use", `Stop "${running2.name}" before rebuilding it \u2014 the rebuild replaces its files.`);
23924
24063
  const ac = new AbortController();
23925
24064
  buildAbort = ac;
23926
24065
  d.build.start(name ?? repoUrl);
@@ -23954,7 +24093,8 @@ function registerApi(app2, d) {
23954
24093
  binPath: out.binPath,
23955
24094
  sourceRepo: repoUrl,
23956
24095
  sourceBranch: branch,
23957
- sourceCommit: commit
24096
+ sourceCommit: commit,
24097
+ defaultBranch: out.defaultBranch
23958
24098
  });
23959
24099
  if (prior) {
23960
24100
  if (d.registry.active()?.id === prior.id) await d.manager.stopAndWait();
@@ -24205,10 +24345,11 @@ function registerApi(app2, d) {
24205
24345
  const { engine, warning } = await d.registry.add(b.name ?? "", b.binPath, {
24206
24346
  sourceRepo: b.sourceRepo,
24207
24347
  sourceBranch: b.sourceBranch,
24208
- sourceCommit: b.sourceCommit
24348
+ sourceCommit: b.sourceCommit,
24349
+ sourcePatchUrl: b.sourcePatchUrl
24209
24350
  });
24210
24351
  if (!isCatalogRepo(b.sourceRepo)) {
24211
- d.registry.recordCustomSource({ name: engine.name, binPath: engine.binPath, kind: engine.kind, sourceRepo: b.sourceRepo, sourceBranch: b.sourceBranch, sourceCommit: b.sourceCommit });
24352
+ d.registry.recordCustomSource({ name: engine.name, binPath: engine.binPath, kind: engine.kind, sourceRepo: b.sourceRepo, sourceBranch: b.sourceBranch, sourceCommit: b.sourceCommit, sourcePatchUrl: b.sourcePatchUrl });
24212
24353
  }
24213
24354
  return c.json({ ...engine, warning: warning ?? null }, 201);
24214
24355
  } catch (e) {
@@ -24436,7 +24577,7 @@ function registerApi(app2, d) {
24436
24577
  if (raw === FS_DRIVES_ROOT && process.platform === "win32") {
24437
24578
  return c.json({ path: FS_DRIVES_ROOT, parent: null, entries: listWindowsDrives() });
24438
24579
  }
24439
- const target = raw ? resolve5(raw) : homedir4();
24580
+ const target = raw ? resolve5(raw) : homedir5();
24440
24581
  let real;
24441
24582
  try {
24442
24583
  real = realpathSync3(target);
@@ -24977,7 +25118,7 @@ function registerApi(app2, d) {
24977
25118
  };
24978
25119
  const isMemoryServer = server2.transport === "stdio" && (server2.command?.includes("server-memory") || server2.args?.some((a) => a.includes("server-memory")));
24979
25120
  if (isMemoryServer && !server2.env?.MEMORY_FILE_PATH) {
24980
- server2.env = { ...server2.env, MEMORY_FILE_PATH: join34(homedir4(), ".turbollm", "mcp-memory.jsonl") };
25121
+ server2.env = { ...server2.env, MEMORY_FILE_PATH: join34(homedir5(), ".turbollm", "mcp-memory.jsonl") };
24981
25122
  }
24982
25123
  d.store.update((cfg2) => {
24983
25124
  cfg2.mcp.servers.push(server2);
@@ -25786,8 +25927,8 @@ function isValidMachineName(name) {
25786
25927
  }
25787
25928
  function sanitizeMachineName(raw) {
25788
25929
  const collapsed = (raw ?? "").replace(/[\\/]+/g, "-").replace(/\s+/g, " ").trim();
25789
- const capped = collapsed.slice(0, MAX_NAME_LEN).trim();
25790
- return /[^\s-]/.test(capped) ? capped : FALLBACK_MACHINE_NAME;
25930
+ const capped2 = collapsed.slice(0, MAX_NAME_LEN).trim();
25931
+ return /[^\s-]/.test(capped2) ? capped2 : FALLBACK_MACHINE_NAME;
25791
25932
  }
25792
25933
  function uniqueMachineName(desired, taken) {
25793
25934
  const used = /* @__PURE__ */ new Set();
@@ -25877,7 +26018,7 @@ function redactDownload(rec) {
25877
26018
  // src/link/link-client.ts
25878
26019
  var DEFAULT_TIMEOUT_MS = 8e3;
25879
26020
  var ERROR_CODE = /^[a-z][a-z0-9_]{0,63}$/i;
25880
- async function failureDetail(res) {
26021
+ async function failureDetail2(res) {
25881
26022
  if (!(res.headers.get("content-type") ?? "").includes("application/json")) return {};
25882
26023
  try {
25883
26024
  const body10 = await res.json();
@@ -26102,7 +26243,7 @@ var LinkClient = class {
26102
26243
  ...json === void 0 ? {} : { body: JSON.stringify(json) },
26103
26244
  signal: AbortSignal.timeout(this.timeoutMs)
26104
26245
  });
26105
- if (!res.ok) return { kind: "http", status: res.status, ...await failureDetail(res) };
26246
+ if (!res.ok) return { kind: "http", status: res.status, ...await failureDetail2(res) };
26106
26247
  const ct = res.headers.get("content-type") ?? "";
26107
26248
  if (!ct.includes("application/json")) return { kind: "network" };
26108
26249
  const parsed = await res.json();