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
@@ -8,7 +8,8 @@
8
8
  * local changes aborts, because a rebase would eat the user's work.
9
9
  * @module dsh-plugin-local-infer/providers/acquire
10
10
  */
11
- import type { LifecycleHost } from '../../core/lib/index.js';
11
+ import type { DeployOptions, EnginePlatformConfig, LaunchProgress, LifecycleHost } from '../../core/lib/index.js';
12
+ import type { EngineOsProfile } from './engines/engineOs.ts';
12
13
  /**
13
14
  * EngineSource is the per-engine acquisition data: the upstream git repo the
14
15
  * clone step uses when no fork is provided, the branch the fetch/rebase step
@@ -20,6 +21,7 @@ export interface EngineSource {
20
21
  repo: string;
21
22
  branch: string;
22
23
  buildSteps: readonly string[];
24
+ windowsBuildSteps?: readonly string[];
23
25
  }
24
26
  /**
25
27
  * The remote name a user-provided fork is added under, so the checkout's own
@@ -31,10 +33,36 @@ export declare const FORK_REMOTE = "dsh-fork";
31
33
  * through cmake (Infernix through its own preset); llama.cpp builds through
32
34
  * the same cmake shape as NInfer; Hyper-Qwen is the compose-driven vLLM
33
35
  * engine, so it carries no local build steps (the compose up pulls the
34
- * image); Strata is the placeholder engine, and its repo and pull step land
35
- * with the engine, the same way its docker image constant did.
36
+ * image); Strata builds through its own setup.sh (the README's
37
+ * Build-from-source route), which the source-build mode runs in the checkout;
38
+ * Basalt builds through its own CMake route (the Ninja/MSVC variant on
39
+ * Windows). The build steps are read from the per-engine OS profiles, so a
40
+ * build command has one home (the OS-specific files under engines/).
36
41
  */
37
42
  export declare const ENGINE_SOURCES: Record<string, EngineSource>;
43
+ /**
44
+ * engineOsProfile reads the registered build profile for an engine id on an OS,
45
+ * or undefined when none is registered. A host OS with no dedicated profile
46
+ * (a Windows host for an engine that carries no Windows route) reads the
47
+ * shared Linux profile.
48
+ */
49
+ export declare function engineOsProfile(engineId: string, os?: 'linux' | 'windows' | 'macos'): EngineOsProfile | undefined;
50
+ /**
51
+ * engineBuildDir reads the directory, relative to the checkout, where an
52
+ * engine's build leaves its binary on an OS, so the binary symlink step reads
53
+ * the right tree. A Windows host reads the engine's Windows build tree (the
54
+ * vcpkg route for NInfer writes into build-windows\apps\Release); every other
55
+ * host reads the shared Linux build directory. An engine that installs its
56
+ * binary to the path (Strata) carries no build directory, so it reads as
57
+ * `build`, the shared default.
58
+ */
59
+ export declare function engineBuildDir(engineId: string, os?: 'linux' | 'windows' | 'macos'): string;
60
+ /**
61
+ * engineBinary reads the engine binary name on an OS (the .exe on Windows), so
62
+ * the adapters and the symlink step share one source. An engine with no local
63
+ * binary (the compose engines) reads as undefined.
64
+ */
65
+ export declare function engineBinary(engineId: string, os?: 'linux' | 'windows' | 'macos'): string | undefined;
38
66
  /**
39
67
  * AcquireLog receives one line (or chunk) of Get output as it lands, so the
40
68
  * route can stream it to the card's Logs section.
@@ -50,42 +78,63 @@ export interface EngineStepResult {
50
78
  aborted?: boolean;
51
79
  detail?: string;
52
80
  }
81
+ /**
82
+ * SyncStepResult is the sync step's EngineStepResult plus the path it took:
83
+ * `cloned` true means a fresh clone ran (no prior checkout), so a failure on
84
+ * that path leaves no current version to launch; an absent `cloned` means the
85
+ * checkout was present and the rebase, fetch, or dirty path ran, so a failure
86
+ * there leaves a launchable current version. The Get step reads the flag to
87
+ * decide a sync failure's fatality instead of re-probing the checkout.
88
+ */
89
+ export interface SyncStepResult extends EngineStepResult {
90
+ cloned?: boolean;
91
+ }
53
92
  /**
54
93
  * engineSource reads the registered acquisition data for an engine id, or
55
94
  * undefined when none is registered.
56
95
  */
57
96
  export declare function engineSource(engineId: string): EngineSource | undefined;
