dsh-plugin-local-infer 0.1.1 → 0.1.3

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 (108) hide show
  1. package/README.md +14 -3
  2. package/docs/zh/README.md +14 -3
  3. package/dsh/client.js +1214 -242
  4. package/package.json +16 -10
  5. package/packages/core/lib/adapter.d.ts +119 -6
  6. package/packages/core/lib/deps.d.ts +4 -3
  7. package/packages/core/lib/index.d.ts +2 -1
  8. package/packages/core/lib/index.js +1 -0
  9. package/packages/core/lib/launch-command.d.ts +13 -0
  10. package/packages/core/lib/launch-command.js +64 -0
  11. package/packages/core/lib/models.d.ts +2 -1
  12. package/packages/core/lib/models.js +10 -1
  13. package/packages/core/lib/registrar.d.ts +5 -5
  14. package/packages/core/lib/registrar.js +3 -3
  15. package/packages/core/lib/status.d.ts +9 -1
  16. package/packages/core/lib/status.js +6 -1
  17. package/packages/core/package.json +1 -1
  18. package/packages/providers/lib/acquire.d.ts +127 -0
  19. package/packages/providers/lib/acquire.js +346 -0
  20. package/packages/providers/lib/adapters.d.ts +19 -172
  21. package/packages/providers/lib/adapters.js +114 -659
  22. package/packages/providers/lib/deps.d.ts +26 -2
  23. package/packages/providers/lib/deps.js +37 -4
  24. package/packages/providers/lib/engines/hyperqwenAdapter.d.ts +38 -0
  25. package/packages/providers/lib/engines/hyperqwenAdapter.js +105 -0
  26. package/packages/providers/lib/engines/infernix.d.ts +170 -0
  27. package/packages/providers/lib/engines/infernix.js +671 -0
  28. package/packages/providers/lib/engines/infernixAdapter.d.ts +53 -0
  29. package/packages/providers/lib/engines/infernixAdapter.js +202 -0
  30. package/packages/providers/lib/engines/llamacppAdapter.d.ts +51 -0
  31. package/packages/providers/lib/engines/llamacppAdapter.js +232 -0
  32. package/packages/providers/lib/{ninfer.d.ts → engines/ninfer.d.ts} +7 -0
  33. package/packages/providers/lib/{ninfer.js → engines/ninfer.js} +8 -1
  34. package/packages/providers/lib/engines/ninferAdapter.d.ts +82 -0
  35. package/packages/providers/lib/engines/ninferAdapter.js +324 -0
  36. package/packages/providers/lib/engines/strata.d.ts +53 -0
  37. package/packages/providers/lib/engines/strata.js +197 -0
  38. package/packages/providers/lib/engines/strataAdapter.d.ts +98 -0
  39. package/packages/providers/lib/engines/strataAdapter.js +427 -0
  40. package/packages/providers/lib/engines.d.ts +5 -3
  41. package/packages/providers/lib/engines.js +16 -8
  42. package/packages/providers/lib/guidelines.js +16 -0
  43. package/packages/providers/lib/index.d.ts +21 -10
  44. package/packages/providers/lib/index.js +23 -9
  45. package/packages/providers/lib/model-defaults.d.ts +8 -5
  46. package/packages/providers/lib/model-defaults.js +47 -5
  47. package/packages/providers/lib/platform.d.ts +31 -0
  48. package/packages/providers/lib/platform.js +64 -0
  49. package/packages/providers/package.json +1 -1
  50. package/packages/service/lib/index.d.ts +74 -22
  51. package/packages/service/lib/index.js +354 -44
  52. package/packages/service/lib/types.d.ts +48 -5
  53. package/packages/service/package.json +1 -1
  54. package/packages/tool/package.json +1 -1
  55. package/packages/ui/lib/config.d.ts +79 -0
  56. package/packages/ui/lib/config.js +235 -0
  57. package/packages/ui/lib/deps.d.ts +3 -3
  58. package/packages/ui/lib/ops/engine.d.ts +108 -0
  59. package/packages/ui/lib/ops/engine.js +493 -0
  60. package/packages/ui/lib/ops/hf.d.ts +27 -0
  61. package/packages/ui/lib/ops/hf.js +60 -0
  62. package/packages/ui/lib/ops/logs.d.ts +23 -0
  63. package/packages/ui/lib/ops/logs.js +58 -0
  64. package/packages/ui/lib/ops/model.d.ts +79 -0
  65. package/packages/ui/lib/ops/model.js +130 -0
  66. package/packages/ui/lib/request.d.ts +106 -0
  67. package/packages/ui/lib/request.js +80 -0
  68. package/packages/ui/lib/route.d.ts +96 -357
  69. package/packages/ui/lib/route.js +123 -1415
  70. package/packages/ui/lib/routes/chat-template.d.ts +14 -0
  71. package/packages/ui/lib/routes/chat-template.js +37 -0
  72. package/packages/ui/lib/routes/deps.d.ts +16 -0
  73. package/packages/ui/lib/routes/deps.js +27 -0
  74. package/packages/ui/lib/routes/download.d.ts +16 -0
  75. package/packages/ui/lib/routes/download.js +48 -0
  76. package/packages/ui/lib/routes/get.d.ts +18 -0
  77. package/packages/ui/lib/routes/get.js +57 -0
  78. package/packages/ui/lib/routes/install.d.ts +16 -0
  79. package/packages/ui/lib/routes/install.js +36 -0
  80. package/packages/ui/lib/routes/launch.d.ts +18 -0
  81. package/packages/ui/lib/routes/launch.js +50 -0
  82. package/packages/ui/lib/routes/provider-delete.d.ts +15 -0
  83. package/packages/ui/lib/routes/provider-delete.js +37 -0
  84. package/packages/ui/lib/routes/register.d.ts +16 -0
  85. package/packages/ui/lib/routes/register.js +28 -0
  86. package/packages/ui/lib/routes/save.d.ts +18 -0
  87. package/packages/ui/lib/routes/save.js +139 -0
  88. package/packages/ui/lib/routes/stop.d.ts +16 -0
  89. package/packages/ui/lib/routes/stop.js +29 -0
  90. package/packages/ui/lib/routes/summary.d.ts +15 -0
  91. package/packages/ui/lib/routes/summary.js +26 -0
  92. package/packages/ui/lib/routes/unregister.d.ts +14 -0
  93. package/packages/ui/lib/routes/unregister.js +26 -0
  94. package/packages/ui/lib/viewmodels/deps.d.ts +31 -0
  95. package/packages/ui/lib/viewmodels/deps.js +89 -0
  96. package/packages/ui/lib/viewmodels/depsHelpers.d.ts +28 -0
  97. package/packages/ui/lib/viewmodels/depsHelpers.js +33 -0
  98. package/packages/ui/lib/viewmodels/summary.d.ts +140 -0
  99. package/packages/ui/lib/viewmodels/summary.js +225 -0
  100. package/packages/ui/lib/viewmodels/summaryHelpers.d.ts +30 -0
  101. package/packages/ui/lib/viewmodels/summaryHelpers.js +42 -0
  102. package/packages/ui/package.json +1 -1
  103. package/packages/providers/lib/strata.d.ts +0 -15
  104. package/packages/providers/lib/strata.js +0 -14
  105. /package/packages/providers/lib/{hyperqwen.d.ts → engines/hyperqwen.d.ts} +0 -0
  106. /package/packages/providers/lib/{hyperqwen.js → engines/hyperqwen.js} +0 -0
  107. /package/packages/providers/lib/{llamacpp.d.ts → engines/llamacpp.d.ts} +0 -0
  108. /package/packages/providers/lib/{llamacpp.js → engines/llamacpp.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-local-infer",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,7 +21,12 @@
21
21
  "ai-agent"
22
22
  ],
