dsh-plugin-local-infer 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/README.md +32 -6
  2. package/cordis.patch.yml +7 -0
  3. package/docs/zh/README.md +24 -4
  4. package/dsh/client.js +4200 -4049
  5. package/package.json +33 -11
  6. package/packages/core/lib/adapter.d.ts +177 -11
  7. package/packages/core/lib/adapter.js +74 -1
  8. package/packages/core/lib/config.d.ts +1 -1
  9. package/packages/core/lib/config.js +1 -1
  10. package/packages/core/lib/debug.d.ts +1 -1
  11. package/packages/core/lib/debug.js +1 -1
  12. package/packages/core/lib/defaults.d.ts +1 -1
  13. package/packages/core/lib/defaults.js +1 -1
  14. package/packages/core/lib/deps.d.ts +48 -20
  15. package/packages/core/lib/deps.js +24 -8
  16. package/packages/core/lib/estimator.d.ts +1 -1
  17. package/packages/core/lib/estimator.js +1 -1
  18. package/packages/core/lib/guideline.d.ts +1 -1
  19. package/packages/core/lib/guideline.js +1 -1
  20. package/packages/core/lib/hardware.d.ts +1 -1
  21. package/packages/core/lib/hardware.js +1 -1
  22. package/packages/core/lib/hf-cache.d.ts +70 -0
  23. package/packages/core/lib/hf-cache.js +83 -0
  24. package/packages/core/lib/index.d.ts +16 -9
  25. package/packages/core/lib/index.js +10 -6
  26. package/packages/core/lib/infer-cache.d.ts +39 -0
  27. package/packages/core/lib/infer-cache.js +53 -0
  28. package/packages/core/lib/launch-command.d.ts +8 -4
  29. package/packages/core/lib/launch-command.js +13 -6
  30. package/packages/core/lib/launch-record.d.ts +1 -1
  31. package/packages/core/lib/launch-record.js +1 -1
  32. package/packages/core/lib/logs.d.ts +8 -1
  33. package/packages/core/lib/logs.js +12 -4
  34. package/packages/core/lib/models.d.ts +120 -14
  35. package/packages/core/lib/models.js +209 -17
  36. package/packages/core/lib/parameter.d.ts +31 -3
  37. package/packages/core/lib/parameter.js +1 -1
  38. package/packages/core/lib/progress.d.ts +54 -0
  39. package/packages/core/lib/progress.js +120 -0
  40. package/packages/core/lib/registrar.d.ts +25 -14
  41. package/packages/core/lib/registrar.js +30 -10
  42. package/packages/core/lib/reserve.d.ts +11 -3
  43. package/packages/core/lib/reserve.js +6 -4
  44. package/packages/core/lib/status.d.ts +12 -6
  45. package/packages/core/lib/status.js +21 -10
  46. package/packages/core/lib/sudo.d.ts +80 -0
  47. package/packages/core/lib/sudo.js +78 -0
  48. package/packages/core/package.json +1 -1
  49. package/packages/orchestrator/lib/config.d.ts +63 -0
  50. package/packages/orchestrator/lib/config.js +111 -0
  51. package/packages/orchestrator/lib/index.d.ts +51 -0
  52. package/packages/orchestrator/lib/index.js +73 -0
  53. package/packages/orchestrator/lib/route.d.ts +60 -0
  54. package/packages/orchestrator/lib/route.js +159 -0
  55. package/packages/orchestrator/locale/en.json +6 -0
  56. package/packages/orchestrator/package.json +39 -0
  57. package/packages/orchestrator/prompt.md +87 -0
  58. package/packages/providers/lib/acquire.d.ts +95 -15
  59. package/packages/providers/lib/acquire.js +239 -40
  60. package/packages/providers/lib/adapters.d.ts +155 -221
  61. package/packages/providers/lib/adapters.js +589 -935
  62. package/packages/providers/lib/debug.d.ts +1 -1
  63. package/packages/providers/lib/debug.js +1 -1
  64. package/packages/providers/lib/deps.d.ts +31 -212
  65. package/packages/providers/lib/deps.js +71 -205
  66. package/packages/providers/lib/engines/basalt/basalt.d.ts +130 -0
  67. package/packages/providers/lib/engines/basalt/basalt.js +304 -0
  68. package/packages/providers/lib/engines/basalt/basaltAdapter.d.ts +85 -0
  69. package/packages/providers/lib/engines/basalt/basaltAdapter.js +439 -0
  70. package/packages/providers/lib/engines/basalt/basaltLinux.d.ts +23 -0
  71. package/packages/providers/lib/engines/basalt/basaltLinux.js +47 -0
  72. package/packages/providers/lib/engines/basalt/basaltMacOS.d.ts +17 -0
  73. package/packages/providers/lib/engines/basalt/basaltMacOS.js +19 -0
  74. package/packages/providers/lib/engines/basalt/basaltWindows.d.ts +24 -0
  75. package/packages/providers/lib/engines/basalt/basaltWindows.js +49 -0
  76. package/packages/providers/lib/engines/engineOs.d.ts +24 -0
  77. package/packages/providers/lib/engines/engineOs.js +17 -0
  78. package/packages/providers/lib/{hyperqwen.d.ts → engines/hyperqwen/hyperqwen.d.ts} +2 -2
  79. package/packages/providers/lib/{hyperqwen.js → engines/hyperqwen/hyperqwen.js} +14 -2
  80. package/packages/providers/lib/engines/hyperqwen/hyperqwenAdapter.d.ts +38 -0
  81. package/packages/providers/lib/engines/hyperqwen/hyperqwenAdapter.js +115 -0
  82. package/packages/providers/lib/engines/hyperqwen/hyperqwenArch.d.ts +9 -0
  83. package/packages/providers/lib/engines/hyperqwen/hyperqwenArch.js +9 -0
  84. package/packages/providers/lib/engines/hyperqwen/hyperqwenDebian.d.ts +9 -0
  85. package/packages/providers/lib/engines/hyperqwen/hyperqwenDebian.js +9 -0
  86. package/packages/providers/lib/engines/hyperqwen/hyperqwenFedora.d.ts +9 -0
  87. package/packages/providers/lib/engines/hyperqwen/hyperqwenFedora.js +9 -0
  88. package/packages/providers/lib/engines/hyperqwen/hyperqwenLinux.d.ts +10 -0
  89. package/packages/providers/lib/engines/hyperqwen/hyperqwenLinux.js +14 -0
  90. package/packages/providers/lib/engines/hyperqwen/hyperqwenMacOS.d.ts +9 -0
  91. package/packages/providers/lib/engines/hyperqwen/hyperqwenMacOS.js +9 -0
  92. package/packages/providers/lib/engines/hyperqwen/hyperqwenWindows.d.ts +9 -0
  93. package/packages/providers/lib/engines/hyperqwen/hyperqwenWindows.js +9 -0
  94. package/packages/providers/lib/{infernix.d.ts → engines/infernix/infernix.d.ts} +7 -4
  95. package/packages/providers/lib/{infernix.js → engines/infernix/infernix.js} +53 -8
  96. package/packages/providers/lib/engines/infernix/infernixAdapter.d.ts +52 -0
  97. package/packages/providers/lib/engines/infernix/infernixAdapter.js +235 -0
  98. package/packages/providers/lib/engines/infernix/infernixArch.d.ts +9 -0
  99. package/packages/providers/lib/engines/infernix/infernixArch.js +9 -0
  100. package/packages/providers/lib/engines/infernix/infernixDebian.d.ts +9 -0
  101. package/packages/providers/lib/engines/infernix/infernixDebian.js +9 -0
  102. package/packages/providers/lib/engines/infernix/infernixFedora.d.ts +9 -0
  103. package/packages/providers/lib/engines/infernix/infernixFedora.js +9 -0
  104. package/packages/providers/lib/engines/infernix/infernixLinux.d.ts +17 -0
  105. package/packages/providers/lib/engines/infernix/infernixLinux.js +35 -0
  106. package/packages/providers/lib/engines/infernix/infernixMacOS.d.ts +16 -0
  107. package/packages/providers/lib/engines/infernix/infernixMacOS.js +18 -0
  108. package/packages/providers/lib/engines/infernix/infernixWindows.d.ts +16 -0
  109. package/packages/providers/lib/engines/infernix/infernixWindows.js +34 -0
  110. package/packages/providers/lib/{llamacpp.d.ts → engines/llamacpp/llamacpp.d.ts} +2 -2
  111. package/packages/providers/lib/{llamacpp.js → engines/llamacpp/llamacpp.js} +12 -2
  112. package/packages/providers/lib/engines/llamacpp/llamacppAdapter.d.ts +50 -0
  113. package/packages/providers/lib/engines/llamacpp/llamacppAdapter.js +274 -0
  114. package/packages/providers/lib/engines/llamacpp/llamacppArch.d.ts +9 -0
  115. package/packages/providers/lib/engines/llamacpp/llamacppArch.js +9 -0
  116. package/packages/providers/lib/engines/llamacpp/llamacppDebian.d.ts +9 -0
  117. package/packages/providers/lib/engines/llamacpp/llamacppDebian.js +9 -0
  118. package/packages/providers/lib/engines/llamacpp/llamacppFedora.d.ts +9 -0
  119. package/packages/providers/lib/engines/llamacpp/llamacppFedora.js +9 -0
  120. package/packages/providers/lib/engines/llamacpp/llamacppLinux.d.ts +17 -0
  121. package/packages/providers/lib/engines/llamacpp/llamacppLinux.js +28 -0
  122. package/packages/providers/lib/engines/llamacpp/llamacppMacOS.d.ts +15 -0
  123. package/packages/providers/lib/engines/llamacpp/llamacppMacOS.js +17 -0
  124. package/packages/providers/lib/engines/llamacpp/llamacppWindows.d.ts +15 -0
  125. package/packages/providers/lib/engines/llamacpp/llamacppWindows.js +26 -0
  126. package/packages/providers/lib/{ninfer.d.ts → engines/ninfer/ninfer.d.ts} +2 -2
  127. package/packages/providers/lib/{ninfer.js → engines/ninfer/ninfer.js} +25 -3
  128. package/packages/providers/lib/engines/ninfer/ninferAdapter.d.ts +81 -0
  129. package/packages/providers/lib/engines/ninfer/ninferAdapter.js +392 -0
  130. package/packages/providers/lib/engines/ninfer/ninferArch.d.ts +9 -0
  131. package/packages/providers/lib/engines/ninfer/ninferArch.js +9 -0
  132. package/packages/providers/lib/engines/ninfer/ninferDebian.d.ts +9 -0
  133. package/packages/providers/lib/engines/ninfer/ninferDebian.js +9 -0
  134. package/packages/providers/lib/engines/ninfer/ninferFedora.d.ts +9 -0
  135. package/packages/providers/lib/engines/ninfer/ninferFedora.js +9 -0
  136. package/packages/providers/lib/engines/ninfer/ninferLinux.d.ts +17 -0
  137. package/packages/providers/lib/engines/ninfer/ninferLinux.js +37 -0
  138. package/packages/providers/lib/engines/ninfer/ninferMacOS.d.ts +15 -0
  139. package/packages/providers/lib/engines/ninfer/ninferMacOS.js +17 -0
  140. package/packages/providers/lib/engines/ninfer/ninferWindows.d.ts +29 -0
  141. package/packages/providers/lib/engines/ninfer/ninferWindows.js +59 -0
  142. package/packages/providers/lib/engines/strata/strata.d.ts +1 -0
  143. package/packages/providers/lib/engines/strata/strata.js +264 -0
  144. package/packages/providers/lib/engines/strata/strataAdapter.d.ts +97 -0
  145. package/packages/providers/lib/engines/strata/strataAdapter.js +463 -0
  146. package/packages/providers/lib/engines/strata/strataArch.d.ts +9 -0
  147. package/packages/providers/lib/engines/strata/strataArch.js +9 -0
  148. package/packages/providers/lib/engines/strata/strataDebian.d.ts +9 -0
  149. package/packages/providers/lib/engines/strata/strataDebian.js +9 -0
  150. package/packages/providers/lib/engines/strata/strataFedora.d.ts +9 -0
  151. package/packages/providers/lib/engines/strata/strataFedora.js +9 -0
  152. package/packages/providers/lib/engines/strata/strataLinux.d.ts +21 -0
  153. package/packages/providers/lib/engines/strata/strataLinux.js +30 -0
  154. package/packages/providers/lib/engines/strata/strataMacOS.d.ts +16 -0
  155. package/packages/providers/lib/engines/strata/strataMacOS.js +17 -0
  156. package/packages/providers/lib/engines/strata/strataWindows.d.ts +27 -0
  157. package/packages/providers/lib/engines/strata/strataWindows.js +37 -0
  158. package/packages/providers/lib/engines.d.ts +8 -5
  159. package/packages/providers/lib/engines.js +22 -11
  160. package/packages/providers/lib/guidelines.d.ts +5 -4
  161. package/packages/providers/lib/guidelines.js +18 -5
  162. package/packages/providers/lib/index.d.ts +33 -24
  163. package/packages/providers/lib/index.js +46 -24
  164. package/packages/providers/lib/model-defaults.d.ts +10 -5
  165. package/packages/providers/lib/model-defaults.js +47 -5
  166. package/packages/providers/lib/platform.d.ts +8 -5
  167. package/packages/providers/lib/platform.js +27 -7
  168. package/packages/providers/lib/presets.d.ts +6 -4
  169. package/packages/providers/lib/presets.js +53 -5
  170. package/packages/providers/lib/systemDeps.d.ts +313 -0
  171. package/packages/providers/lib/systemDeps.js +284 -0
  172. package/packages/providers/lib/vram.d.ts +34 -8
  173. package/packages/providers/lib/vram.js +81 -8
  174. package/packages/providers/package.json +2 -2
  175. package/packages/service/lib/index.d.ts +187 -23
  176. package/packages/service/lib/index.js +667 -90
  177. package/packages/service/lib/launch-target.d.ts +19 -0
  178. package/packages/service/lib/launch-target.js +28 -0
  179. package/packages/service/lib/run-logs.d.ts +72 -0
  180. package/packages/service/lib/run-logs.js +125 -0
  181. package/packages/service/lib/types.d.ts +166 -8
  182. package/packages/service/lib/types.js +1 -1
  183. package/packages/service/package.json +2 -2
  184. package/packages/tool/lib/index.d.ts +1 -1
  185. package/packages/tool/lib/index.js +1 -1
  186. package/packages/tool/package.json +2 -2
  187. package/packages/ui/lib/config.d.ts +89 -0
  188. package/packages/ui/lib/config.js +251 -0
  189. package/packages/ui/lib/deps.d.ts +4 -4
  190. package/packages/ui/lib/deps.js +1 -1
  191. package/packages/ui/lib/form.d.ts +1 -1
  192. package/packages/ui/lib/form.js +1 -1
  193. package/packages/ui/lib/index.d.ts +7 -1
  194. package/packages/ui/lib/index.js +12 -2
  195. package/packages/ui/lib/meter.d.ts +1 -1
  196. package/packages/ui/lib/meter.js +1 -1
  197. package/packages/ui/lib/ops/engine.d.ts +216 -0
  198. package/packages/ui/lib/ops/engine.js +697 -0
  199. package/packages/ui/lib/ops/get-steps.d.ts +98 -0
  200. package/packages/ui/lib/ops/get-steps.js +359 -0
  201. package/packages/ui/lib/ops/hf.d.ts +42 -0
  202. package/packages/ui/lib/ops/hf.js +150 -0
  203. package/packages/ui/lib/ops/logs.d.ts +78 -0
  204. package/packages/ui/lib/ops/logs.js +119 -0
  205. package/packages/ui/lib/ops/model.d.ts +71 -0
  206. package/packages/ui/lib/ops/model.js +123 -0
  207. package/packages/ui/lib/request.d.ts +106 -0
  208. package/packages/ui/lib/request.js +80 -0
  209. package/packages/ui/lib/route.d.ts +106 -422
  210. package/packages/ui/lib/route.js +136 -1845
  211. package/packages/ui/lib/routes/chat-template.d.ts +1 -0
  212. package/packages/ui/lib/routes/chat-template.js +1 -0
  213. package/packages/ui/lib/routes/deps.d.ts +1 -0
  214. package/packages/ui/lib/routes/deps.js +1 -0
  215. package/packages/ui/lib/routes/download.d.ts +1 -0
  216. package/packages/ui/lib/routes/download.js +1 -0
  217. package/packages/ui/lib/routes/get.d.ts +1 -0
  218. package/packages/ui/lib/routes/get.js +106 -0
  219. package/packages/ui/lib/routes/install.d.ts +16 -0
  220. package/packages/ui/lib/routes/install.js +36 -0
  221. package/packages/ui/lib/routes/launch.d.ts +24 -0
  222. package/packages/ui/lib/routes/launch.js +74 -0
  223. package/packages/ui/lib/routes/logs.d.ts +57 -0
  224. package/packages/ui/lib/routes/logs.js +117 -0
  225. package/packages/ui/lib/routes/provider-delete.d.ts +15 -0
  226. package/packages/ui/lib/routes/provider-delete.js +37 -0
  227. package/packages/ui/lib/routes/register.d.ts +16 -0
  228. package/packages/ui/lib/routes/register.js +28 -0
  229. package/packages/ui/lib/routes/save.d.ts +18 -0
  230. package/packages/ui/lib/routes/save.js +159 -0
  231. package/packages/ui/lib/routes/stop.d.ts +19 -0
  232. package/packages/ui/lib/routes/stop.js +32 -0
  233. package/packages/ui/lib/routes/stream.d.ts +40 -0
  234. package/packages/ui/lib/routes/stream.js +68 -0
  235. package/packages/ui/lib/routes/summary.d.ts +15 -0
  236. package/packages/ui/lib/routes/summary.js +26 -0
  237. package/packages/ui/lib/routes/unregister.d.ts +14 -0
  238. package/packages/ui/lib/routes/unregister.js +26 -0
  239. package/packages/ui/lib/viewmodels/deps.d.ts +1 -0
  240. package/packages/ui/lib/viewmodels/deps.js +1 -0
  241. package/packages/ui/lib/viewmodels/depsHelpers.d.ts +1 -0
  242. package/packages/ui/lib/viewmodels/depsHelpers.js +94 -0
  243. package/packages/ui/lib/viewmodels/summary.d.ts +145 -0
  244. package/packages/ui/lib/viewmodels/summary.js +245 -0
  245. package/packages/ui/lib/viewmodels/summaryHelpers.d.ts +30 -0
  246. package/packages/ui/lib/viewmodels/summaryHelpers.js +42 -0
  247. package/packages/ui/lib/warnings.d.ts +1 -1
  248. package/packages/ui/lib/warnings.js +1 -1
  249. package/packages/ui/package.json +4 -4
  250. package/packages/providers/lib/strata.d.ts +0 -15
  251. package/packages/providers/lib/strata.js +0 -14