58
97
  /**
59
- * engineBuildSteps reads the build steps an engine runs in its checkout, so
60
- * the adapter's deploy step and the Get build step share one list.
98
+ * engineBuildSteps reads the build steps an engine runs in its checkout for an
99
+ * OS, so the adapter's deploy step and the Get build step share one list. The
100
+ * steps come from the engine's OS profile: a Windows host reads the engine's
101
+ * Windows profile (the vcpkg route for NInfer), and a host OS with no
102
+ * dedicated profile reads the shared Linux profile.
61
103
  */
62
- export declare function engineBuildSteps(engineId: string): readonly string[];
104
+ export declare function engineBuildSteps(engineId: string, os?: 'linux' | 'windows' | 'macos'): readonly string[];
63
105
  /**
64
106
  * syncEngineRepo brings the engine checkout to the listed source. A missing
65
107
  * checkout is a fresh clone of the fork (or the engine's upstream repo when
66
108
  * no fork is provided). An existing checkout with local changes aborts, so
67
109
  * the user's work is never rebased away; a clean checkout fetches the listed
68
110
  * fork (added as the named fork remote when provided, origin when not) and
69
- * rebases onto its branch. Every command echoes into log as it runs.
111
+ * rebases onto its branch. Every command echoes into log as it runs. The
112
+ * result's `cloned` flag names the path taken: set on a fresh clone (the
113
+ * checkout was absent), absent on the present-checkout path, so a caller
114
+ * prices a failure's fatality from the flag instead of re-probing the
115
+ * checkout.
70
116
  */
71
117
  export declare function syncEngineRepo(host: LifecycleHost, request: {
72
118
  engineId: string;
73
119
  directory: string;
74
120
  fork?: string;
75
- }, log: AcquireLog): Promise<EngineStepResult>;
121
+ }, log: AcquireLog): Promise<SyncStepResult>;
76
122
  /**
77
123
  * SyncRebuildRequest names the checkout the conditional rebuild prices: the
78
- * engine, its directory, the optional fork remote, the engine binary the
79
- * symlink step links when a rebuild leaves it out of the path, and the
80
- * Play/Run trust gate that decides whether that symlink runs (on) or is
81
- * written to the logs (off).
124
+ * engine, its directory, the optional fork remote, and the Play/Run trust gate
125
+ * that decides whether the binary symlink (added when a rebuild leaves the
126
+ * binary out of the path) runs (on) or is written to the logs (off). The
127
+ * symlink step reads the bare binary name from the engine profile, so the
128
+ * request carries no binary of its own.
82
129
  */
83
130
  export interface SyncRebuildRequest {
84
131
  engineId: string;
85
132
  directory: string;
86
133
  fork?: string;
87
- binary?: string;
88
134
  allowRunDeps?: boolean;
135
+ buildCommands?: string;
136
+ onBuildStart?: () => void;
137
+ os?: 'linux' | 'windows' | 'macos';
89
138
  }
90
139
  /**
91
140
  * SyncRebuildResult reports the conditional rebuild: whether it passed,
@@ -115,11 +164,42 @@ export interface SyncRebuildResult {
115
164
  * added (run when the trust gate is on, written to the logs when it is off).
116
165
  */
117
166
  export declare function syncAndRebuildEngine(host: LifecycleHost, request: SyncRebuildRequest, log: AcquireLog): Promise<SyncRebuildResult>;
167
+ /**
168
+ * EngineDeploySync reports the deploy step's git sync (the shared
169
+ * syncAndRebuildEngine): whether it passed, a detail for a failure, and the
170
+ * captured build output the deploy step threads to the card's Logs section
171
+ * (a "Building..." separator leads a rebuild's output, the same line the
172
+ * card writes when the build step event lands).
173
+ */
174
+ export interface EngineDeploySync {
175
+ ok: boolean;
176
+ detail?: string;
177
+ buildLogs: string;
178
+ }
179
+ /**
180
+ * syncEngineForDeploy runs the deploy step's git sync (the shared
181
+ * syncAndRebuildEngine) with the option shape every adapter's deploy step
182
+ * assembles: the platform entry's fork and build-command override, the
183
+ * Play/Run trust gate, the plan's host OS (the Windows vcpkg route when the
184
+ * user carries no build-command override), and the progress sink the card's
185
+ * button reads (the git sync reports "Downloading...", the build step
186
+ * reports "Building..." the moment it starts). The captured build output is
187
+ * returned as buildLogs, so a caller threads it to the card's Logs section
188
+ * without re-running the steps.
189
+ */
190
+ export declare function syncEngineForDeploy(host: LifecycleHost, request: {
191
+ engineId: string;
192
+ directory: string;
193
+ platform?: EnginePlatformConfig;
194
+ options?: DeployOptions;
195
+ os?: 'linux' | 'windows' | 'macos';
196
+ onProgress?: LaunchProgress;
197
+ }): Promise<EngineDeploySync>;
118
198
  /**
119
199
  * buildEngineRepo runs the engine's build steps in the checkout, streaming
120
200
  * each step's output through log. An engine with no build steps (the compose
121
201
  * engines) passes through with a note; a failing step stops the build with
122
202
  * the step's detail.
123
203
  */