23
23
  "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/cybtachyon/dsh-plugin-local-infer"
27
+ },
24
28
  "type": "module",
29
+ "packageManager": "pnpm@12.11.2",
25
30
  "engines": {
26
31
  "node": "^22.19.0 || >=24.0.0"
27
32
  },
@@ -85,22 +90,23 @@
85
90
  "peerDependencies": {
86
91
  "@deepseek-ai/cordis": ">=4.0.5-alpha.2"
87
92
  },
93
+ "scripts": {
94
+ "build": "tsc -b",
95
+ "typecheck": "tsc -b",
96
+ "test": "vitest run",
97
+ "test:coverage": "vitest run --coverage",
98
+ "lint": "eslint .",
99
+ "clean": "tsc -b --clean"
100
+ },
88
101
  "devDependencies": {
89
102
  "@eslint/js": "^10.0.1",
90
103
  "@types/node": "^22.10.0",
91
104
  "@vitest/coverage-v8": "^5.0.4",
92
105
  "eslint": "^10.12.0",
106
+ "tsx": "^4.23.15",
93
107
  "typescript": "^5.9.0",
94
108
  "typescript-eslint": "^8.71.2",
95
109
  "vite-tsconfig-paths": "^5.1.4",
96
110
  "vitest": "^5.0.4"
97
- },
98
- "scripts": {
99
- "build": "tsc -b",
100
- "typecheck": "tsc -b",
101
- "test": "vitest run",
102
- "test:coverage": "vitest run --coverage",
103
- "lint": "eslint .",
104
- "clean": "tsc -b --clean"
105
111
  }
106
- }
112
+ }
@@ -5,6 +5,13 @@ import type { HardwareProfile } from './hardware.ts';
5
5
  import type { ParameterSpec } from './parameter.ts';