@@ -0,0 +1,159 @@
1
+ /**
2
+ * The POST /local-infer/save route: it upserts a provider entry (an id
3
+ * updates the stored entry in place, an absent id updates a stored entry that
4
+ * already matches the engine, config, and platform in place, and only a save
5
+ * with no match mints a new id and prepends the entry), persists the global
6
+ * fields the card's toggles edit, re-syncs the service's platform registry with
7
+ * the saved map, and re-runs the registration for an already-registered model
8
+ * so its input types stay in step with the saved config.
9
+ * @module dsh-plugin-local-infer/ui/routes/save
10
+ */
11
+ import { parsePlatformConfigs, readConfig, writeConfig } from "../config.js";
12
+ import { syncPlatformConfigs } from "../ops/engine.js";
13
+ import { syncRegisteredModel } from "../ops/model.js";
14
+ import { defineRoute } from "../route.js";
15
+ import { buildSummary } from "../viewmodels/summary.js";
16
+ /**
17
+ * newProviderId mints the stable id a new provider entry carries: a
18
+ * timestamp base plus a random tail, so two Saves in the same tick never
19
+ * collide. The card edits and deletes against this id.
20
+ */
21
+ function newProviderId() {
22
+ return `provider-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
23
+ }
24
+ /**
25
+ * saveRoute builds the POST /local-infer/save route definition the register
26
+ * step holds by an effect.
27
+ */
28
+ export function saveRoute(localInfer) {
29
+ return defineRoute({
30
+ name: 'local-infer-save',
31
+ path: '/local-infer/save',
32
+ method: 'POST',
33
+ failStatus: 400,
34
+ tooLargeMessage: 'config payload too large',
35
+ run: async ({ body }, res) => {
36
+ const engine = typeof body.engine === 'string' ? body.engine : 'ninfer';
37
+ const config = body.config && typeof body.config === 'object' ? body.config : {};
38
+ const allowRunDeps = typeof body.allowRunDeps === 'boolean' ? body.allowRunDeps : false;
39
+ const platform = parsePlatformConfigs(body.platform);
40
+ // A body that predates the key keeps the descriptions visible.
41
+ const showHelp = typeof body.showHelp === 'boolean' ? body.showHelp : true;
42
+ const providerId = typeof body.id === 'string' && body.id.trim() !== '' ? body.id : undefined;
43
+ const stored = readConfig();
44
+ // A body that omits the log location (or sends a blank one) keeps the
45
+ // stored template, so an unsaved edit never wipes a custom path.
46
+ const logPath = typeof body.logPath === 'string' && body.logPath !== '' ? body.logPath : stored.logPath;
47
+ // The card's Auto Rebuild checkbox arrives as a body boolean; a body
48
+ // that omits the key (or carries a non-boolean) reads as absent so the
49
+ // upsert keeps the stored entry's flag instead of dropping it.
50
+ const autoRebuild = typeof body.autoRebuild === 'boolean' ? body.autoRebuild : undefined;
51
+ // carryAutoRebuild writes the Auto Rebuild flag onto an upserted
52
+ // entry: the body's boolean wins, a body that omits the key carries
53
+ // the stored entry's flag over, and an entry with neither keeps no key
54
+ // (the card draws the box unchecked, so no migration is needed).
55
+ const carryAutoRebuild = (provider, storedFlag) => {
56
+ const flag = autoRebuild ?? storedFlag;
57
+ if (flag !== undefined) {
58
+ provider.autoRebuild = flag;
59
+ }
60
+ };
61
+ // Save upserts a provider entry: an id updates the stored entry in
62
+ // place, an absent id updates a stored entry that already matches the
63
+ // engine, config, and platform in place, and only a save with no match
64
+ // mints a new id and prepends the entry, so a Save always adds (or
65
+ // refreshes) a card entry at the top of the list without duplicating a
66
+ // provider that is already stored. The provider's platform config is the
67
+ // per-engine entry the deploy step reads, so a provider never carries a
68
+ // field it cannot price. A save that updates an entry in place carries
69
+ // the entry's launch record over, so editing a running provider's config
70
+ // never drops the record the restart re-link prices, and carries the
71
+ // entry's Auto Rebuild flag over when the body omits it, so an old save
72
+ // caller never drops a stored checkbox value. A settings-only
73
+ // save skips the upsert: it persists the global fields the toggle edits
74
+ // and keeps the stored list intact, so the help and allow-run toggles
75
+ // never mint a duplicate entry.
76
+ let providers;
77
+ if (body.settingsOnly === true) {
78
+ providers = stored.providers;
79
+ }
80
+ else if (providerId !== undefined) {
81
+ // An id updates the stored entry in place, carrying its launch record
82
+ // over so editing a running provider's config never drops the record
83
+ // the restart re-link prices.
84
+ const existing = stored.providers.find((entry) => entry.id === providerId);
85
+ const provider = {
86
+ id: providerId,
87
+ engine,
88
+ config,
89
+ platform: platform[engine] ?? {},
90
+ };
91
+ carryAutoRebuild(provider, existing?.autoRebuild);
92
+ if (existing?.launch !== undefined) {
93
+ provider.launch = existing.launch;
94
+ }
95
+ providers = stored.providers.map((entry) => (entry.id === providerId ? provider : entry));
96
+ }
97
+ else {
98
+ // No id: a "New provider" save. If a stored entry already matches the
99
+ // engine, config, and platform, update it in place (carrying its launch
100
+ // record) instead of minting a duplicate: a "New provider" that restores
101
+ // the cached draft and Saves without a change would otherwise prepend a
102
+ // second copy of the same provider.
103
+ const platformConfig = platform[engine] ?? {};
104
+ const matching = stored.providers.find((entry) => entry.engine === engine &&
105
+ JSON.stringify(entry.config) === JSON.stringify(config) &&
106
+ JSON.stringify(entry.platform) === JSON.stringify(platformConfig));
107
+ if (matching !== undefined) {
108
+ const provider = {
109
+ id: matching.id,
110
+ engine,
111
+ config,
112
+ platform: platformConfig,
113
+ };
114
+ carryAutoRebuild(provider, matching.autoRebuild);
115
+ if (matching.launch !== undefined) {
116
+ provider.launch = matching.launch;
117
+ }
118
+ providers = stored.providers.map((entry) => (entry.id === matching.id ? provider : entry));
119
+ }
120
+ else {
121
+ const provider = {
122
+ id: newProviderId(),
123
+ engine,
124
+ config,
125
+ platform: platformConfig,
126
+ };
127
+ // A brand-new entry with no body key stays without the key.
128
+ carryAutoRebuild(provider, undefined);
129
+ providers = [provider, ...stored.providers];
130
+ }
131
+ }
132
+ writeConfig(engine, config, allowRunDeps, platform, showHelp, providers, logPath);
133
+ // Re-sync the service registry with the saved platform map, so the next
134
+ // launch reads the platform config the user just saved.
135
+ syncPlatformConfigs(localInfer, {
136
+ engine,
137
+ config,
138
+ allowRunDeps,
139
+ platform,
140
+ showHelp,
141
+ providers,
142
+ logPath,
143
+ lastLogs: stored.lastLogs,
144
+ });
145
+ // A Save on an already-registered model re-runs the registration, so
146
+ // the DSH model row's input types (the vision flag) stay in step with
147
+ // the config the user just saved. Best-effort: a profile without the
148
+ // settings seam, or an engine no adapter serves, leaves the save intact.
149
+ try {
150
+ await syncRegisteredModel(localInfer, engine, config);
151
+ }
152
+ catch {
153
+ // A sync that cannot run is a no-op for the save.
154
+ }
155
+ res.json(200, await buildSummary(localInfer, { engine, config }));
156
+ },
157
+ });
158
+ }
159
+ //# sourceMappingURL=save.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The POST /local-infer/stop route: it renders the same plan the launch
3
+ * rendered and runs the service's stop entry point, so the stop targets the
4
+ * exact container or process the launch started (a re-adopted engine through
5
+ * the record's pid). The stop then clears the target entry's launch record,
6
+ * so a recycled pid cannot re-link an engine the user just stopped. A stop
7
+ * that fails persists a note to the last-run logs and the log file and
8
+ * answers 500, so the card's stop deadline settles the entry to the red Logs
9
+ * state with the broken step named.
10
+ * @module dsh-plugin-local-infer/ui/routes/stop
11
+ */
12
+ import type { LocalInferService } from 'dsh-plugin-local-infer/service';
13
+ import type { LocalInferRoute } from '../route.ts';
14
+ /**
15
+ * stopRoute builds the POST /local-infer/stop route definition the register
16
+ * step holds by an effect.
17
+ */
18
+ export declare function stopRoute(localInfer: LocalInferService): LocalInferRoute;
19
+ //# sourceMappingURL=stop.d.ts.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The POST /local-infer/stop route: it renders the same plan the launch
3
+ * rendered and runs the service's stop entry point, so the stop targets the
4
+ * exact container or process the launch started (a re-adopted engine through
5
+ * the record's pid). The stop then clears the target entry's launch record,
6
+ * so a recycled pid cannot re-link an engine the user just stopped. A stop
7
+ * that fails persists a note to the last-run logs and the log file and
8
+ * answers 500, so the card's stop deadline settles the entry to the red Logs
9
+ * state with the broken step named.
10
+ * @module dsh-plugin-local-infer/ui/routes/stop
11
+ */
12
+ import { stopEngine } from "../ops/engine.js";
13
+ import { defineRoute } from "../route.js";
14
+ import { launchRequestFromBody } from "../request.js";
15
+ /**
16
+ * stopRoute builds the POST /local-infer/stop route definition the register
17
+ * step holds by an effect.
18
+ */
19
+ export function stopRoute(localInfer) {
20
+ return defineRoute({
21
+ name: 'local-infer-stop',
22
+ path: '/local-infer/stop',
23
+ method: 'POST',
24
+ failStatus: 500,
25
+ tooLargeMessage: 'stop payload too large',
26
+ run: async ({ body }, res) => {
27
+ await stopEngine(localInfer, launchRequestFromBody(body));
28
+ res.json(200, { stopped: true });
29
+ },
30
+ });
31
+ }
32
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The NDJSON stream helpers the get and launch routes share: the one-line
3
+ * writer, the throttled progress sink the routes hand the hf download's
4
+ * onChunk callback to, and the abort controller that watches the response
5
+ * for the card's Cancel. The helpers keep the two routes' stream wiring
6
+ * identical, so a change to the throttle cadence, the event line format, or
7
+ * the cancel rule lands in one place.
8
+ * @module dsh-plugin-local-infer/ui/routes/stream
9
+ */
10
+ import type { HfProgress } from 'dsh-plugin-local-infer/core';
11
+ import type { RouteResponder } from '../route.ts';
12
+ export declare const PROGRESS_MIN_INTERVAL_MS = 500;
13
+ /**
14
+ * ndjsonLine writes one NDJSON event line to the stream: the JSON-encoded
15
+ * event plus a newline, through the responder's chunk writer (a fake
16
+ * response without write() collects nothing).
17
+ */
18
+ export declare function ndjsonLine(res: RouteResponder, line: unknown): void;
19
+ /**
20
+ * throttledProgressSink builds the onChunk callback a stream route hands the
21
+ * hf download's progress sink to: the core chunk folder turns the tqdm lines
22
+ * into a structured state, and the sink emits the state at most once per
23
+ * PROGRESS_MIN_INTERVAL_MS, so a hot tqdm cadence never floods the stream.
24
+ * The first parseable state emits immediately (the last-emit timestamp
25
+ * starts at 0).
26
+ */
27
+ export declare function throttledProgressSink(write: (progress: HfProgress) => void): (chunk: string) => void;
28
+ /**
29
+ * abortOnResponseClose wires the card's cancel to the response: a client
30
+ * disconnect (the response's close before the route ends) aborts the
31
+ * returned controller, and markFinished settles the run so a close after
32
+ * the route ended reads as the normal end, not a cancel. A fake response
33
+ * without on() never fires the close, and the run degrades to running to
34
+ * completion.
35
+ */
36
+ export declare function abortOnResponseClose(res: RouteResponder): {
37
+ signal: AbortSignal;
38
+ markFinished(): void;
39
+ };
40
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The NDJSON stream helpers the get and launch routes share: the one-line
3
+ * writer, the throttled progress sink the routes hand the hf download's
4
+ * onChunk callback to, and the abort controller that watches the response
5
+ * for the card's Cancel. The helpers keep the two routes' stream wiring
6
+ * identical, so a change to the throttle cadence, the event line format, or
7
+ * the cancel rule lands in one place.
8
+ * @module dsh-plugin-local-infer/ui/routes/stream
9
+ */
10
+ import { foldHfProgressChunk } from '../../../core/lib/index.js';
11
+ // The minimum interval between progress events: the tqdm cadence is hot (a
12
+ // bar redraw per chunk), so a route emits at most one event per half second,
13
+ // and the card's own throttle bounds the store updates.
14
+ export const PROGRESS_MIN_INTERVAL_MS = 500;
15
+ /**
16
+ * ndjsonLine writes one NDJSON event line to the stream: the JSON-encoded
17
+ * event plus a newline, through the responder's chunk writer (a fake
18
+ * response without write() collects nothing).
19
+ */
20
+ export function ndjsonLine(res, line) {
21
+ res.write(JSON.stringify(line) + '\n');
22
+ }
23
+ /**
24
+ * throttledProgressSink builds the onChunk callback a stream route hands the
25
+ * hf download's progress sink to: the core chunk folder turns the tqdm lines
26
+ * into a structured state, and the sink emits the state at most once per
27
+ * PROGRESS_MIN_INTERVAL_MS, so a hot tqdm cadence never floods the stream.
28
+ * The first parseable state emits immediately (the last-emit timestamp
29
+ * starts at 0).
30
+ */
31
+ export function throttledProgressSink(write) {
32
+ let folded;
33
+ let lastProgressAt = 0;
34
+ return (chunk) => {
35
+ const state = foldHfProgressChunk(chunk, folded);
36
+ if (state === undefined)
37
+ return;
38
+ folded = state;
39
+ const now = Date.now();
40
+ if (now - lastProgressAt >= PROGRESS_MIN_INTERVAL_MS) {
41
+ lastProgressAt = now;
42
+ write(state);
43
+ }
44
+ };
45
+ }
46
+ /**
47
+ * abortOnResponseClose wires the card's cancel to the response: a client
48
+ * disconnect (the response's close before the route ends) aborts the
49
+ * returned controller, and markFinished settles the run so a close after
50
+ * the route ended reads as the normal end, not a cancel. A fake response
51
+ * without on() never fires the close, and the run degrades to running to
52
+ * completion.
53
+ */
54
+ export function abortOnResponseClose(res) {
55
+ const controller = new AbortController();
56
+ let finished = false;
57
+ res.onClose(() => {
58
+ if (!finished)
59
+ controller.abort();
60
+ });
61
+ return {
62
+ signal: controller.signal,
63
+ markFinished: () => {
64
+ finished = true;
65
+ },
66
+ };
67
+ }
68
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The GET /local-infer/summary route: the card's read model. The handler
3
+ * parses the engine and config query params (an absent engine or a broken
4
+ * config falls back to the stored config) and answers with the summary the
5
+ * card renders.
6
+ * @module dsh-plugin-local-infer/ui/routes/summary
7
+ */
8
+ import type { LocalInferService } from 'dsh-plugin-local-infer/service';
9
+ import type { LocalInferRoute } from '../route.ts';
10
+ /**
11
+ * summaryRoute builds the GET /local-infer/summary route definition the
12
+ * register step holds by an effect.
13
+ */
14
+ export declare function summaryRoute(localInfer: LocalInferService): LocalInferRoute;
15
+ //# sourceMappingURL=summary.d.ts.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The GET /local-infer/summary route: the card's read model. The handler
3
+ * parses the engine and config query params (an absent engine or a broken
4
+ * config falls back to the stored config) and answers with the summary the
5
+ * card renders.
6
+ * @module dsh-plugin-local-infer/ui/routes/summary
7
+ */
8
+ import { defineRoute } from "../route.js";
9
+ import { depsRequestFromUrl } from "../request.js";
10
+ import { buildSummary } from "../viewmodels/summary.js";
11
+ /**
12
+ * summaryRoute builds the GET /local-infer/summary route definition the
13
+ * register step holds by an effect.
14
+ */
15
+ export function summaryRoute(localInfer) {
16
+ return defineRoute({
17
+ name: 'local-infer-config',
18
+ path: '/local-infer/summary',
19
+ method: 'GET',
20
+ failStatus: 500,
21
+ run: async ({ url }, res) => {
22
+ res.json(200, await buildSummary(localInfer, depsRequestFromUrl(url)));
23
+ },
24
+ });
25
+ }
26
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The POST /local-infer/unregister route: it runs the service's removeModel
3
+ * entry point for the engine's provider and the model row the register step
4
+ * resolved, so the removal targets the exact entry the registration wrote.
5
+ * @module dsh-plugin-local-infer/ui/routes/unregister
6
+ */
7
+ import type { LocalInferService } from 'dsh-plugin-local-infer/service';
8
+ import type { LocalInferRoute } from '../route.ts';
9
+ /**
10
+ * unregisterRoute builds the POST /local-infer/unregister route definition
11
+ * the register step holds by an effect.
12
+ */
13
+ export declare function unregisterRoute(localInfer: LocalInferService): LocalInferRoute;
14
+ //# sourceMappingURL=unregister.d.ts.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The POST /local-infer/unregister route: it runs the service's removeModel
3
+ * entry point for the engine's provider and the model row the register step
4
+ * resolved, so the removal targets the exact entry the registration wrote.
5
+ * @module dsh-plugin-local-infer/ui/routes/unregister
6
+ */
7
+ import { unregisterFor } from "../ops/model.js";
8
+ import { defineRoute } from "../route.js";
9
+ import { registerRequestFromBody } from "../request.js";
10
+ /**
11
+ * unregisterRoute builds the POST /local-infer/unregister route definition
12
+ * the register step holds by an effect.
13
+ */
14
+ export function unregisterRoute(localInfer) {
15
+ return defineRoute({
16
+ name: 'local-infer-unregister',
17
+ path: '/local-infer/unregister',
18
+ method: 'POST',
19
+ failStatus: 500,
20
+ tooLargeMessage: 'unregister payload too large',
21
+ run: async ({ body }, res) => {
22
+ res.json(200, await unregisterFor(localInfer, registerRequestFromBody(body)));
23
+ },
24
+ });
25
+ }
26
+ //# sourceMappingURL=unregister.js.map
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The Play/Run helper for the deps panel: runInstallFor refuses with a 409
3
+ * while the trust gate is off (the copy/paste path is always the fallback),
4
+ * runs the install through the service, and refreshes the deps model from the
5
+ * re-check, so the "Get" gate opens only after a re-check passes.
6
+ * @module dsh-plugin-local-infer/ui/viewmodels/depsHelpers
7
+ */
8
+ import { SUDO_CLI_NOTE } from '../../../core/lib/index.js';
9
+ import { runLogKey } from '../../../service/lib/index.js';
10
+ import { readConfig } from "../config.js";
11
+ import { captureRunBaseline, finishRunSection, INSTALL_RUN_SEED } from "../ops/logs.js";
12
+ import { registerSudoObserver } from "../ops/engine.js";
13
+ import { RouteError } from "../route.js";
14
+ import { buildDeps } from "./deps.js";
15
+ /**
16
+ * runInstallFor is the Play/Run path: it refuses with a 409 while the
17
+ * trust gate is off (the copy/paste path is always the fallback), runs the
18
+ * install through the service, and refreshes the deps model from the
19
+ * re-check, so the "Get" gate opens only after a re-check passes. A run that
20
+ * actually starts brackets one live hub run under the runLogKey rule (the
21
+ * stored entry matching the engine and model, else the engine id): the seed
22
+ * is the "Installing deps..." separator, the service writes each install
23
+ * command's `$ <command>` line into the run (through onLog) before its child
24
+ * spawns, each per-dep install result appends in the Get step's
25
+ * ` <id>: <status>` shape, and the run ends at settle, a throwing install
26
+ * included. The settle text also folds through the shared run-section persister
27
+ * (finishRunSection over the pre-run baseline), so the last install run
28
+ * survives a card reload in the stored last-run logs and the log file, like
29
+ * the other provider processes; the baseline close keeps the sudo note the
30
+ * observer persists mid-run from landing twice. The run registers the sudo
31
+ * hook the launch and Get paths register, so a guarded dependency install
32
+ * waiting for a password on the CLI lands the shared SUDO_CLI_NOTE line in
33
+ * the live run and the persisted run. A 409 refusal starts no run.
34
+ */
35
+ export async function runInstallFor(localInfer, request) {
36
+ const stored = readConfig();
37
+ if (stored.allowRunDeps !== true) {
38
+ throw new RouteError(409, 'one-click install is not allowed; copy the install string instead');
39
+ }
40
+ const model = await buildDeps(localInfer, request);
41
+ const hardware = await localInfer.detectHardware();
42
+ const runKey = runLogKey(stored.providers, model.engineId, undefined, request.config ?? stored.config);
43
+ // The install run owns a sudo hook under its run key (the shared
44
+ // registerSudoObserver wiring): a guarded install waiting on the CLI writes
45
+ // the core CLI note into the live hub and, through persistProcessLog inside
46
+ // the helper, into the persisted run and log file, so a hung install reads
47
+ // where the prompt landed. The disposer clears only this run's slot.
48
+ // One append feeds both readers: the live hub run and the run's collected
49
+ // settle text (persisted at the finally), no second copy of the text.
50
+ let collected = '';
51
+ const append = (line) => {
52
+ collected += line;
53
+ localInfer.appendRunLog(runKey, line);
54
+ };
55
+ const clearSudoObserver = registerSudoObserver(localInfer, runKey, `${SUDO_CLI_NOTE}\n`, append);
56
+ // The stored last-run logs as they stand before the run persists anything:
57
+ // the sudo observer persists its note while the run hangs on the prompt,
58
+ // and finishRunSection closes the run over the baseline so that provisional
59
+ // line and the collected run text persist once (the Get route's pattern).
60
+ const baseline = captureRunBaseline();
61
+ localInfer.beginRun(runKey, INSTALL_RUN_SEED);
62
+ try {
63
+ const result = await localInfer.runInstall(model.engineId, model.buildMode, hardware, {
64
+ // Each install command's `$ <command>` line reaches the live hub and the
65
+ // collected run before the guarded runner spawns the child, so the run
66
+ // reads the command ahead of the per-dep results below.
67
+ onLog: append,
68
+ });
69
+ // The per-dep results stream as the re-check reported them, in the same
70
+ // indented shape the Get step's dep block logs, so both readers (the
71
+ // live buffer and, later, a re-rendered Logs pane) say the same thing.
72
+ for (const entry of result.results) {
73
+ append(` ${entry.id}: ${entry.status}${entry.detail !== undefined ? ` (${entry.detail})` : ''}\n`);
74
+ }
75
+ const deps = model.deps.map((dep) => {
76
+ const entry = result.results.find((entry) => entry.id === dep.id);
77
+ return entry === undefined ? dep : { ...dep, status: entry.status };
78
+ });
79
+ return {
80
+ result,
81
+ deps: { ...model, deps, allSatisfied: result.ok },
82
+ };
83
+ }
84
+ finally {
85
+ clearSudoObserver();
86
+ // The settle fold: the run's text (seed, command lines, per-dep results,
87
+ // any sudo note) persists as one section so a card reload still shows the
88
+ // last install run. A throwing install persists the partial section (the
89
+ // commands that ran) before the error reaches the route.
90
+ finishRunSection(baseline, `${INSTALL_RUN_SEED}${collected}`);
91
+ localInfer.endRun(runKey);
92
+ }
93
+ }
94
+ //# sourceMappingURL=depsHelpers.js.map
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The summary view model: the JSON the card renders. buildSummary assembles
3
+ * the engine list, the detected hardware, the form model for the selected
4
+ * engine, the VRAM meter model for the current config, the verdict the banner
5
+ * renders, the guideline warning model the red banner and inline warnings
6
+ * render from, and the config the form edits. The estimate is the single
7
+ * source the meter and the verdict banner agree on, and the guideline results
8
+ * are the single source the red banner and the inline warnings agree on.
9
+ * @module dsh-plugin-local-infer/ui/viewmodels/summary
10
+ */
11
+ import type { Config, DefaultProvenance, HardwareProfile, ProviderStatus, VramVerdict } from 'dsh-plugin-local-infer/core';
12
+ import type { LocalInferService } from 'dsh-plugin-local-infer/service';
13
+ import type { StoredPlatformConfig } from '../config.ts';
14
+ import type { FormModel } from '../form.ts';
15
+ import type { MeterModel } from '../meter.ts';
16
+ import type { WarningsModel } from '../warnings.ts';
17
+ /**
18
+ * LocalInferSummary is the JSON the card renders: the engine list, the model
19
+ * list, the detected hardware, the form model for the selected engine, the
20
+ * meter model for the current config, the verdict the banner renders, the
21
+ * guideline warning model the red banner and inline warnings render from,
22
+ * and the config the form edits. Every field is plain data, so it crosses
23
+ * the socket without a function left behind.
24
+ */
25
+ export interface LocalInferSummary {
26
+ engines: {
27
+ id: string;
28
+ label: string;
29
+ supports: {
30
+ docker: boolean;
31
+ local: boolean;
32
+ binary?: boolean;
33
+ };
34
+ defaultBuildCommand?: string;
35
+ defaultDockerTag?: string;
36
+ defaultEngineDirectory: string;
37
+ }[];
38
+ models: {
39
+ id: string;
40
+ source: string;
41
+ }[];
42
+ hardware: HardwareProfile;
43
+ engineId: string;
44
+ formModel: FormModel;
45
+ meterModel: MeterModel;
46
+ verdict: VramVerdict;
47
+ warningsModel: WarningsModel;
48
+ config: Config;
49
+ platformConfigs: Record<string, StoredPlatformConfig>;
50
+ defaults: DefaultsModel;
51
+ modelDirectory: string;
52
+ modelDefaults: ModelDefaultModel[];
53
+ modelStatus: ModelStatusModel | null;
54
+ chatTemplateStatus: ChatTemplateStatusModel | null;
55
+ modelFiles: string[];
56
+ showHelp: boolean;
57
+ providers: ProviderSummaryEntry[];
58
+ logPath: string;
59
+ lastLogs: string;
60
+ unsureBanner: {
61
+ message: string;
62
+ killCommands: string[];
63
+ } | null;
64
+ registered: boolean;
65
+ }
66
+ /**
67
+ * ProviderSummaryEntry is the card-facing view of one saved provider config:
68
+ * the stable id the card edits and deletes against, the engine and model the
69
+ * title renders ("NInfer: <model>"), the running-state the status dot paints,
70
+ * the per-provider Auto Rebuild checkbox value the edit form and the entry
71
+ * launch read, and the full config and platform the expanded form draws from.
72
+ * An entry saved before the autoRebuild key existed carries no key, so the
73
+ * card draws the checkbox unchecked.
74
+ */
75
+ export interface ProviderSummaryEntry {
76
+ id: string;
77
+ engine: string;
78
+ model: string;
79
+ status: ProviderStatus;
80
+ config: Config;
81
+ platform: StoredPlatformConfig;
82
+ autoRebuild?: boolean;
83
+ }
84
+ /**
85
+ * ModelDefaultModel is the card-facing view of one default model entry: the
86
+ * registry key the config `model` field carries, the label the dropdown
87
+ * shows, and the Hugging Face URL the no-CLI row links to.
88
+ */
89
+ export interface ModelDefaultModel {
90
+ id: string;
91
+ label: string;
92
+ url: string;
93
+ }
94
+ /**
95
+ * ModelStatusModel is the model-status read model the card draws: whether the
96
+ * model is present in the model directory, whether the hf CLI is available,
97
+ * and the download command the Download button runs. The card shows the row
98
+ * only while the model is absent.
99
+ */
100
+ export interface ModelStatusModel {
101
+ modelId: string;
102
+ label: string;
103
+ url: string;
104
+ present: boolean;
105
+ hfCli: boolean;
106
+ downloadCommand: string;
107
+ }
108
+ /**
109
+ * ChatTemplateStatusModel is the answer to "is the selected chat template in
110
+ * the model directory": the repo reference, its display label, whether the
111
+ * template file is present, whether the hf CLI is available, and the download
112
+ * command the card runs (and the System dependencies box shows when the auto-
113
+ * download is unavailable).
114
+ */
115
+ export interface ChatTemplateStatusModel {
116
+ repo: string;
117
+ label: string;
118
+ present: boolean;
119
+ hfCli: boolean;
120
+ downloadCommand: string;
121
+ }
122
+ /**
123
+ * DefaultsModel is the resolved-defaults read model the card draws: the
124
+ * starting config the defaults resolver produced and the per-key provenance
125
+ * that names the source that supplied each value (a preset, the user, or a
126
+ * spec default).
127
+ */
128
+ export interface DefaultsModel {
129
+ config: Config;
130
+ provenance: DefaultProvenance[];
131
+ }
132
+ /**
133
+ * buildSummary assembles the card's read model for one engine and config: the
134
+ * static engine and model lists, the detected hardware, the form model the
135
+ * selected engine's parameter surface renders to, the meter model the
136
+ * service's estimate prices, and the warning model the service's guideline
137
+ * engine fires. The estimate is the single source the meter and the verdict
138
+ * banner agree on, and the guideline results are the single source the red
139
+ * banner and the inline warnings agree on.
140
+ */
141
+ export declare function buildSummary(localInfer: LocalInferService, request?: {
142
+ engine?: string;
143
+ config?: Config;
144
+ }): Promise<LocalInferSummary>;
145
+ //# sourceMappingURL=summary.d.ts.map