124
- export declare function buildEngineRepo(host: LifecycleHost, engineId: string, directory: string, log: AcquireLog): Promise<EngineStepResult>;
125
- //# sourceMappingURL=acquire.d.ts.map
204
+ export declare function buildEngineRepo(host: LifecycleHost, engineId: string, directory: string, log: AcquireLog, buildCommands?: string, os?: 'linux' | 'windows' | 'macos'): Promise<EngineStepResult>;
205
+ //# sourceMappingURL=acquire.d.ts.map
@@ -8,50 +8,134 @@
8
8
  * local changes aborts, because a rebase would eat the user's work.
9
9
  * @module dsh-plugin-local-infer/providers/acquire
10
10
  */
11
+ import { commandLogLine } from '../../core/lib/index.js';
12
+ import { NINFER_LINUX } from "./engines/ninfer/ninferLinux.js";
13
+ import { NINFER_MACOS } from "./engines/ninfer/ninferMacOS.js";
14
+ import { NINFER_WINDOWS } from "./engines/ninfer/ninferWindows.js";
15
+ import { INFERNIX_LINUX } from "./engines/infernix/infernixLinux.js";
16
+ import { INFERNIX_MACOS } from "./engines/infernix/infernixMacOS.js";
17
+ import { INFERNIX_WINDOWS } from "./engines/infernix/infernixWindows.js";
18
+ import { LLAMACPP_LINUX } from "./engines/llamacpp/llamacppLinux.js";
19
+ import { LLAMACPP_MACOS } from "./engines/llamacpp/llamacppMacOS.js";
20
+ import { LLAMACPP_WINDOWS } from "./engines/llamacpp/llamacppWindows.js";
21
+ import { HYPERQWEN_LINUX } from "./engines/hyperqwen/hyperqwenLinux.js";
22
+ import { HYPERQWEN_MACOS } from "./engines/hyperqwen/hyperqwenMacOS.js";
23
+ import { HYPERQWEN_WINDOWS } from "./engines/hyperqwen/hyperqwenWindows.js";
24
+ import { STRATA_LINUX } from "./engines/strata/strataLinux.js";
25
+ import { STRATA_MACOS } from "./engines/strata/strataMacOS.js";
26
+ import { STRATA_WINDOWS } from "./engines/strata/strataWindows.js";
27
+ import { BASALT_LINUX } from "./engines/basalt/basaltLinux.js";
28
+ import { BASALT_MACOS } from "./engines/basalt/basaltMacOS.js";
29
+ import { BASALT_WINDOWS } from "./engines/basalt/basaltWindows.js";
11
30
  /**
12
31
  * The remote name a user-provided fork is added under, so the checkout's own
13
32
  * origin (the upstream it was cloned from) survives the fork override.
14
33
  */
15
34
  export const FORK_REMOTE = 'dsh-fork';