6
6
  import type { SystemDep } from './deps.ts';
7
7
  import type { EngineLaunchSignature } from './reserve.ts';
8
+ /**
9
+ * BuildMode is the deploy mode an engine renders a plan for: a docker
10
+ * container, a local source build, or a prebuilt binary install (the Strata
11
+ * Terminal the install script places on the path). The adapter reads it from
12
+ * the config `buildMode` field and renders the matching plan.
13
+ */
14
+ export type BuildMode = 'docker' | 'local' | 'binary';
8
15
  /**
9
16
  * LaunchPlan is the engine-neutral result of rendering a config: how to start
10
17
  * the engine (a local process, a docker container, or a compose file), the
@@ -21,6 +28,7 @@ export interface LaunchPlan {
21
28
  env?: Record<string, string>;
22
29
  composeFile?: string;
23
30
  profile?: string;
31
+ composeArgs?: string;
24
32
  containerName?: string;
25
33
  modelId?: string;
26
34
  modelDirectory?: string;
@@ -32,12 +40,31 @@ export interface LaunchPlan {
32
40
  * specific engine: where the engine platform lives locally (the checkout and
33
41
  * build location) and the git repo that overrides the engine's default
34
42
  * upstream for this config. The deploy step consumes both: it clones the fork
35
- * (or the default upstream) into the engine directory.
43
+ * (or the default upstream) into the engine directory. `buildCommands`
44
+ * overrides the engine's local build command (the local build mode) and
45
+ * `dockerTag` overrides the docker image tag the docker deploy pulls (the
46
+ * docker build mode); each rides the platform map, so an engine switch keeps
47
+ * them.
36
48
  */
37
49
  export interface EnginePlatformConfig {
38
50
  engineId: string;
39
51
  engineDirectory?: string;
40
52
  engineFork?: string;
53
+ buildCommands?: string;
54
+ dockerTag?: string;
55
+ }
56
+ /**
57
+ * ProcessExitInfo is the one-shot answer to a spawned process's lifetime end:
58
+ * the exit code (null when a signal or a spawn failure ended it), the signal
59
+ * name (null when the process exited on its own), and the spawn error message
60
+ * when the child never ran (a missing binary). The host emits it exactly once
61
+ * per child, from the close event or the error event, so a crash or a pkill
62
+ * reaches the plugin's state through one seam instead of an uncaughtException.
63
+ */
64
+ export interface ProcessExitInfo {
65
+ code: number | null;
66
+ signal: string | null;
67
+ error?: string;
41
68
  }
42
69
  /**
43
70
  * ProcessHandle is the handle to a spawned engine process: its pid and a kill
@@ -46,12 +73,17 @@ export interface EnginePlatformConfig {
46
73
  * the host captures the child's stdout and stderr, is the accumulated output
47
74
  * the launch step surfaces in the card's Logs section and writes to the log
48
75
  * file. A host that does not capture streams (a container it cannot read)
49
- * omits it.
76
+ * omits it. `onExit`, when the host watches the child, registers a listener
77
+ * fired once with the ProcessExitInfo when the child ends (a crash, a signal
78
+ * like a pkill, or a clean stop); a listener registered after the child has
79
+ * already ended fires immediately. A host that cannot watch (a stub, or a
80
+ * container it cannot read) omits it.
50
81
  */
51
82
  export interface ProcessHandle {
52
83
  pid: number;
53
84
  kill(): void;
54
85
  logs?: string;
86
+ onExit?(listener: (exit: ProcessExitInfo) => void): void;
55
87
  }
56
88
  /**
57
89
  * FetchResult is the answer to a health poll: the HTTP status and whether it
@@ -71,6 +103,15 @@ export interface FetchResult {
71
103
  */