35
+ /**
36
+ * The per-engine, per-OS build profiles, keyed by engine id then OS. The
37
+ * distro-specific Linux profiles (Arch, Debian, Fedora) reuse the shared Linux
38
+ * profile, so they are not separate keys here. A host OS with no dedicated
39
+ * profile (a Windows host for an engine that carries no Windows route) reads
40
+ * the shared Linux profile.
41
+ */
42
+ const ENGINE_OS_PROFILES = {
43
+ ninfer: { linux: NINFER_LINUX, macos: NINFER_MACOS, windows: NINFER_WINDOWS },
44
+ hyperqwen: { linux: HYPERQWEN_LINUX, macos: HYPERQWEN_MACOS, windows: HYPERQWEN_WINDOWS },
45
+ llamacpp: { linux: LLAMACPP_LINUX, macos: LLAMACPP_MACOS, windows: LLAMACPP_WINDOWS },
46
+ infernix: { linux: INFERNIX_LINUX, macos: INFERNIX_MACOS, windows: INFERNIX_WINDOWS },
47
+ strata: { linux: STRATA_LINUX, macos: STRATA_MACOS, windows: STRATA_WINDOWS },
48
+ basalt: { linux: BASALT_LINUX, macos: BASALT_MACOS, windows: BASALT_WINDOWS },
49
+ };
16
50
  /**
17
51
  * The per-engine git sources, keyed by engine id. NInfer and Infernix build
18
52
  * through cmake (Infernix through its own preset); llama.cpp builds through
19
53
  * the same cmake shape as NInfer; Hyper-Qwen is the compose-driven vLLM
20
54
  * engine, so it carries no local build steps (the compose up pulls the
21
- * image); Strata is the placeholder engine, and its repo and pull step land
22
- * with the engine, the same way its docker image constant did.
55
+ * image); Strata builds through its own setup.sh (the README's
56
+ * Build-from-source route), which the source-build mode runs in the checkout;
57
+ * Basalt builds through its own CMake route (the Ninja/MSVC variant on
58
+ * Windows). The build steps are read from the per-engine OS profiles, so a
59
+ * build command has one home (the OS-specific files under engines/).
23
60
  */
24
61
  export const ENGINE_SOURCES = {
25
62
  ninfer: {
26
63
  repo: 'https://github.com/Neroued/ninfer',
27
64
  branch: 'master',
28
- buildSteps: [
29
- 'cmake -S {dir} -B {dir}/build -G Ninja -DCMAKE_BUILD_TYPE=Release && cmake --build {dir}/build -j',
30
- ],
65
+ buildSteps: NINFER_LINUX.buildSteps,
66
+ // The Windows route is the ninfer-all fork's vcpkg build: the manifest
67
+ // pulls the dev libraries, then the engine's own build-native.bat
68
+ // configures and builds. The step cds into the checkout first (the {dir}
69
+ // placeholder), because vcpkg install and the relative script path both
70
+ // resolve against the checkout root.
71
+ windowsBuildSteps: NINFER_WINDOWS.buildSteps,
31
72
  },
32
73
  hyperqwen: {
33
74
  repo: 'https://github.com/vllm-project/vllm',
34
75
  branch: 'main',
35
- buildSteps: [],
76
+ buildSteps: HYPERQWEN_LINUX.buildSteps,
36
77
  },
37
78
  llamacpp: {
38
79
  repo: 'https://github.com/ggml-org/llama.cpp',
39
80
  branch: 'master',
40
- buildSteps: [
41
- 'cmake -S {dir} -B {dir}/build -G Ninja -DCMAKE_BUILD_TYPE=Release && cmake --build {dir}/build -j',
42
- ],
81
+ buildSteps: LLAMACPP_LINUX.buildSteps,
43
82
  },
44
83
  infernix: {
45
84
  repo: 'https://github.com/Wallawalla47/Infernix',
46
85
  branch: 'main',
47
- buildSteps: ['cd {dir} && cmake --preset release && cmake --build build -j'],
86
+ buildSteps: INFERNIX_LINUX.buildSteps,
48
87
  },
49
88
  strata: {
50
- repo: 'https://github.com/strata/strata',
89
+ // The README's Build-from-source route: git clone the repo, then run the
90
+ // engine's own setup.sh. `--yes` takes the recommended answer to every
91
+ // question (no prompts) and `--no-start` builds and installs the engine
92
+ // without starting the server, which the adapter's launch step then does.
93
+ repo: 'https://github.com/Niko1221/Strata',
51
94
  branch: 'main',
52
- buildSteps: ['docker pull ghcr.io/strata/strata'],
95
+ buildSteps: STRATA_LINUX.buildSteps,
96
+ },
97
+ basalt: {
98
+ // The engine's own CMake route: configure a Release build into the
99
+ // `build` directory, then compile it. The Windows variant adds the Ninja
100
+ // generator and runs in the MSVC developer shell (vcvars64.bat).
101
+ repo: 'https://github.com/jesdga95/basalt',
102
+ branch: 'main',
103
+ buildSteps: BASALT_LINUX.buildSteps,
104
+ windowsBuildSteps: BASALT_WINDOWS.buildSteps,
53
105
  },
54
106
  };
107
+ /**
108
+ * engineOsProfile reads the registered build profile for an engine id on an OS,
109
+ * or undefined when none is registered. A host OS with no dedicated profile
110
+ * (a Windows host for an engine that carries no Windows route) reads the
111
+ * shared Linux profile.
112
+ */
113
+ export function engineOsProfile(engineId, os = 'linux') {
114
+ const profiles = ENGINE_OS_PROFILES[engineId];
115
+ if (profiles === undefined)
116
+ return undefined;
117
+ return profiles[os] ?? profiles.linux;
118
+ }
119
+ /**
120
+ * engineBuildDir reads the directory, relative to the checkout, where an
121
+ * engine's build leaves its binary on an OS, so the binary symlink step reads
122
+ * the right tree. A Windows host reads the engine's Windows build tree (the
123
+ * vcpkg route for NInfer writes into build-windows\apps\Release); every other
124
+ * host reads the shared Linux build directory. An engine that installs its
125
+ * binary to the path (Strata) carries no build directory, so it reads as
126
+ * `build`, the shared default.
127
+ */
128
+ export function engineBuildDir(engineId, os = 'linux') {
129
+ return engineOsProfile(engineId, os)?.buildDir ?? 'build';
130
+ }
131
+ /**
132
+ * engineBinary reads the engine binary name on an OS (the .exe on Windows), so
133
+ * the adapters and the symlink step share one source. An engine with no local
134
+ * binary (the compose engines) reads as undefined.
135
+ */
136
+ export function engineBinary(engineId, os = 'linux') {
137
+ return engineOsProfile(engineId, os)?.binary;
138
+ }
55
139
  /**
56
140
  * engineSource reads the registered acquisition data for an engine id, or
57
141
  * undefined when none is registered.
@@ -60,17 +144,23 @@ export function engineSource(engineId) {
60
144
  return ENGINE_SOURCES[engineId];
61
145
  }
62
146
  /**
63
- * engineBuildSteps reads the build steps an engine runs in its checkout, so
64
- * the adapter's deploy step and the Get build step share one list.
147
+ * engineBuildSteps reads the build steps an engine runs in its checkout for an
148
+ * OS, so the adapter's deploy step and the Get build step share one list. The
149
+ * steps come from the engine's OS profile: a Windows host reads the engine's
150
+ * Windows profile (the vcpkg route for NInfer), and a host OS with no
151
+ * dedicated profile reads the shared Linux profile.
65
152
  */