72
104
  export interface LifecycleHost {
73
105
  run(command: string): Promise<CommandOutcome>;
106
+ /**
107
+ * Optional: run a command line and stream each stdout/stderr chunk to
108
+ * onOutput while it runs, so the Get step can append build output to the
109
+ * card's Logs section as it lands. A host that cannot stream omits it,
110
+ * and the caller falls back to run. No overall timeout: a full CUDA
111
+ * build legitimately runs past any fixed cap, and the card prices a
112
+ * stalled step with its own output-idle watchdog.
113
+ */
114
+ runStream?(command: string, onOutput: (chunk: string) => void): Promise<CommandOutcome>;
74
115
  spawn(argv: readonly string[], env: Record<string, string>, cwd?: string): ProcessHandle;
75
116
  fetch(url: string): Promise<FetchResult>;
76
117
  debug?: DebugLogger;
@@ -84,26 +125,75 @@ export interface HealthCheckOptions {
84
125
  pollMs?: number;
85
126
  maxAttempts?: number;
86
127
  }
128
+ /**
129
+ * LaunchStep is the phase of a launch the card's Launch control prices: the
130
+ * system-dependency install (prereqs), the model and git checkout download
131
+ * (download), the source build or image pull (build), and the health poll that
132
+ * waits for the engine to come ready (launch). The service and the adapters
133
+ * report the step as each phase begins, so the card's button reads the phase
134
+ * that is actually running instead of a single "Launching..." for the whole
135
+ * rebuild. The card times out only the launch step; the rebuild steps (prereqs,
136
+ * download, build) run as long as they need to.
137
+ */
138
+ export type LaunchStep = 'prereqs' | 'download' | 'build' | 'launch';
139
+ /**
140
+ * LaunchProgress is the callback the launch flow calls as each phase begins.
141
+ * The route threads it from the streamed launch response through the service
142
+ * and the adapter's deploy step; a launch with no progress sink omits it.
143
+ */
144
+ export type LaunchProgress = (step: LaunchStep) => void;
145
+ /**
146
+ * EngineLaunchState is the phase of an engine start the health poll reports:
147
+ * downloading (the engine is fetching its engine or model files, the health
148
+ * endpoint is not up yet), starting (the files are in place and the engine is
149
+ * coming up), ready (the health endpoint reports 200), and failed (the start
150
+ * did not reach readiness). An engine that downloads before it can serve (the
151
+ * Strata Terminal) spends a long time in downloading, so the poll reports the
152
+ * phase instead of timing out and reading the start as broken.
153
+ */
154
+ export type EngineLaunchState = 'downloading' | 'starting' | 'ready' | 'failed';
87
155
  /**
88
156
  * HealthResult is the answer to a /health poll: whether the engine is up, the
89
- * HTTP status, and a detail string for the UI.
157
+ * HTTP status, a detail string for the UI, and the start phase the poll
158
+ * observed (absent for an engine whose start has no download phase).
90
159
  */
91
160
  export interface HealthResult {
92
161
  healthy: boolean;
93
162
  status?: number;
94
163
  detail?: string;
164
+ state?: EngineLaunchState;
165
+ }
166
+ /**
167
+ * DeployOptions tunes a deploy run: `allowRunDeps` is the Play/Run trust gate
168
+ * (the card's "Allow one-click install (sudo)" checkbox). A local deploy that
169
+ * rebuilds the engine and leaves its binary out of the path links it into
170
+ * /usr/local/bin; the gate decides whether that sudo link runs (on) or is
171
+ * written to the card's Logs section for the user to run (off). `autoRebuild`
172
+ * is the card's "Auto Rebuild" checkbox: when false, the git sync (fetch,
173
+ * rebase, and the conditional rebuild) is skipped and the checkout launches
174
+ * as-is. Absent reads as true (the rebuild runs).
175
+ */
176
+ export interface DeployOptions {
177
+ allowRunDeps?: boolean;
178
+ autoRebuild?: boolean;
95
179
  }
96
180
  /**
97
181
  * DeployResult reports whether a build-or-pull succeeded, with a detail for the
98
182
  * UI. `handle` is the spawned engine process when the deploy step started one
99
183
  * locally: the service reads its accumulated `logs` after the health poll, so
100
184
  * the launch result carries the engine's output for the card's Logs section and
101
- * the log file. A deploy that only pulls an image omits it.
185
+ * the log file, and attaches the handle's `onExit` listener to learn of a crash
186
+ * or a pkill. A deploy that starts no process (a compose up) omits it. `logs`
187
+ * is the deploy step's own captured output (the rebuild decision, the build
188
+ * command, and its output) the service prepends to the engine's output, so the
189
+ * card's Logs section shows the build a launch ran. A deploy that runs no
190
+ * rebuild (a docker pull, a compose up) omits it.
102
191
  */
103
192
  export interface DeployResult {
104
193
  ok: boolean;
105
194
  detail?: string;
106
195
  handle?: ProcessHandle;
196
+ logs?: string;
107
197
  }
108
198
  /**
109
199
  * EngineAdapter is the abstract base one subclass implements per engine. It
@@ -119,11 +209,12 @@ export declare abstract class EngineAdapter {
119
209
  abstract readonly supports: {
120
210
  docker: boolean;
121
211
  local: boolean;
212
+ binary?: boolean;
122
213
  };
123
214
  abstract parameters(): readonly ParameterSpec[];
124
215
  abstract renderLaunch(config: Config, hw: HardwareProfile): LaunchPlan;
125
216
  abstract healthCheck(plan: LaunchPlan, options?: HealthCheckOptions): Promise<HealthResult>;
126
- abstract deploy(plan: LaunchPlan, platform?: EnginePlatformConfig): Promise<DeployResult>;
217
+ abstract deploy(plan: LaunchPlan, platform?: EnginePlatformConfig, onProgress?: LaunchProgress, options?: DeployOptions): Promise<DeployResult>;
127
218
  abstract stop(plan: LaunchPlan): Promise<void>;
128
219
  /**
129
220
  * Optional: the host deps this engine's deploy step needs for a build mode,
@@ -131,7 +222,7 @@ export declare abstract class EngineAdapter {
131
222
  * registered adapter first and falls back to the provider-registered plan
132
223
  * for the engine id when the adapter declares none.
133
224
  */
134
- systemDeps?(buildMode: 'docker' | 'local', hw: HardwareProfile): readonly SystemDep[];
225
+ systemDeps?(buildMode: BuildMode, hw: HardwareProfile): readonly SystemDep[];
135
226
  /**
136
227
  * Optional: the model the engine serves under a config: the id the engine
137
228
  * advertises on its OpenAI endpoint and the display name the registrar
@@ -143,6 +234,28 @@ export declare abstract class EngineAdapter {
143
234
  id: string;
144
235
  name: string;
145
236
  } | undefined;
237
+ /**
238
+ * Optional: the model the running engine actually serves, read live from the
239
+ * engine (a terminal query against its model endpoint) rather than derived
240
+ * from the config. An engine that sizes and picks the model itself publishes
241
+ * the served id this returns instead of the config-derived modelRef; it
242
+ * returns undefined when the engine is not up, the answer cannot be read, or
243
+ * the config names a model, and the route falls back to modelRef. The
244
+ * register, save-sync, unregister, and summary paths await it, so it must
245
+ * never reject.
246
+ */
247
+ discoverModel?(config: Config): Promise<{
248
+ id: string;
249
+ name: string;
250
+ } | undefined>;
251
+ /**
252
+ * Optional: the request modalities the engine serves under a config, the
253
+ * input types the DSH model row carries. An engine whose vision control is
254
+ * not the plain boolean flag (Strata's on|off|gpu|cpu enum, on by default)
255
+ * declares this so the Image input follows the engine's own vocabulary; an
256
+ * engine that declares none keeps the register step's boolean rule.
257
+ */
258
+ modelInput?(config: Config): string[] | undefined;
146
259
  /**
147
260
  * Optional: the launch signature the engine's toArgv/toEnv produce for a
148
261
  * config: the binary (or docker image) it runs under, the port it binds,
@@ -1,4 +1,4 @@
1
- import type { EngineAdapter } from './adapter.ts';
1
+ import type { BuildMode, EngineAdapter } from './adapter.ts';
2
2
  import type { HardwareProfile } from './hardware.ts';
3
3
  /**
4
4
  * DepStatus is the lifecycle state of one host dependency: satisfied, missing,
@@ -60,6 +60,7 @@ export interface SystemDep {
60
60
  export interface EngineDepPlan {
61
61
  docker: readonly SystemDep[];
62
62
  local: readonly SystemDep[];
63
+ binary?: readonly SystemDep[];
63
64
  }
64
65
  /**
65
66
  * DepCheck is the per-dep answer from checkDeps: the dep and its current
@@ -103,7 +104,7 @@ export interface InstallResult {
103
104
  * service implements this interface over the pure functions below.
104
105
  */
105
106
  export interface SystemDepProvisioner {
106
- planDeps(engine: EngineAdapter, buildMode: 'docker' | 'local', hw: HardwareProfile): readonly SystemDep[];
107
+ planDeps(engine: EngineAdapter, buildMode: BuildMode, hw: HardwareProfile): readonly SystemDep[];
107
108
  checkDeps(deps: readonly SystemDep[], hw: HardwareProfile): Promise<DepCheck[]>;
108
109
  renderInstallString(deps: readonly SystemDep[], hw: HardwareProfile): string;
109
110
  runInstall(deps: readonly SystemDep[], hw: HardwareProfile, runCommand: CommandRunner): Promise<InstallResult>;
@@ -122,7 +123,7 @@ export declare function detectPackageManager(probes: PackageManagerProbes): Pack
122
123
  * declares none (the Phase 5 adapters will) plans an empty list, and the
123
124
  * service falls back to the provider-registered plan for the engine id.
124
125
  */
125
- export declare function planDeps(engine: EngineAdapter, buildMode: 'docker' | 'local', hw: HardwareProfile): readonly SystemDep[];
126
+ export declare function planDeps(engine: EngineAdapter, buildMode: BuildMode, hw: HardwareProfile): readonly SystemDep[];
126
127
  /**
127
128
  * checkDeps probes every dep and reports its status. A dep without a detect
128
129
  * probe reads as missing, not satisfied: an unknown state degrades to the
@@ -13,13 +13,14 @@ export { DEFAULT_OS_RESERVE_MIB, estimateVram, KV_PAGE_TOKENS, pageRoundUp, TIGH
13
13
  export type { EstimateContext, EstimateOptions, SpeculationTerms, VramBreakdown, VramContribution, VramEstimate, VramModel, VramVerdict, } from './estimator.ts';
14
14
  export { GPU_USAGE_BUSY_THRESHOLD, matchLaunchSignature, parseNvidiaSmiGpuUsage, resolveOsReserve, } from './reserve.ts';
15
15
  export { commandMatchesArgv, launchRecordFromPlan, reLinkRecord, } from './launch-record.ts';
16
+ export { formatLaunchCommand } from './launch-command.ts';
16
17
  export type { ProviderLaunchRecord, ReLinkInput, ReLinkOutcome, } from './launch-record.ts';
17
18
  export type { EngineLaunchSignature, GpuUsageProbe, ProcessLine, ReserveResolution, ReserveSource, } from './reserve.ts';
18
19
  export { computeProviderStatuses, isGpuBusy, } from './status.ts';
19
20
  export type { ProviderRef, ProviderStatus, ProviderStatusInput, RunningEngineRef, } from './status.ts';
20
21
  export { DEFAULT_LOG_PATH_TEMPLATE, LOG_FILE_NAME_TEMPLATE, LOG_TEMPLATE_ENGINE, LOG_TEMPLATE_HOME, LOG_TEMPLATE_MODEL, LOG_TEMPLATE_TIMESTAMP, formatLogTimestamp, resolveLogPath, slugifyModelName, } from './logs.ts';
21
22
  export { EngineAdapter } from './adapter.ts';
22
- export type { DeployResult, EnginePlatformConfig, FetchResult, HealthCheckOptions, HealthResult, LaunchPlan, LifecycleHost, ProcessHandle, } from './adapter.ts';
23
+ export type { BuildMode, DeployOptions, DeployResult, EngineLaunchState, EnginePlatformConfig, FetchResult, HealthCheckOptions, HealthResult, LaunchPlan, LaunchProgress, LaunchStep, LifecycleHost, ProcessExitInfo, ProcessHandle, } from './adapter.ts';
23
24
  export { NOOP_DEBUG_LOGGER, createCommandDebugLogger, isHarnessLogDebug, } from './debug.ts';
24
25
  export type { CreateCommandDebugLoggerArgs, DebugLogger, } from './debug.ts';
25
26
  export { constraintId, evaluateGuidelines, modelGuidelines } from './guideline.ts';
@@ -9,6 +9,7 @@ export { detectHardware, parseNvidiaSmiGpu, } from "./hardware.js";
9
9
  export { DEFAULT_OS_RESERVE_MIB, estimateVram, KV_PAGE_TOKENS, pageRoundUp, TIGHT_HEADROOM_BYTES, } from "./estimator.js";
10
10
  export { GPU_USAGE_BUSY_THRESHOLD, matchLaunchSignature, parseNvidiaSmiGpuUsage, resolveOsReserve, } from "./reserve.js";
11
11
  export { commandMatchesArgv, launchRecordFromPlan, reLinkRecord, } from "./launch-record.js";
12
+ export { formatLaunchCommand } from "./launch-command.js";
12
13
  export { computeProviderStatuses, isGpuBusy, } from "./status.js";
13
14
  export { DEFAULT_LOG_PATH_TEMPLATE, LOG_FILE_NAME_TEMPLATE, LOG_TEMPLATE_ENGINE, LOG_TEMPLATE_HOME, LOG_TEMPLATE_MODEL, LOG_TEMPLATE_TIMESTAMP, formatLogTimestamp, resolveLogPath, slugifyModelName, } from "./logs.js";
14
15
  export { EngineAdapter } from "./adapter.js";
@@ -0,0 +1,13 @@
1
+ import type { LaunchPlan } from './adapter.ts';
2
+ /**
3
+ * formatLaunchCommand renders a launch plan as the shell command string the
4
+ * deploy step runs to start the engine: the quoted argv joined for a process
5
+ * or docker plan, or the `docker compose up` line for a compose plan. The
6
+ * card's Logs section shows the line above the engine's own output, so a
7
+ * user can read and copy the exact binary and arguments a launch started
8
+ * with. The Hyper-Qwen deploy runs this same string, so the printed line and
9
+ * the host command cannot drift. Returns undefined when the plan names no
10
+ * command (a non-compose plan with no argv).
11
+ */
12
+ export declare function formatLaunchCommand(plan: LaunchPlan): string | undefined;
13
+ //# sourceMappingURL=launch-command.d.ts.map
@@ -0,0 +1,64 @@
1
+ /**
2
+ * formatLaunchCommand renders a launch plan as the shell command string the
3
+ * deploy step runs to start the engine: the quoted argv joined for a process
4
+ * or docker plan, or the `docker compose up` line for a compose plan. The
5
+ * card's Logs section shows the line above the engine's own output, so a
6
+ * user can read and copy the exact binary and arguments a launch started
7
+ * with. The Hyper-Qwen deploy runs this same string, so the printed line and
8
+ * the host command cannot drift. Returns undefined when the plan names no
9
+ * command (a non-compose plan with no argv).
10
+ */
11
+ export function formatLaunchCommand(plan) {
12
+ if (plan.kind === 'compose') {
13
+ // The user's compose arguments (the card's docker-tag field) override the
14
+ // default profile: the flags the up step runs before the required `up -d`.
15
+ // An empty field reads as "use the default profile", so the up step falls
16
+ // back to the engine's own default.
17
+ const composeArgs = plan.composeArgs?.split(/\s+/).filter((token) => token !== '');
18
+ if (composeArgs !== undefined && composeArgs.length > 0) {
19
+ return [
20
+ 'docker',
21
+ 'compose',
22
+ '-f',
23
+ plan.composeFile ?? 'docker-compose.yml',
24
+ ...composeArgs,
25
+ 'up',
26
+ '-d',
27
+ ]
28
+ .map(quoteArg)
29
+ .join(' ');
30
+ }
31
+ // The compose defaults match the adapter's renderLaunch fallbacks, so a
32
+ // plan built from a bare config still spells a runnable command.
33
+ return [
34
+ 'docker',
35
+ 'compose',
36
+ '-f',
37
+ plan.composeFile ?? 'docker-compose.yml',
38
+ '--profile',
39
+ plan.profile ?? 'single',
40
+ 'up',
41
+ '--pull',
42
+ 'always',
43
+ '-d',
44
+ ]
45
+ .map(quoteArg)
46
+ .join(' ');
47
+ }
48
+ const argv = plan.argv;
49
+ if (argv === undefined || argv.length === 0)
50
+ return undefined;
51
+ return argv.map(quoteArg).join(' ');
52
+ }
53
+ /**
54
+ * quoteArg renders one argv token for display: a token made only of shell-
55
+ * safe characters rides bare, anything else lands in single quotes with
56
+ * embedded quotes escaped, so the printed line pastes back into a shell
57
+ * unchanged.
58
+ */
59
+ function quoteArg(token) {
60
+ if (token !== '' && /^[A-Za-z0-9@%+=:,./_-]+$/.test(token))
61
+ return token;
62
+ return `'${token.replaceAll("'", "'\\''")}'`;
63
+ }
64
+ //# sourceMappingURL=launch-command.js.map
@@ -67,7 +67,8 @@ export interface ModelDirectoryEntry {
67
67
  /**
68
68
  * modelEntriesForEngine filters a model directory listing down to the model
69
69
  * entries the engine's Model dropdown offers: NInfer takes `.ninfer`
70
- * artifacts, llama.cpp takes `.gguf` files, and Hyper-Qwen takes the repo
70
+ * artifacts, Infernix takes `.ninfer` artifacts and its own `.infernix`
71
+ * files, llama.cpp takes `.gguf` files, and Hyper-Qwen takes the repo
71
72
  * directories that hold a `.safetensors` weight file. Every other engine
72
73
  * (Strata) offers nothing, matching its empty default list. The result is
73
74
  * sorted, so the dropdown order is stable across summary fetches.
@@ -61,7 +61,8 @@ export function hfChatTemplateCommand(repo, directory) {
61
61
  /**
62
62
  * modelEntriesForEngine filters a model directory listing down to the model
63
63
  * entries the engine's Model dropdown offers: NInfer takes `.ninfer`
64
- * artifacts, llama.cpp takes `.gguf` files, and Hyper-Qwen takes the repo
64
+ * artifacts, Infernix takes `.ninfer` artifacts and its own `.infernix`
65
+ * files, llama.cpp takes `.gguf` files, and Hyper-Qwen takes the repo
65
66
  * directories that hold a `.safetensors` weight file. Every other engine
66
67
  * (Strata) offers nothing, matching its empty default list. The result is
67
68
  * sorted, so the dropdown order is stable across summary fetches.
@@ -74,6 +75,14 @@ export function modelEntriesForEngine(entries, engineId) {
74
75
  .filter((entry) => !entry.isDirectory && entry.name.endsWith('.ninfer'))
75
76
  .map((entry) => entry.name);
76
77
  break;
78
+ case 'infernix':
79
+ // Infernix loads NInfer artifacts unchanged plus its own `.infernix`
80
+ // files, so the dropdown offers both extensions.
81
+ names = entries
82
+ .filter((entry) => !entry.isDirectory &&
83
+ (entry.name.endsWith('.ninfer') || entry.name.endsWith('.infernix')))
84
+ .map((entry) => entry.name);
85
+ break;
77
86
  case 'llamacpp':
78
87
  names = entries
79
88
  .filter((entry) => !entry.isDirectory && entry.name.endsWith('.gguf'))
@@ -1,4 +1,4 @@
1
- import type { LaunchPlan } from './adapter.ts';
1
+ import type { BuildMode, LaunchPlan } from './adapter.ts';
2
2
  /**
3
3
  * DshModelEntry is the shape of the model row the registrar writes into the
4
4
  * dsh-llm-pi-ai provider: the provider id, the model id and display name, the
@@ -83,11 +83,11 @@ export interface DshModelRegistrar {
83
83
  export declare function modelSlug(modelId: string): string;
84
84
  /**
85
85
  * providerIdFor derives the provider id the registrar writes for an engine
86
- * and model: the engine id, the build mode (local or docker), and the model
87
- * slug, so a profile can carry the same engine in both build modes and
88
- * several models per engine without a collision.
86
+ * and model: the engine id, the build mode (local, docker, or binary), and the
87
+ * model slug, so a profile can carry the same engine in several build modes
88
+ * and several models per engine without a collision.
89
89
  */
90
- export declare function providerIdFor(engineId: string, modelId: string, buildMode: 'local' | 'docker'): string;
90
+ export declare function providerIdFor(engineId: string, modelId: string, buildMode: BuildMode): string;
91
91
  /**
92
92
  * apiKeyEnvFor derives the env-var name a local provider reads its optional
93
93
  * API key from: the engine id, uppercased and non-alphanumerics mapped to
@@ -27,9 +27,9 @@ export function modelSlug(modelId) {
27
27
  }
28
28
  /**
29
29
  * providerIdFor derives the provider id the registrar writes for an engine
30
- * and model: the engine id, the build mode (local or docker), and the model
31
- * slug, so a profile can carry the same engine in both build modes and
32
- * several models per engine without a collision.
30
+ * and model: the engine id, the build mode (local, docker, or binary), and the
31
+ * model slug, so a profile can carry the same engine in several build modes
32
+ * and several models per engine without a collision.
33
33
  */
34
34
  export function providerIdFor(engineId, modelId, buildMode) {
35
35
  return `${engineId}-${buildMode}-${modelSlug(modelId)}`;
@@ -1,4 +1,5 @@
1
1
  import type { GpuUsageProbe } from './reserve.ts';
2
+ import type { Config } from './config.ts';
2
3
  /**
3
4
  * ProviderStatus is the running-state the plugin card paints each saved
4
5
  * provider's status dot: green while the engine is confirmed serving the
@@ -10,12 +11,19 @@ export type ProviderStatus = 'running' | 'stopped' | 'unsure';
10
11
  * ProviderRef is the identity side of one saved provider config: the stable
11
12
  * id the card renders and the engine and model it serves. The model is
12
13
  * optional because a config that names no model cannot be confirmed against
13
- * a running engine, and reads as unsure rather than a guess.
14
+ * a running engine, and reads as unsure rather than a guess. The config is
15
+ * optional and lets the detector price the provider's own launch signature
16
+ * (the engine binary the build mode runs, the port it binds) rather than the
17
+ * adapter's default-port signature; the port is the resolved bind port the
18
+ * detector read from that signature, and the resolver ties a running engine
19
+ * to the provider only when the ports agree.
14
20
  */
15
21
  export interface ProviderRef {
16
22
  id: string;
17
23
  engine: string;
18
24
  model?: string;
25
+ config?: Config;
26
+ port?: number;
19
27
  }
20
28
  /**
21
29
  * RunningEngineRef is one engine the detector confirmed live: the engine id,
@@ -28,7 +28,12 @@ export function isGpuBusy(usage) {
28
28
  export function computeProviderStatuses(input) {
29
29
  const statuses = {};
30
30
  for (const provider of input.providers) {
31
- const matches = input.running.filter((ref) => ref.engineId === provider.engine);
31
+ // A provider that names a port only counts an engine running on that
32
+ // port: two configs of one engine on different ports are distinct
33
+ // engines, and a match on the other port must not confirm (or deny) this
34
+ // config's model. An unnamed port keeps the engine-wide match.
35
+ const matches = input.running.filter((ref) => ref.engineId === provider.engine &&
36
+ (provider.port === undefined || ref.port === undefined || ref.port === provider.port));
32
37
  if (matches.length > 0) {
33
38
  // The engine is running. The question is which model it serves: the
34
39
  // detector's identified model, if it tied one, else unknown.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-local-infer-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Pure, framework-free domain model for the dsh local-inference plugin: ParameterSpec, HardwareProfile, EngineAdapter, VramEstimator, GuidelineEngine, DefaultsResolver, DshModelRegistrar, and SystemDepProvisioner primitives shared by every engine and model",
5
5
  "keywords": [
6
6
  "dsh",