66
- export function engineBuildSteps(engineId) {
67
- return ENGINE_SOURCES[engineId]?.buildSteps ?? [];
153
+ export function engineBuildSteps(engineId, os = 'linux') {
154
+ return engineOsProfile(engineId, os)?.buildSteps ?? [];
68
155
  }
69
156
  /**
70
157
  * runLogged runs one shell command through the host's streaming surface when
71
158
  * it has one (echoing the command and streaming its output through log), and
72
159
  * through plain run otherwise. The streamed copy is also collected, so a
73
- * caller can price the command's stdout (the dirty-check, for one).
160
+ * caller can price the command's stdout (the dirty-check, for one). A
161
+ * `shell` option (the engine OS profile's shell) runs the command in that
162
+ * shell instead of the host's default. A `needsSudo` option declares the
163
+ * command runs sudo, so the host applies the sudo-prompt rule to it.
74
164
  */
75
165
  async function runLogged(host, command, log, options) {
76
166
  let output = '';
@@ -79,13 +169,20 @@ async function runLogged(host, command, log, options) {
79
169
  log(chunk);
80
170
  };
81
171
  if (options?.echo !== false) {
82
- log(`$ ${command}\n`);
172
+ log(commandLogLine(command));
83
173
  }
174
+ // exactOptionalPropertyTypes: carry only the options the caller set, so an
175
+ // absent shell or sudo declaration never passes `{ shell: undefined }`
176
+ // through.
177
+ const runOptions = {
178
+ ...(options?.shell === undefined ? {} : { shell: options.shell }),
179
+ ...(options?.needsSudo === undefined ? {} : { needsSudo: options.needsSudo }),
180
+ };
84
181
  if (host.runStream !== undefined) {
85
- const outcome = await host.runStream(command, collect);
182
+ const outcome = await host.runStream(command, collect, runOptions);
86
183
  return { ...outcome, output };
87
184
  }
88
- const outcome = await host.run(command);
185
+ const outcome = await host.run(command, runOptions);
89
186
  return { ...outcome, output };
90
187
  }
91
188
  /**
@@ -94,7 +191,11 @@ async function runLogged(host, command, log, options) {
94
191
  * no fork is provided). An existing checkout with local changes aborts, so
95
192
  * the user's work is never rebased away; a clean checkout fetches the listed
96
193
  * fork (added as the named fork remote when provided, origin when not) and
97
- * rebases onto its branch. Every command echoes into log as it runs.
194
+ * rebases onto its branch. Every command echoes into log as it runs. The
195
+ * result's `cloned` flag names the path taken: set on a fresh clone (the
196
+ * checkout was absent), absent on the present-checkout path, so a caller
197
+ * prices a failure's fatality from the flag instead of re-probing the
198
+ * checkout.
98
199
  */
99
200
  export async function syncEngineRepo(host, request, log) {
100
201
  const source = engineSource(request.engineId);
@@ -106,7 +207,12 @@ export async function syncEngineRepo(host, request, log) {
106
207
  }
107
208
  const directory = request.directory;
108
209
  const fork = request.fork !== undefined && request.fork.trim() !== '' ? request.fork : undefined;
109
- const present = await host.run(`[ -d ${directory}/.git ]`);
210
+ // The presence probe is a git query, not a POSIX [ -d ] test: the shell the
211
+ // host runs is cmd on Windows, where [ is not a command and the test reads
212
+ // as a missing checkout every time. rev-parse exits 0 only inside a work
213
+ // tree, so a real checkout reads as present and an absent or non-git
214
+ // directory reads as missing, on every platform.
215
+ const present = await host.run(`git -C ${directory} rev-parse --is-inside-work-tree`);
110
216
  if (present.ok) {
111
217
  const status = await runLogged(host, `git -C ${directory} status --short`, log);
112
218
  if (!status.ok) {
@@ -145,9 +251,9 @@ export async function syncEngineRepo(host, request, log) {
145
251
  }
146
252
  const clone = await runLogged(host, `git clone ${fork ?? source.repo} ${directory}`, log);
147
253
  if (!clone.ok) {
148
- return { ok: false, detail: `git clone failed: ${clone.detail ?? 'unknown'}` };
254
+ return { ok: false, cloned: true, detail: `git clone failed: ${clone.detail ?? 'unknown'}` };
149
255
  }
150
- return { ok: true };
256
+ return { ok: true, cloned: true };
151
257
  }
152
258
  /**
153
259
  * syncAndRebuildEngine brings the engine checkout to the listed source and
@@ -181,7 +287,12 @@ export async function syncAndRebuildEngine(host, request, log) {
181
287
  chunks.push(chunk);
182
288
  log(chunk);
183
289
  };
184
- const present = await host.run(`[ -d ${directory}/.git ]`);
290
+ // The presence probe is a git query, not a POSIX [ -d ] test: the shell the
291
+ // host runs is cmd on Windows, where [ is not a command and the test reads
292
+ // as a missing checkout every time. rev-parse exits 0 only inside a work
293
+ // tree, so a real checkout reads as present and an absent or non-git
294
+ // directory reads as missing, on every platform.
295
+ const present = await host.run(`git -C ${directory} rev-parse --is-inside-work-tree`);
185
296
  let rebuilt = false;
186
297
  if (!present.ok) {
187
298
  // A missing checkout is a fresh clone of the fork (or the upstream repo),
@@ -201,10 +312,13 @@ export async function syncAndRebuildEngine(host, request, log) {
201
312
  else {
202
313
  if (fork !== undefined) {
203
314
  // Add the fork under its own remote name; a re-launch updates the URL in
204
- // place so an edited fork field takes effect on the next run.
205
- const add = await runLogged(host, `git -C ${directory} remote add ${FORK_REMOTE} ${fork}`, emit, { echo: false });
315
+ // place so an edited fork field takes effect on the next run. Both
316
+ // remote commands mutate the checkout's git config, so their `$ command`
317
+ // lines lead their output (the TASK-007 exec-audit rule): a re-launch
318
+ // reads the expected add failure and the set-url that follows.
319
+ const add = await runLogged(host, `git -C ${directory} remote add ${FORK_REMOTE} ${fork}`, emit);
206
320
  if (!add.ok) {
207
- await runLogged(host, `git -C ${directory} remote set-url ${FORK_REMOTE} ${fork}`, emit, { echo: false });
321
+ await runLogged(host, `git -C ${directory} remote set-url ${FORK_REMOTE} ${fork}`, emit);
208
322
  }
209
323
  }
210
324
  const status = await runLogged(host, `git -C ${directory} status --short`, emit, { echo: false });
@@ -221,7 +335,10 @@ export async function syncAndRebuildEngine(host, request, log) {
221
335
  return { ok: true, rebuilt: false, logs: chunks.join('') };
222
336
  }
223
337
  // The fetch is best-effort: an offline host prices the checkout it has.
224
- await runLogged(host, `git -C ${directory} fetch ${remote} || true`, emit, { echo: false });
338
+ // It mutates the checkout's objects and refs, so the `$ command` line
339
+ // leads its output (the TASK-007 exec-audit rule); the `|| true` keeps a
340
+ // failed fetch silent so the behind-check prices what is already present.
341
+ await runLogged(host, `git -C ${directory} fetch ${remote} || true`, emit);
225
342
  // Price whether the current branch is behind the remote branch. A fetch
226
343
  // that failed (offline) or a missing remote branch reads as not-behind, so
227
344
  // the checkout builds as-is.
@@ -264,10 +381,26 @@ export async function syncAndRebuildEngine(host, request, log) {
264
381
  }
265
382
  }
266
383
  if (rebuilt) {
267
- // Run the engine's build steps, capturing the command and its output.
268
- for (const step of source.buildSteps) {
384
+ // The user's local build command (the card's Build commands field)
385
+ // overrides the engine's default build steps: it runs in the checkout, so
386
+ // it is wrapped with a cd into the directory. Absent falls back to the
387
+ // engine's acquisition build steps.
388
+ const buildCommands = request.buildCommands !== undefined && request.buildCommands.trim() !== ''
389
+ ? request.buildCommands
390
+ : undefined;
391
+ const steps = buildCommands !== undefined
392
+ ? [`cd ${directory} && ${buildCommands}`]
393
+ : engineBuildSteps(request.engineId, request.os ?? 'linux');
394
+ // The engine OS profile's shell (the Windows vcpkg route's pwsh.exe)
395
+ // runs the build steps; absent keeps the host's default shell.
396
+ const shell = engineOsProfile(request.engineId, request.os ?? 'linux')?.shell;
397
+ const stepOptions = shell !== undefined ? { shell } : undefined;
398
+ // Report the build phase before the first step runs, so the card's button
399
+ // reads "Building..." while the build is in flight, not after it completes.
400
+ request.onBuildStart?.();
401
+ for (const step of steps) {
269
402
  const command = step.replaceAll('{dir}', directory);
270
- const outcome = await runLogged(host, command, emit);
403
+ const outcome = await runLogged(host, command, emit, stepOptions);
271
404
  if (!outcome.ok) {
272
405
  return {
273
406
  ok: false,
@@ -279,13 +412,24 @@ export async function syncAndRebuildEngine(host, request, log) {
279
412
  }
280
413
  // A rebuild that did not install the binary (no make install) leaves it
281
414
  // out of the path. Check, and add a symlink when the trust gate allows.
282
- const binary = request.binary;
415
+ // The symlink name is the profile's bare binary (the .exe on Windows), not
416
+ // the launch path: a Windows launch path carries its build tree
417
+ // (build-windows\apps\Release\ninfer-serve.exe), so linking it under
418
+ // buildDir would double the tree.
419
+ const binary = engineBinary(request.engineId, request.os ?? 'linux');
283
420
  if (binary !== undefined) {
284
421
  const which = await host.run(`command -v ${binary}`);
285
422
  if (!which.ok) {
286
- const link = `sudo ln -sf ${directory}/build/${binary} /usr/local/bin/${binary}`;
423
+ // The build directory is OS-specific: a Windows host reads the
424
+ // engine's Windows build tree (the vcpkg route for NInfer writes into
425
+ // build-windows\apps\Release), every other host the shared Linux build
426
+ // directory.
427
+ const buildDir = engineBuildDir(request.engineId, request.os ?? 'linux');
428
+ const link = `sudo ln -sf ${directory}/${buildDir}/${binary} /usr/local/bin/${binary}`;
287
429
  if (request.allowRunDeps === true) {
288
- const result = await runLogged(host, link, emit);
430
+ // The hand-written sudo line declares its need at this call site;
431
+ // the host never parses the command text to find out.
432
+ const result = await runLogged(host, link, emit, { needsSudo: true });
289
433
  if (!result.ok) {
290
434
  emit(`!! ${link} failed: ${result.detail ?? 'unknown'}\n`);
291
435
  }
@@ -298,28 +442,83 @@ export async function syncAndRebuildEngine(host, request, log) {
298
442
  }
299
443
  return { ok: true, rebuilt, logs: chunks.join('') };
300
444
  }
445
+ /**
446
+ * syncEngineForDeploy runs the deploy step's git sync (the shared
447
+ * syncAndRebuildEngine) with the option shape every adapter's deploy step
448
+ * assembles: the platform entry's fork and build-command override, the
449
+ * Play/Run trust gate, the plan's host OS (the Windows vcpkg route when the
450
+ * user carries no build-command override), and the progress sink the card's
451
+ * button reads (the git sync reports "Downloading...", the build step
452
+ * reports "Building..." the moment it starts). The captured build output is
453
+ * returned as buildLogs, so a caller threads it to the card's Logs section
454
+ * without re-running the steps.
455
+ */
456
+ export async function syncEngineForDeploy(host, request) {
457
+ // The git sync is the checkout download: the card's button reads
458
+ // "Downloading..." for it, not "Building...".
459
+ request.onProgress?.('download');
460
+ const sync = await syncAndRebuildEngine(host, {
461
+ engineId: request.engineId,
462
+ directory: request.directory,
463
+ ...(request.platform?.engineFork !== undefined
464
+ ? { fork: request.platform.engineFork }
465
+ : {}),
466
+ ...(request.options?.allowRunDeps !== undefined
467
+ ? { allowRunDeps: request.options.allowRunDeps }
468
+ : {}),
469
+ ...(request.platform?.buildCommands !== undefined
470
+ ? { buildCommands: request.platform.buildCommands }
471
+ : {}),
472
+ ...(request.os !== undefined ? { os: request.os } : {}),
473
+ // Report the build phase the moment the build steps start, so the
474
+ // card's button reads "Building..." while the build is in flight.
475
+ onBuildStart: () => request.onProgress?.('build'),
476
+ }, () => { });
477
+ // The build output reads as a fresh Logs section: a "Building..."
478
+ // separator leads the rebuild decision and output, the same line the card
479
+ // writes when the build step event lands.
480
+ let buildLogs = sync.logs;
481
+ if (sync.rebuilt && buildLogs !== '') {
482
+ buildLogs = `--- Building...\n${buildLogs}`;
483
+ }
484
+ return {
485
+ ok: sync.ok,
486
+ ...(sync.detail !== undefined ? { detail: sync.detail } : {}),
487
+ buildLogs,
488
+ };
489
+ }
301
490
  /**
302
491
  * buildEngineRepo runs the engine's build steps in the checkout, streaming
303
492
  * each step's output through log. An engine with no build steps (the compose
304
493
  * engines) passes through with a note; a failing step stops the build with
305
494
  * the step's detail.
306
495
  */
307
- export async function buildEngineRepo(host, engineId, directory, log) {
496
+ export async function buildEngineRepo(host, engineId, directory, log, buildCommands, os = 'linux') {
308
497
  const source = engineSource(engineId);
309
498
  if (source === undefined) {
310
499
  return { ok: false, detail: `no engine source is registered for "${engineId}"` };
311
500
  }
312
- if (source.buildSteps.length === 0) {
501
+ // The user's local build command (the card's Build commands field) overrides
502
+ // the engine's default build steps: it runs in the checkout, so it is wrapped
503
+ // with a cd into the directory. Absent falls back to the engine's steps for
504
+ // the host OS (the Windows vcpkg route on a Windows host).
505
+ const commands = buildCommands !== undefined && buildCommands.trim() !== '' ? buildCommands : undefined;
506
+ const steps = commands !== undefined ? [`cd ${directory} && ${commands}`] : engineBuildSteps(engineId, os);
507
+ if (steps.length === 0) {
313
508
  log(`no local build steps for ${engineId}\n`);
314
509
  return { ok: true };
315
510
  }
316
- for (const step of source.buildSteps) {
511
+ // The engine OS profile's shell (the Windows vcpkg route's pwsh.exe)
512
+ // runs the build steps; absent keeps the host's default shell.
513
+ const shell = engineOsProfile(engineId, os)?.shell;
514
+ const stepOptions = shell !== undefined ? { shell } : undefined;
515
+ for (const step of steps) {
317
516
  const command = step.replaceAll('{dir}', directory);
318
- const outcome = await runLogged(host, command, log);
517
+ const outcome = await runLogged(host, command, log, stepOptions);
319
518
  if (!outcome.ok) {
320
519
  return { ok: false, detail: `build failed: ${outcome.detail ?? 'unknown'}` };
321
520
  }
322
521
  }
323
522
  return { ok: true };
324
523
  }
325
- //# sourceMappingURL=acquire.js.map
524
+ //# sourceMappingURL=acquire.js.map