@camstack/system 1.0.6 → 1.0.8

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 (33) hide show
  1. package/dist/addon-runner.js +40 -23
  2. package/dist/addon-runner.mjs +20 -4
  3. package/dist/addon-utils.d.ts +20 -0
  4. package/dist/addon-utils.js +11 -0
  5. package/dist/addon-utils.mjs +3 -0
  6. package/dist/builtins/device-manager/device-manager.addon.js +8 -8
  7. package/dist/builtins/device-manager/device-manager.addon.mjs +8 -8
  8. package/dist/builtins/native-metrics/native-metrics.addon.d.ts +8 -0
  9. package/dist/builtins/native-metrics/native-metrics.addon.js +50 -3
  10. package/dist/builtins/native-metrics/native-metrics.addon.mjs +50 -3
  11. package/dist/builtins/platform-probe/index.js +27 -139
  12. package/dist/builtins/platform-probe/index.mjs +28 -140
  13. package/dist/builtins/platform-probe/platform-scorer.d.ts +17 -10
  14. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +2 -2
  15. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +2 -2
  16. package/dist/custom-action-registry-BEXwC-oo.mjs +38 -0
  17. package/dist/custom-action-registry-vLYEFTtv.js +43 -0
  18. package/dist/index.js +129 -779
  19. package/dist/index.mjs +100 -750
  20. package/dist/kernel/config-manager.d.ts +4 -4
  21. package/dist/kernel/fs-utils.d.ts +16 -6
  22. package/dist/kernel/index.d.ts +1 -1
  23. package/dist/kernel/moleculer/device-cap-proxy.d.ts +2 -1
  24. package/dist/kernel/moleculer/readiness-context.d.ts +2 -1
  25. package/dist/kernel/transport/child-cap-protocol.d.ts +10 -0
  26. package/dist/{manifest-python-deps-B4BmMoGT.js → manifest-python-deps-BWURo7dc.js} +62 -88
  27. package/dist/{manifest-python-deps-CXbKrOdk.mjs → manifest-python-deps-BcrTzHH_.mjs} +55 -75
  28. package/dist/model-download-service-C7AjBsX9.mjs +668 -0
  29. package/dist/model-download-service-JtVQtbb6.js +752 -0
  30. package/dist/process/resource-monitor.d.ts +9 -0
  31. package/dist/{resource-monitor-ClDGFyf6.mjs → resource-monitor-BkP504Vq.mjs} +20 -1
  32. package/dist/{resource-monitor-IIEanuJt.js → resource-monitor-DNNomR-i.js} +21 -1
  33. package/package.json +6 -1
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_resource_monitor = require("./resource-monitor-IIEanuJt.js");
3
+ const require_model_download_service = require("./model-download-service-JtVQtbb6.js");
4
+ const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
4
5
  const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
5
6
  const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
6
7
  const require_builtins_sqlite_storage_index = require("./builtins/sqlite-storage/index.js");
@@ -19,7 +20,8 @@ const require_builtins_local_auth_local_auth_addon = require("./builtins/local-a
19
20
  require("./builtins/local-auth/index.js");
20
21
  const require_builtins_device_manager_device_manager_addon = require("./builtins/device-manager/device-manager.addon.js");
21
22
  require("./builtins/device-manager/index.js");
22
- const require_manifest_python_deps = require("./manifest-python-deps-B4BmMoGT.js");
23
+ const require_manifest_python_deps = require("./manifest-python-deps-BWURo7dc.js");
24
+ const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
23
25
  const require_graceful_fs$1 = require("./graceful-fs-lg19SZNz.js");
24
26
  let _camstack_types_node = require("@camstack/types/node");
25
27
  let node_http = require("node:http");
@@ -39,6 +41,7 @@ let node_os = require("node:os");
39
41
  node_os = require_chunk.__toESM(node_os);
40
42
  let node_fs_promises = require("node:fs/promises");
41
43
  let zod = require("zod");
44
+ let _camstack_types_addon = require("@camstack/types/addon");
42
45
  let fs = require("fs");
43
46
  fs = require_chunk.__toESM(fs, 1);
44
47
  let node_events = require("node:events");
@@ -79,216 +82,6 @@ var EventBus = class {
79
82
  }
80
83
  };
81
84
  //#endregion
82
- //#region src/http/authenticated-file-server.ts
83
- /**
84
- * Authenticated data-plane file-server — a shared host primitive for addons that
85
- * must serve files (media segments, scrub-frame stores, exported clips) directly
86
- * to browsers/players, node-direct (NOT through the hub tRPC, per the recording
87
- * design's "no media bytes traverse the hub").
88
- *
89
- * It bundles the three things every such server needs so addons don't re-roll
90
- * them: CORS (the data plane is cross-origin from the admin-ui — a native
91
- * `<video src>` is exempt but hls.js / WebCodecs fetch via XHR and are blocked
92
- * without it), HTTP `Range` (seeking / partial fetch), and a SCOPED TOKEN check.
93
- *
94
- * The token rides as the FIRST path segment (`<urlPrefix><token>/<relPath>`) so
95
- * a player resolving a manifest's RELATIVE child URIs carries it on every
96
- * sub-request automatically — no per-URI rewriting, no cookies/headers a dumb
97
- * player can't set. The caller supplies `verifyToken(token, relPath)`; the
98
- * primitive stays auth-scheme-agnostic.
99
- *
100
- * Pure helpers (`parseTokenizedUrl`, `resolveFilePath`, `contentTypeFor`,
101
- * `parseRangeHeader`) are exported for unit testing; `createAuthenticatedFileServer`
102
- * is the thin `node:http` glue.
103
- */
104
- function corsHeaders(origin) {
105
- return {
106
- "access-control-allow-origin": origin,
107
- "access-control-allow-methods": "GET, HEAD, OPTIONS",
108
- "access-control-allow-headers": "range",
109
- "access-control-expose-headers": "content-length, content-range, accept-ranges",
110
- "access-control-max-age": "86400"
111
- };
112
- }
113
- /**
114
- * Split a request URL (`<urlPrefix><token>/<relPath>`) into its token + rel
115
- * path. Returns null for anything outside the prefix or missing either part.
116
- * `relPath` is URL-decoded; a decode failure → null.
117
- */
118
- function parseTokenizedUrl(urlPrefix, urlPath) {
119
- if (!urlPath.startsWith(urlPrefix)) return null;
120
- const after = urlPath.slice(urlPrefix.length);
121
- const slash = after.indexOf("/");
122
- if (slash <= 0) return null;
123
- const token = after.slice(0, slash);
124
- let rel;
125
- try {
126
- rel = decodeURIComponent(after.slice(slash + 1));
127
- } catch {
128
- return null;
129
- }
130
- if (rel.length === 0) return null;
131
- return {
132
- token,
133
- rel
134
- };
135
- }
136
- /**
137
- * Map a rel path to one candidate absolute path PER root, keeping only roots the
138
- * path stays within (traversal guard). The handler serves the first candidate
139
- * that exists. Rejects a path that escapes every root.
140
- */
141
- function resolveFilePath(roots, rel) {
142
- if (rel.length === 0) return { error: "forbidden" };
143
- const candidates = [];
144
- for (const rootDir of roots) {
145
- const root = node_path.default.resolve(rootDir);
146
- const abs = node_path.default.resolve(root, rel);
147
- if (abs === root || abs.startsWith(root + node_path.default.sep)) candidates.push(abs);
148
- }
149
- if (candidates.length === 0) return { error: "forbidden" };
150
- return { candidates };
151
- }
152
- var DEFAULT_CONTENT_TYPES = {
153
- ".m3u8": "application/vnd.apple.mpegurl",
154
- ".m4s": "video/mp4",
155
- ".mp4": "video/mp4",
156
- ".idx": "application/octet-stream",
157
- ".html": "text/html; charset=utf-8",
158
- ".js": "application/javascript; charset=utf-8",
159
- ".mjs": "application/javascript; charset=utf-8",
160
- ".css": "text/css; charset=utf-8",
161
- ".json": "application/json; charset=utf-8",
162
- ".map": "application/json; charset=utf-8",
163
- ".svg": "image/svg+xml",
164
- ".png": "image/png",
165
- ".jpg": "image/jpeg",
166
- ".jpeg": "image/jpeg",
167
- ".ico": "image/x-icon",
168
- ".woff2": "font/woff2",
169
- ".woff": "font/woff",
170
- ".ttf": "font/ttf"
171
- };
172
- function contentTypeFor(filePath, overrides) {
173
- const ext = node_path.default.extname(filePath).toLowerCase();
174
- return overrides?.[ext] ?? DEFAULT_CONTENT_TYPES[ext] ?? "application/octet-stream";
175
- }
176
- /** Parse an HTTP `Range` header against a known size. Null = serve whole file. */
177
- function parseRangeHeader(header, size) {
178
- if (!header) return null;
179
- const m = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
180
- if (!m) return null;
181
- const [, rawStart, rawEnd] = m;
182
- if (rawStart === "" && rawEnd === "") return null;
183
- let start;
184
- let end;
185
- if (rawStart === "") {
186
- const n = Number(rawEnd);
187
- if (n <= 0) return null;
188
- start = Math.max(0, size - n);
189
- end = size - 1;
190
- } else {
191
- start = Number(rawStart);
192
- end = rawEnd === "" ? size - 1 : Number(rawEnd);
193
- }
194
- if (!Number.isFinite(start) || !Number.isFinite(end) || start > end || start >= size) return null;
195
- return {
196
- start,
197
- end: Math.min(end, size - 1)
198
- };
199
- }
200
- /**
201
- * Start an authenticated file-server. Returns the bound port so the caller can
202
- * build URLs. `getRoots`/`verifyToken` are invoked per request, so they always
203
- * reflect the live config.
204
- */
205
- async function createAuthenticatedFileServer(opts) {
206
- const cors = corsHeaders(opts.corsOrigin ?? "*");
207
- const server = (0, node_http.createServer)((req, res) => {
208
- handle(opts, cors, req, res);
209
- });
210
- await new Promise((resolve, reject) => {
211
- server.once("error", reject);
212
- server.listen(opts.port, () => resolve());
213
- });
214
- const addr = server.address();
215
- return {
216
- port: typeof addr === "object" && addr ? addr.port : opts.port,
217
- close: () => new Promise((resolve) => server.close(() => resolve()))
218
- };
219
- }
220
- async function handle(opts, cors, req, res) {
221
- if (req.method === "OPTIONS") {
222
- res.writeHead(204, cors).end();
223
- return;
224
- }
225
- if (req.method !== "GET" && req.method !== "HEAD") {
226
- res.writeHead(405, cors).end();
227
- return;
228
- }
229
- const urlPath = (req.url ?? "").split("?")[0] ?? "";
230
- const parsed = parseTokenizedUrl(opts.urlPrefix, urlPath);
231
- if (!parsed) {
232
- res.writeHead(403, cors).end();
233
- return;
234
- }
235
- if (!opts.verifyToken(parsed.token, parsed.rel)) {
236
- res.writeHead(401, cors).end();
237
- return;
238
- }
239
- const resolved = resolveFilePath(opts.getRoots(), parsed.rel);
240
- if ("error" in resolved) {
241
- res.writeHead(403, cors).end();
242
- return;
243
- }
244
- let absPath = null;
245
- let size = 0;
246
- for (const candidate of resolved.candidates) try {
247
- const st = await node_fs.promises.stat(candidate);
248
- if (st.isFile()) {
249
- absPath = candidate;
250
- size = st.size;
251
- break;
252
- }
253
- } catch {}
254
- if (absPath === null) {
255
- res.writeHead(404, cors).end();
256
- return;
257
- }
258
- const baseHeaders = {
259
- ...cors,
260
- "content-type": contentTypeFor(absPath, opts.contentTypes),
261
- "accept-ranges": "bytes",
262
- "cache-control": "no-cache"
263
- };
264
- const range = parseRangeHeader(req.headers.range, size);
265
- if (range) {
266
- res.writeHead(206, {
267
- ...baseHeaders,
268
- "content-range": `bytes ${range.start}-${range.end}/${size}`,
269
- "content-length": String(range.end - range.start + 1)
270
- });
271
- if (req.method === "HEAD") {
272
- res.end();
273
- return;
274
- }
275
- (0, node_fs.createReadStream)(absPath, {
276
- start: range.start,
277
- end: range.end
278
- }).pipe(res);
279
- return;
280
- }
281
- res.writeHead(200, {
282
- ...baseHeaders,
283
- "content-length": String(size)
284
- });
285
- if (req.method === "HEAD") {
286
- res.end();
287
- return;
288
- }
289
- (0, node_fs.createReadStream)(absPath).pipe(res);
290
- }
291
- //#endregion
292
85
  //#region src/http/data-plane-registry.ts
293
86
  function trimSlashes(s) {
294
87
  return s.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -389,459 +182,8 @@ function proxyToUpstream(opts) {
389
182
  clientReq.pipe(upstream);
390
183
  }
391
184
  //#endregion
392
- //#region src/http/file-data-plane.ts
393
- /**
394
- * A ready-made data-plane request handler that serves files from a set of roots
395
- * with HTTP `Range`, for addons whose data-plane is "stream files off disk"
396
- * (recording playback, scrub-frame stores, exported clips).
397
- *
398
- * This is the addon-side handler the addon hands to `ctx.dataPlane.serve({ handler })`.
399
- * It receives the REAL Node `req`/`res`, resolves the request path against the
400
- * addon's roots (traversal-guarded), and streams the file. NO token and NO CORS:
401
- * the hub already authenticated the caller and the data plane is same-origin
402
- * through the hub's port. Reuses the shared Range/content-type/traversal helpers
403
- * so there is one implementation across the standalone file-server and this.
404
- */
405
- /** Build a `(req, res)` handler that serves `getRoots()` files with Range. */
406
- function createFileDataPlaneHandler(opts) {
407
- return async (req, res) => {
408
- if (req.method !== "GET" && req.method !== "HEAD") {
409
- res.writeHead(405).end();
410
- return;
411
- }
412
- const urlPath = (req.url ?? "/").split("?")[0] ?? "/";
413
- let rel;
414
- try {
415
- rel = decodeURIComponent(urlPath.replace(/^\/+/, ""));
416
- } catch {
417
- res.writeHead(400).end();
418
- return;
419
- }
420
- if (rel.length === 0) {
421
- res.writeHead(404).end();
422
- return;
423
- }
424
- const resolved = resolveFilePath(opts.getRoots(), rel);
425
- if ("error" in resolved) {
426
- res.writeHead(403).end();
427
- return;
428
- }
429
- let absPath = null;
430
- let size = 0;
431
- for (const candidate of resolved.candidates) try {
432
- const st = await node_fs.promises.stat(candidate);
433
- if (st.isFile()) {
434
- absPath = candidate;
435
- size = st.size;
436
- break;
437
- }
438
- } catch {}
439
- if (absPath === null) {
440
- res.writeHead(404).end();
441
- return;
442
- }
443
- const baseHeaders = {
444
- "content-type": contentTypeFor(absPath, opts.contentTypes),
445
- "accept-ranges": "bytes",
446
- "cache-control": "no-cache"
447
- };
448
- const range = parseRangeHeader(req.headers.range, size);
449
- if (range) {
450
- res.writeHead(206, {
451
- ...baseHeaders,
452
- "content-range": `bytes ${range.start}-${range.end}/${size}`,
453
- "content-length": String(range.end - range.start + 1)
454
- });
455
- if (req.method === "HEAD") {
456
- res.end();
457
- return;
458
- }
459
- (0, node_fs.createReadStream)(absPath, {
460
- start: range.start,
461
- end: range.end
462
- }).pipe(res);
463
- return;
464
- }
465
- res.writeHead(200, {
466
- ...baseHeaders,
467
- "content-length": String(size)
468
- });
469
- if (req.method === "HEAD") {
470
- res.end();
471
- return;
472
- }
473
- (0, node_fs.createReadStream)(absPath).pipe(res);
474
- };
475
- }
476
- //#endregion
477
- //#region src/download/model-downloader.ts
478
- /** Build fetch headers, including HF auth token for huggingface.co URLs */
479
- function buildHeaders(url) {
480
- const headers = { "User-Agent": "CamStack/1.0" };
481
- const hfToken = process.env["HF_TOKEN"] ?? process.env["HUGGING_FACE_HUB_TOKEN"];
482
- if (hfToken && url.includes("huggingface.co")) headers["Authorization"] = `Bearer ${hfToken}`;
483
- return headers;
484
- }
485
- /**
486
- * Download a single file from a URL to a destination path.
487
- * Uses native fetch() (Node 22+) which handles redirects natively.
488
- * Streams to disk with optional progress callback.
489
- * Returns the destination path. Skips download if file already exists.
490
- */
491
- async function downloadFile(url, destPath, onProgress) {
492
- if (node_fs.existsSync(destPath)) return destPath;
493
- node_fs.mkdirSync(node_path.dirname(destPath), { recursive: true });
494
- const tmpPath = destPath + ".downloading";
495
- try {
496
- const response = await fetch(url, {
497
- redirect: "follow",
498
- headers: buildHeaders(url)
499
- });
500
- if (!response.ok) throw new Error(`HTTP ${response.status} downloading ${url}`);
501
- if (!response.body) throw new Error(`No response body from ${url}`);
502
- const total = parseInt(response.headers.get("content-length") ?? "0", 10);
503
- let downloaded = 0;
504
- const fileStream = node_fs.createWriteStream(tmpPath);
505
- const reader = response.body.getReader();
506
- try {
507
- for (;;) {
508
- const { done, value } = await reader.read();
509
- if (done || !value) break;
510
- fileStream.write(value);
511
- downloaded += value.length;
512
- onProgress?.(downloaded, total);
513
- }
514
- } finally {
515
- fileStream.end();
516
- await new Promise((resolve, reject) => {
517
- fileStream.on("finish", resolve);
518
- fileStream.on("error", reject);
519
- });
520
- }
521
- node_fs.renameSync(tmpPath, destPath);
522
- return destPath;
523
- } catch (err) {
524
- try {
525
- node_fs.unlinkSync(tmpPath);
526
- } catch {}
527
- throw err;
528
- }
529
- }
530
- /**
531
- * Fetch JSON from a URL using native fetch().
532
- */
533
- async function fetchJson(url) {
534
- const response = await fetch(url, {
535
- redirect: "follow",
536
- headers: buildHeaders(url)
537
- });
538
- if (!response.ok) throw new Error(`HTTP ${response.status} fetching ${url}`);
539
- return response.json();
540
- }
541
- /**
542
- * Download a model with fallback URLs and optional SHA256 verification.
543
- * Legacy API preserved for backward compatibility -- delegates to downloadFile().
544
- */
545
- async function downloadModel(options) {
546
- const { url, fallbackUrls = [], destDir, filename, expectedSha256, onProgress } = options;
547
- const fname = filename ?? url.split("/").pop() ?? "model.bin";
548
- const destPath = node_path.join(destDir, fname);
549
- if (node_fs.existsSync(destPath)) return {
550
- filePath: destPath,
551
- downloadedBytes: 0,
552
- fromCache: true
553
- };
554
- node_fs.mkdirSync(destDir, { recursive: true });
555
- const urls = [url, ...fallbackUrls];
556
- let lastError = null;
557
- for (const tryUrl of urls) try {
558
- await downloadFile(tryUrl, destPath, onProgress);
559
- if (expectedSha256) {
560
- const hash = await computeSha256(destPath);
561
- if (hash !== expectedSha256) {
562
- node_fs.unlinkSync(destPath);
563
- throw new Error(`SHA256 mismatch: expected ${expectedSha256}, got ${hash}`);
564
- }
565
- }
566
- return {
567
- filePath: destPath,
568
- downloadedBytes: node_fs.statSync(destPath).size,
569
- fromCache: false
570
- };
571
- } catch (e) {
572
- lastError = e;
573
- if (node_fs.existsSync(destPath)) node_fs.unlinkSync(destPath);
574
- }
575
- throw lastError ?? /* @__PURE__ */ new Error(`Failed to download model from ${url}`);
576
- }
577
- async function computeSha256(filePath) {
578
- return new Promise((resolve, reject) => {
579
- const hash = (0, node_crypto.createHash)("sha256");
580
- const stream = node_fs.createReadStream(filePath);
581
- stream.on("data", (chunk) => hash.update(chunk));
582
- stream.on("end", () => resolve(hash.digest("hex")));
583
- stream.on("error", reject);
584
- });
585
- }
586
- /**
587
- * Download every file in a HuggingFace directory bundle (e.g.,
588
- * `.mlpackage` / OpenVINO IR pair) atomically. `knownFiles` lists the
589
- * relative paths inside the directory; the function fetches each from
590
- * `${url}/${file}` and renames the staging directory only on full
591
- * success. Mirrors `ModelDownloadService.downloadDirectory` but
592
- * exposed as a standalone for catalog-less callers.
593
- */
594
- async function downloadDirectory(url, destDir, knownFiles, onProgress) {
595
- const match = url.match(/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/main\/(.+)/);
596
- if (!match) throw new Error(`Cannot parse HuggingFace URL: ${url}`);
597
- const [, repo, dirPath] = match;
598
- const files = (knownFiles ?? []).map((f) => ({
599
- relativePath: f,
600
- fileUrl: `https://huggingface.co/${repo}/resolve/main/${dirPath}/${f}`
601
- }));
602
- if (files.length === 0) throw new Error(`Directory bundle requires explicit \`files\` list (got none for ${url})`);
603
- const tmpDir = destDir + ".downloading";
604
- node_fs.rmSync(tmpDir, {
605
- recursive: true,
606
- force: true
607
- });
608
- node_fs.mkdirSync(tmpDir, { recursive: true });
609
- let totalDownloaded = 0;
610
- try {
611
- for (const file of files) {
612
- const destPath = node_path.join(tmpDir, file.relativePath);
613
- node_fs.mkdirSync(node_path.dirname(destPath), { recursive: true });
614
- await downloadFile(file.fileUrl, destPath, (downloaded, _total) => {
615
- onProgress?.(totalDownloaded + downloaded, void 0);
616
- });
617
- totalDownloaded += node_fs.statSync(destPath).size;
618
- }
619
- node_fs.rmSync(destDir, {
620
- recursive: true,
621
- force: true
622
- });
623
- node_fs.renameSync(tmpDir, destDir);
624
- } catch (err) {
625
- node_fs.rmSync(tmpDir, {
626
- recursive: true,
627
- force: true
628
- });
629
- throw err;
630
- }
631
- }
632
- /**
633
- * Resolve a `ModelCatalogEntry` against `modelsDir`: download model file
634
- * (or directory bundle) + extra files (labels JSON, charset dict, …),
635
- * skip if already on disk. Returns the local model path.
636
- */
637
- async function ensureModel(modelsDir, entry, format, onProgress) {
638
- const formatEntry = entry.formats[format];
639
- if (!formatEntry) throw new Error(`Model "${entry.id}" has no ${format} format. Available: ${Object.keys(entry.formats).join(", ")}`);
640
- if (entry.extraFiles) for (const extra of entry.extraFiles) await downloadFile(extra.url, node_path.join(modelsDir, extra.filename));
641
- const filename = formatEntry.url.split("/").pop() ?? `${entry.id}.${format}`;
642
- const modelPath = node_path.join(modelsDir, filename);
643
- if (node_fs.existsSync(modelPath)) if (formatEntry.isDirectory && !node_fs.existsSync(node_path.join(modelPath, "Manifest.json"))) node_fs.rmSync(modelPath, {
644
- recursive: true,
645
- force: true
646
- });
647
- else return modelPath;
648
- node_fs.mkdirSync(modelsDir, { recursive: true });
649
- if (formatEntry.isDirectory) await downloadDirectory(formatEntry.url, modelPath, formatEntry.files, onProgress);
650
- else await downloadFile(formatEntry.url, modelPath, (downloaded, total) => onProgress?.(downloaded, total === 0 ? void 0 : total));
651
- return modelPath;
652
- }
653
- /** Compute the on-disk path for a given model + format, even when not yet downloaded. */
654
- function getModelFilePath(modelsDir, entry, format) {
655
- const formatEntry = entry.formats[format];
656
- if (!formatEntry) return null;
657
- const filename = formatEntry.url.split("/").pop() ?? `${entry.id}.${format}`;
658
- return node_path.join(modelsDir, filename);
659
- }
660
- /** True iff the model file (or `Manifest.json` for directory bundles) exists and is non-empty. */
661
- function isModelDownloaded(modelsDir, entry, format) {
662
- const formatEntry = entry.formats[format];
663
- if (!formatEntry) return false;
664
- const modelPath = getModelFilePath(modelsDir, entry, format);
665
- if (!modelPath || !node_fs.existsSync(modelPath)) return false;
666
- if (formatEntry.isDirectory) return node_fs.existsSync(node_path.join(modelPath, "Manifest.json"));
667
- return node_fs.statSync(modelPath).size > 0;
668
- }
669
- /** Remove the on-disk model file/directory. Returns true if something was deleted. */
670
- function deleteModelFromDisk(modelsDir, entry, format) {
671
- const modelPath = getModelFilePath(modelsDir, entry, format);
672
- if (!modelPath || !node_fs.existsSync(modelPath)) return false;
673
- if (entry.formats[format]?.isDirectory) node_fs.rmSync(modelPath, {
674
- recursive: true,
675
- force: true
676
- });
677
- else node_fs.unlinkSync(modelPath);
678
- return true;
679
- }
680
- //#endregion
681
- //#region src/download/model-download-service.ts
682
- /**
683
- * Unified model download service.
684
- *
685
- * Handles downloading model files and extra files (labels, dicts) from a
686
- * catalog of ModelCatalogEntry items. Supports single-file models and
687
- * directory bundles (e.g., .mlpackage for CoreML).
688
- *
689
- * Addons use this via `context.models.ensure(modelId, format)`.
690
- */
691
- var ModelDownloadService = class {
692
- modelsDir;
693
- onProgress;
694
- catalog;
695
- constructor(modelsDir, catalog, onProgress) {
696
- this.modelsDir = modelsDir;
697
- this.onProgress = onProgress;
698
- const map = /* @__PURE__ */ new Map();
699
- for (const entry of catalog) map.set(entry.id, entry);
700
- this.catalog = map;
701
- }
702
- /**
703
- * Ensure a model (and its extra files) is downloaded.
704
- * Returns the local filesystem path to the model file/directory.
705
- */
706
- async ensure(modelId, format) {
707
- const entry = this.catalog.get(modelId);
708
- if (!entry) throw new Error(`ModelDownloadService: unknown model "${modelId}"`);
709
- const selectedFormat = format ?? this.pickDefaultFormat(entry);
710
- const formatEntry = entry.formats[selectedFormat];
711
- if (!formatEntry) throw new Error(`ModelDownloadService: model "${modelId}" has no ${selectedFormat} format`);
712
- await this.ensureExtraFiles(modelId);
713
- const modelPath = this.modelFilePath(entry, selectedFormat);
714
- if (node_fs.existsSync(modelPath)) if (formatEntry.isDirectory) if (!node_fs.existsSync(node_path.join(modelPath, "Manifest.json"))) node_fs.rmSync(modelPath, {
715
- recursive: true,
716
- force: true
717
- });
718
- else return modelPath;
719
- else return modelPath;
720
- node_fs.mkdirSync(this.modelsDir, { recursive: true });
721
- if (formatEntry.isDirectory) await this.downloadDirectory(formatEntry.url, modelPath, formatEntry.files, modelId);
722
- else await downloadFile(formatEntry.url, modelPath, this.onProgress ? (downloaded, total) => this.onProgress(modelId, downloaded, total) : void 0);
723
- return modelPath;
724
- }
725
- /**
726
- * Ensure extra files for a model are downloaded.
727
- * Returns the local paths of all extra files.
728
- */
729
- async ensureExtraFiles(modelId) {
730
- const entry = this.catalog.get(modelId);
731
- if (!entry) throw new Error(`ModelDownloadService: unknown model "${modelId}"`);
732
- const extras = entry.extraFiles;
733
- if (!extras || extras.length === 0) return [];
734
- const paths = [];
735
- for (const extra of extras) {
736
- const destPath = node_path.join(this.modelsDir, extra.filename);
737
- await downloadFile(extra.url, destPath);
738
- paths.push(destPath);
739
- }
740
- return paths;
741
- }
742
- /** Absolute path to the shared models directory. */
743
- getModelsDir() {
744
- return this.modelsDir;
745
- }
746
- /** Check if a model file is already present on disk. */
747
- isDownloaded(modelId, format) {
748
- const entry = this.catalog.get(modelId);
749
- if (!entry) return false;
750
- const selectedFormat = format ?? this.pickDefaultFormat(entry);
751
- const formatEntry = entry.formats[selectedFormat];
752
- if (!formatEntry) return false;
753
- const modelPath = this.modelFilePath(entry, selectedFormat);
754
- if (!node_fs.existsSync(modelPath)) return false;
755
- if (formatEntry.isDirectory) return node_fs.existsSync(node_path.join(modelPath, "Manifest.json"));
756
- return node_fs.statSync(modelPath).size > 0;
757
- }
758
- /** Get the catalog entry for a model by ID. */
759
- getEntry(modelId) {
760
- return this.catalog.get(modelId);
761
- }
762
- pickDefaultFormat(entry) {
763
- for (const fmt of [
764
- "onnx",
765
- "coreml",
766
- "openvino",
767
- "tflite",
768
- "pt"
769
- ]) if (entry.formats[fmt]) return fmt;
770
- const first = Object.keys(entry.formats)[0];
771
- if (first) return first;
772
- throw new Error(`ModelDownloadService: model "${entry.id}" has no formats`);
773
- }
774
- modelFilePath(entry, format) {
775
- const formatEntry = entry.formats[format];
776
- if (!formatEntry) throw new Error(`Model ${entry.id} has no ${format} format`);
777
- const urlParts = formatEntry.url.split("/");
778
- const filename = urlParts[urlParts.length - 1] ?? `${entry.id}.${format}`;
779
- return node_path.join(this.modelsDir, filename);
780
- }
781
- /**
782
- * Download a directory bundle (e.g., .mlpackage) from HuggingFace.
783
- * ATOMIC: downloads to temp dir, renames only on complete success.
784
- */
785
- async downloadDirectory(url, destDir, knownFiles, _modelId) {
786
- const match = url.match(/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/main\/(.+)/);
787
- if (!match) throw new Error(`Cannot parse HuggingFace URL: ${url}`);
788
- const [, repo, dirPath] = match;
789
- let filesToDownload;
790
- if (knownFiles && knownFiles.length > 0) filesToDownload = knownFiles.map((f) => ({
791
- relativePath: f,
792
- fileUrl: `https://huggingface.co/${repo}/resolve/main/${dirPath}/${f}`
793
- }));
794
- else {
795
- const hfFiles = await this.listHfFiles(repo, dirPath);
796
- if (hfFiles.length === 0) throw new Error(`No files found in HuggingFace directory: ${dirPath}`);
797
- filesToDownload = hfFiles.map((f) => ({
798
- relativePath: f.path.substring(dirPath.length + 1),
799
- fileUrl: `https://huggingface.co/${repo}/resolve/main/${f.path}`
800
- }));
801
- }
802
- const tmpDir = destDir + ".downloading";
803
- node_fs.rmSync(tmpDir, {
804
- recursive: true,
805
- force: true
806
- });
807
- node_fs.mkdirSync(tmpDir, { recursive: true });
808
- try {
809
- for (const file of filesToDownload) {
810
- const destPath = node_path.join(tmpDir, file.relativePath);
811
- node_fs.mkdirSync(node_path.dirname(destPath), { recursive: true });
812
- await downloadFile(file.fileUrl, destPath);
813
- }
814
- node_fs.rmSync(destDir, {
815
- recursive: true,
816
- force: true
817
- });
818
- node_fs.renameSync(tmpDir, destDir);
819
- } catch (err) {
820
- node_fs.rmSync(tmpDir, {
821
- recursive: true,
822
- force: true
823
- });
824
- throw err;
825
- }
826
- }
827
- /** Recursively list all files in a HuggingFace directory via API. */
828
- async listHfFiles(repo, dirPath) {
829
- const entries = await fetchJson(`https://huggingface.co/api/models/${repo}/tree/main/${dirPath}`);
830
- const files = [];
831
- for (const entry of entries) if (entry.type === "file") files.push({
832
- path: entry.path,
833
- size: entry.size ?? entry.lfs?.size ?? 0
834
- });
835
- else if (entry.type === "directory") {
836
- const subFiles = await this.listHfFiles(repo, entry.path);
837
- files.push(...subFiles);
838
- }
839
- return files;
840
- }
841
- };
842
- //#endregion
843
185
  //#region src/python/python-env-manager.ts
844
- var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
186
+ var execFileAsync$3 = (0, node_util.promisify)(node_child_process.execFile);
845
187
  var PythonEnvManager = class {
846
188
  venvPath;
847
189
  cachedProbe = null;
@@ -851,12 +193,12 @@ var PythonEnvManager = class {
851
193
  async probe() {
852
194
  if (this.cachedProbe) return this.cachedProbe;
853
195
  for (const cmd of ["python3", "python"]) try {
854
- const { stdout } = await execFileAsync$2(cmd, ["--version"]);
196
+ const { stdout } = await execFileAsync$3(cmd, ["--version"]);
855
197
  const version = stdout.trim().replace("Python ", "");
856
198
  const major = parseInt(version.split(".")[0] ?? "0", 10);
857
199
  const minor = parseInt(version.split(".")[1] ?? "0", 10);
858
200
  if (major < 3 || major === 3 && minor < 10) continue;
859
- const { stdout: pathOut } = await execFileAsync$2(cmd, ["-c", "import sys; print(sys.executable)"]);
201
+ const { stdout: pathOut } = await execFileAsync$3(cmd, ["-c", "import sys; print(sys.executable)"]);
860
202
  this.cachedProbe = {
861
203
  available: true,
862
204
  version,
@@ -872,13 +214,13 @@ var PythonEnvManager = class {
872
214
  async ensure(options) {
873
215
  const probe = await this.probe();
874
216
  if (!probe.available || !probe.path) throw new Error("Python 3.10+ is required but not found on this system");
875
- if (!node_fs.existsSync(node_path.join(this.venvPath, "bin", "python"))) await execFileAsync$2(probe.path, [
217
+ if (!node_fs.existsSync(node_path.join(this.venvPath, "bin", "python"))) await execFileAsync$3(probe.path, [
876
218
  "-m",
877
219
  "venv",
878
220
  this.venvPath
879
221
  ]);
880
222
  const venvPython = node_path.join(this.venvPath, "bin", "python");
881
- if (options.packages.length > 0) await execFileAsync$2(venvPython, [
223
+ if (options.packages.length > 0) await execFileAsync$3(venvPython, [
882
224
  "-m",
883
225
  "pip",
884
226
  "install",
@@ -2077,12 +1419,11 @@ var StorageManager = class {
2077
1419
  return namespace ? this.createNamespacedLocation(location, namespace) : location;
2078
1420
  }
2079
1421
  createLegacyShim() {
2080
- const self = this;
2081
1422
  return {
2082
1423
  async initialize() {},
2083
1424
  async shutdown() {},
2084
- getLocation(name) {
2085
- return self.getLocation(name);
1425
+ getLocation: (name) => {
1426
+ return this.getLocation(name);
2086
1427
  }
2087
1428
  };
2088
1429
  }
@@ -2358,7 +1699,7 @@ function matchPath(pattern, path) {
2358
1699
  }
2359
1700
  //#endregion
2360
1701
  //#region src/tls/cert-manager.ts
2361
- var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
1702
+ var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
2362
1703
  /**
2363
1704
  * Ensure a self-signed TLS certificate exists in the given directory.
2364
1705
  * Generates one if missing. Returns paths to cert and key files.
@@ -2395,7 +1736,7 @@ async function ensureTlsCert(dataDir, options) {
2395
1736
  for (const dns of sanDns) sanParts.push(`DNS:${dns}`);
2396
1737
  for (const ip of sanIps) sanParts.push(`IP:${ip}`);
2397
1738
  const sanString = sanParts.join(",");
2398
- await execFileAsync$1("openssl", [
1739
+ await execFileAsync$2("openssl", [
2399
1740
  "req",
2400
1741
  "-x509",
2401
1742
  "-newkey",
@@ -3228,6 +2569,7 @@ var AddonEngineManager = class {
3228
2569
  };
3229
2570
  //#endregion
3230
2571
  //#region src/kernel/fs-utils.ts
2572
+ var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
3231
2573
  /**
3232
2574
  * Ensure a directory exists (recursive).
3233
2575
  * Single source of truth — replaces scattered mkdirSync calls.
@@ -3236,18 +2578,20 @@ function ensureDir(dirPath) {
3236
2578
  node_fs.mkdirSync(dirPath, { recursive: true });
3237
2579
  }
3238
2580
  /**
3239
- * Copy a directory recursively.
3240
- * Single source of truth — extracted from addon-installer + first-boot-installer.
2581
+ * Copy a directory recursively — ASYNC so it never blocks the event loop.
2582
+ *
2583
+ * The hub installs addons into `/data/addons`, which on Unraid is a slow
2584
+ * shfs/FUSE mount. The former synchronous `fs.copyFileSync`-per-file loop
2585
+ * blocked the Node event loop for the whole copy of a large bundle (e.g.
2586
+ * `addon-pipeline`), which froze the hub's HTTP listener mid-`camstack deploy`
2587
+ * (accept backlog piling up, existing connections stuck in CLOSE_WAIT). Using
2588
+ * `fs.promises.cp` keeps the I/O off the event loop. (Node ≥18 stable.)
3241
2589
  */
3242
- function copyDirRecursive(src, dest) {
3243
- ensureDir(dest);
3244
- const entries = node_fs.readdirSync(src, { withFileTypes: true });
3245
- for (const entry of entries) {
3246
- const srcPath = node_path.join(src, entry.name);
3247
- const destPath = node_path.join(dest, entry.name);
3248
- if (entry.isDirectory()) copyDirRecursive(srcPath, destPath);
3249
- else node_fs.copyFileSync(srcPath, destPath);
3250
- }
2590
+ async function copyDirRecursive(src, dest) {
2591
+ await node_fs.promises.cp(src, dest, {
2592
+ recursive: true,
2593
+ force: true
2594
+ });
3251
2595
  }
3252
2596
  /**
3253
2597
  * Strip @camstack/* dependencies and devDependencies from a package.json object.
@@ -3277,7 +2621,7 @@ function stripCamstackDeps(pkg) {
3277
2621
  * Copies directories (not individual files) from source to destination.
3278
2622
  * Skips "dist" (already handled) and glob patterns.
3279
2623
  */
3280
- function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
2624
+ async function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
3281
2625
  const rawFiles = pkgJson.files;
3282
2626
  if (!Array.isArray(rawFiles)) return;
3283
2627
  for (const fileEntry of rawFiles) {
@@ -3286,11 +2630,11 @@ function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
3286
2630
  const srcPath = node_path.join(sourceDir, fileEntry);
3287
2631
  if (!node_fs.existsSync(srcPath)) continue;
3288
2632
  const destPath = node_path.join(destDir, fileEntry);
3289
- const stat = node_fs.statSync(srcPath);
3290
- if (stat.isDirectory()) copyDirRecursive(srcPath, destPath);
2633
+ const stat = await node_fs.promises.stat(srcPath);
2634
+ if (stat.isDirectory()) await copyDirRecursive(srcPath, destPath);
3291
2635
  else if (stat.isFile()) {
3292
2636
  ensureDir(node_path.dirname(destPath));
3293
- node_fs.copyFileSync(srcPath, destPath);
2637
+ await node_fs.promises.copyFile(srcPath, destPath);
3294
2638
  }
3295
2639
  }
3296
2640
  }
@@ -3335,12 +2679,16 @@ function ensureLibraryBuilt(packageName, packagesDir) {
3335
2679
  /**
3336
2680
  * Install a single npm package into a target directory (package.json + dist/).
3337
2681
  * No validation on camstack.addons -- works for any @camstack/* package.
3338
- * Uses synchronous child_process calls (suitable for first-boot and update paths).
2682
+ *
2683
+ * ASYNC: uses `execFile`/`fs.promises` throughout so a package install on a
2684
+ * live node never blocks the event loop (the `npm pack` + tar extract + copy to
2685
+ * the slow shfs/FUSE `/data` would otherwise freeze the HTTP listener). See
2686
+ * `copyDirRecursive` for the wedge this prevents.
3339
2687
  */
3340
- function installPackageFromNpmSync(packageName, targetDir) {
3341
- const tmpDir = node_fs.mkdtempSync(node_path.join(node_os.tmpdir(), "camstack-install-"));
2688
+ async function installPackageFromNpm(packageName, targetDir) {
2689
+ const tmpDir = await node_fs.promises.mkdtemp(node_path.join(node_os.tmpdir(), "camstack-install-"));
3342
2690
  try {
3343
- const tgzFilename = (0, node_child_process.execFileSync)("npm", [
2691
+ const { stdout } = await execFileAsync$1("npm", [
3344
2692
  "pack",
3345
2693
  packageName,
3346
2694
  "--pack-destination",
@@ -3348,12 +2696,13 @@ function installPackageFromNpmSync(packageName, targetDir) {
3348
2696
  ], {
3349
2697
  timeout: 12e4,
3350
2698
  encoding: "utf-8"
3351
- }).trim().split("\n").pop()?.trim();
2699
+ });
2700
+ const tgzFilename = stdout.trim().split("\n").pop()?.trim();
3352
2701
  if (!tgzFilename) throw new Error("npm pack produced no output");
3353
2702
  const tgzPath = node_path.join(tmpDir, tgzFilename);
3354
2703
  const extractDir = node_path.join(tmpDir, "extracted");
3355
2704
  ensureDir(extractDir);
3356
- (0, node_child_process.execFileSync)("tar", [
2705
+ await execFileAsync$1("tar", [
3357
2706
  "-xzf",
3358
2707
  tgzPath,
3359
2708
  "-C",
@@ -3361,20 +2710,20 @@ function installPackageFromNpmSync(packageName, targetDir) {
3361
2710
  ], { timeout: 3e4 });
3362
2711
  const packageSubDir = node_path.join(extractDir, "package");
3363
2712
  const srcPkgJsonDir = node_fs.existsSync(node_path.join(packageSubDir, "package.json")) ? packageSubDir : extractDir;
3364
- node_fs.rmSync(targetDir, {
2713
+ await node_fs.promises.rm(targetDir, {
3365
2714
  recursive: true,
3366
2715
  force: true
3367
2716
  });
3368
2717
  ensureDir(targetDir);
3369
- node_fs.copyFileSync(node_path.join(srcPkgJsonDir, "package.json"), node_path.join(targetDir, "package.json"));
2718
+ await node_fs.promises.copyFile(node_path.join(srcPkgJsonDir, "package.json"), node_path.join(targetDir, "package.json"));
3370
2719
  const distSrc = node_path.join(srcPkgJsonDir, "dist");
3371
- if (node_fs.existsSync(distSrc)) copyDirRecursive(distSrc, node_path.join(targetDir, "dist"));
2720
+ if (node_fs.existsSync(distSrc)) await copyDirRecursive(distSrc, node_path.join(targetDir, "dist"));
3372
2721
  try {
3373
- const npmPkg = (0, _camstack_types.asJsonObject)((0, _camstack_types.parseJsonUnknown)(node_fs.readFileSync(node_path.join(srcPkgJsonDir, "package.json"), "utf-8")));
3374
- if (npmPkg) copyExtraFileDirs(npmPkg, srcPkgJsonDir, targetDir);
2722
+ const npmPkg = (0, _camstack_types.asJsonObject)((0, _camstack_types.parseJsonUnknown)(await node_fs.promises.readFile(node_path.join(srcPkgJsonDir, "package.json"), "utf-8")));
2723
+ if (npmPkg) await copyExtraFileDirs(npmPkg, srcPkgJsonDir, targetDir);
3375
2724
  } catch {}
3376
2725
  } finally {
3377
- node_fs.rmSync(tmpDir, {
2726
+ await node_fs.promises.rm(tmpDir, {
3378
2727
  recursive: true,
3379
2728
  force: true
3380
2729
  });
@@ -3824,15 +3173,15 @@ var AddonInstaller = class AddonInstaller {
3824
3173
  await this.ensureBuilt(packageName, sourceDir, pkgData);
3825
3174
  const distDir = node_path.join(sourceDir, "dist");
3826
3175
  if (!node_fs.existsSync(distDir)) throw new Error(`${packageName} has no dist/ after build`);
3827
- node_fs.rmSync(targetDir, {
3176
+ await node_fs.promises.rm(targetDir, {
3828
3177
  recursive: true,
3829
3178
  force: true
3830
3179
  });
3831
3180
  ensureDir(targetDir);
3832
- node_fs.writeFileSync(node_path.join(targetDir, "package.json"), JSON.stringify(stripCamstackDeps(pkgData), null, 2));
3833
- copyDirRecursive(distDir, node_path.join(targetDir, "dist"));
3834
- copyExtraFileDirs(pkgData, sourceDir, targetDir);
3835
- node_fs.writeFileSync(node_path.join(targetDir, ".install-source"), "local");
3181
+ await node_fs.promises.writeFile(node_path.join(targetDir, "package.json"), JSON.stringify(stripCamstackDeps(pkgData), null, 2));
3182
+ await copyDirRecursive(distDir, node_path.join(targetDir, "dist"));
3183
+ await copyExtraFileDirs(pkgData, sourceDir, targetDir);
3184
+ await node_fs.promises.writeFile(node_path.join(targetDir, ".install-source"), "local");
3836
3185
  const localPkgVersion = (0, _camstack_types.asString)(pkgData.version, "0.0.0");
3837
3186
  this.manifest.upsert(packageName, {
3838
3187
  version: localPkgVersion,
@@ -3952,17 +3301,17 @@ var AddonInstaller = class AddonInstaller {
3952
3301
  if (!pkgView) throw new Error(`Invalid package.json at ${pkgJsonPath}`);
3953
3302
  if (!pkgView.camstackAddons) throw new Error(`Package ${pkgView.name} has no camstack.addons manifest`);
3954
3303
  const targetDir = node_path.join(this.addonsDir, pkgView.name);
3955
- node_fs.rmSync(targetDir, {
3304
+ await node_fs.promises.rm(targetDir, {
3956
3305
  recursive: true,
3957
3306
  force: true
3958
3307
  });
3959
3308
  ensureDir(targetDir);
3960
3309
  const sourceDir = node_path.dirname(pkgJsonPath);
3961
3310
  const strippedManifest = stripCamstackDeps(pkgView.raw);
3962
- node_fs.writeFileSync(node_path.join(targetDir, "package.json"), JSON.stringify(strippedManifest, null, 2));
3311
+ await node_fs.promises.writeFile(node_path.join(targetDir, "package.json"), JSON.stringify(strippedManifest, null, 2));
3963
3312
  const sourceDist = node_path.join(sourceDir, "dist");
3964
- if (node_fs.existsSync(sourceDist)) copyDirRecursive(sourceDist, node_path.join(targetDir, "dist"));
3965
- copyExtraFileDirs(pkgView.raw, sourceDir, targetDir);
3313
+ if (node_fs.existsSync(sourceDist)) await copyDirRecursive(sourceDist, node_path.join(targetDir, "dist"));
3314
+ await copyExtraFileDirs(pkgView.raw, sourceDir, targetDir);
3966
3315
  const strippedRuntimeDeps = strippedManifest["dependencies"];
3967
3316
  if (strippedRuntimeDeps != null && typeof strippedRuntimeDeps === "object" && Object.keys(strippedRuntimeDeps).length > 0) {
3968
3317
  this.logger.info(`${pkgView.name} — installing runtime dependencies`, { meta: { targetDir } });
@@ -3986,7 +3335,7 @@ var AddonInstaller = class AddonInstaller {
3986
3335
  try {
3987
3336
  await require_manifest_python_deps.installManifestNativeDeps(targetDir, pkgView.raw, this.logger, this.registry);
3988
3337
  } catch (nativeErr) {
3989
- node_fs.rmSync(targetDir, {
3338
+ await node_fs.promises.rm(targetDir, {
3990
3339
  recursive: true,
3991
3340
  force: true
3992
3341
  });
@@ -4001,7 +3350,7 @@ var AddonInstaller = class AddonInstaller {
4001
3350
  version: pkgView.version
4002
3351
  };
4003
3352
  } finally {
4004
- node_fs.rmSync(tmpDir, {
3353
+ await node_fs.promises.rm(tmpDir, {
4005
3354
  recursive: true,
4006
3355
  force: true
4007
3356
  });
@@ -4769,7 +4118,7 @@ var CapabilityRegistry = class CapabilityRegistry {
4769
4118
  const bare = this.bareAddonId(addonId);
4770
4119
  const nodeMap = this.singletonNodeOverrides.get(capabilityName);
4771
4120
  if (nodeMap) {
4772
- for (const [nodeId, ov] of [...nodeMap]) if (ov === bare) nodeMap.delete(nodeId);
4121
+ for (const [nodeId, ov] of Array.from(nodeMap)) if (ov === bare) nodeMap.delete(nodeId);
4773
4122
  if (nodeMap.size === 0) this.singletonNodeOverrides.delete(capabilityName);
4774
4123
  }
4775
4124
  this.logger.info("Provider unregistered from capability", {
@@ -8031,7 +7380,7 @@ var ConfigManager = class ConfigManager {
8031
7380
  constructor(configPath) {
8032
7381
  this.configPath = configPath;
8033
7382
  const rawYaml = this.loadYaml();
8034
- const merged = this.applyEnvOverrides((0, _camstack_types.asJsonObject)(rawYaml) ?? {});
7383
+ const merged = this.applyEnvOverrides((0, _camstack_types_addon.asJsonObject)(rawYaml) ?? {});
8035
7384
  this.bootstrapConfig = bootstrapSchema.parse(merged);
8036
7385
  this.warnDefaultCredentials();
8037
7386
  const dataPath = this.bootstrapConfig.server.dataPath ?? "camstack-data";
@@ -8046,20 +7395,20 @@ var ConfigManager = class ConfigManager {
8046
7395
  setSettingsStore(store) {
8047
7396
  this.settingsStore = store;
8048
7397
  }
8049
- get(path) {
8050
- return this.resolveConfigValue(path);
7398
+ get(configPath) {
7399
+ return this.resolveConfigValue(configPath);
8051
7400
  }
8052
- resolveConfigValue(path) {
8053
- const bootstrapValue = this.getFromBootstrap(path);
7401
+ resolveConfigValue(configPath) {
7402
+ const bootstrapValue = this.getFromBootstrap(configPath);
8054
7403
  if (bootstrapValue !== void 0) return bootstrapValue;
8055
7404
  if (this.settingsStore !== null) {
8056
- const storeValue = this.settingsStore.getSystem(path);
7405
+ const storeValue = this.settingsStore.getSystem(configPath);
8057
7406
  if (storeValue !== void 0) return storeValue;
8058
- const nested = this.getNestedFromSystemSettings(path);
7407
+ const nested = this.getNestedFromSystemSettings(configPath);
8059
7408
  if (nested !== null) return nested;
8060
7409
  }
8061
- if (path in _camstack_types.RUNTIME_DEFAULTS) return _camstack_types.RUNTIME_DEFAULTS[path];
8062
- return this.getFromRuntimeDefaults(path) ?? void 0;
7410
+ if (configPath in _camstack_types.RUNTIME_DEFAULTS) return _camstack_types.RUNTIME_DEFAULTS[configPath];
7411
+ return this.getFromRuntimeDefaults(configPath) ?? void 0;
8063
7412
  }
8064
7413
  /**
8065
7414
  * Write a value to the settings-store.
@@ -8086,9 +7435,9 @@ var ConfigManager = class ConfigManager {
8086
7435
  */
8087
7436
  getSection(section) {
8088
7437
  const merged = {};
8089
- const defaults = (0, _camstack_types.asJsonObject)(this.getFromRuntimeDefaults(section));
7438
+ const defaults = (0, _camstack_types_addon.asJsonObject)(this.getFromRuntimeDefaults(section));
8090
7439
  if (defaults) Object.assign(merged, defaults);
8091
- const bootstrapSection = (0, _camstack_types.asJsonObject)({ ...this.bootstrapConfig }[section]);
7440
+ const bootstrapSection = (0, _camstack_types_addon.asJsonObject)({ ...this.bootstrapConfig }[section]);
8092
7441
  if (bootstrapSection) Object.assign(merged, bootstrapSection);
8093
7442
  if (this.settingsStore !== null) {
8094
7443
  const nested = this.getNestedFromSystemSettings(section);
@@ -8107,7 +7456,7 @@ var ConfigManager = class ConfigManager {
8107
7456
  /** Read all config for an addon from addon_settings. */
8108
7457
  getAddonConfig(addonId) {
8109
7458
  if (this.settingsStore !== null) return this.settingsStore.getAllAddon(addonId);
8110
- return (0, _camstack_types.asJsonObject)(this.getFromBootstrap(`addons.${addonId}`)) ?? {};
7459
+ return (0, _camstack_types_addon.asJsonObject)(this.getFromBootstrap(`addons.${addonId}`)) ?? {};
8111
7460
  }
8112
7461
  /** Write (bulk-replace) config for an addon to addon_settings. */
8113
7462
  setAddonConfig(addonId, config) {
@@ -8275,8 +7624,8 @@ var ConfigManager = class ConfigManager {
8275
7624
  };
8276
7625
  this.saveRuntimeState();
8277
7626
  }
8278
- getBootstrap(path) {
8279
- return this.getFromBootstrap(path);
7627
+ getBootstrap(configPath) {
7628
+ return this.getFromBootstrap(configPath);
8280
7629
  }
8281
7630
  /** Features accessor -- reads from settings-store when available, falls back to RUNTIME_DEFAULTS */
8282
7631
  get features() {
@@ -8345,8 +7694,8 @@ var ConfigManager = class ConfigManager {
8345
7694
  update(section, data) {
8346
7695
  if (!ConfigManager.BOOTSTRAP_SECTIONS.has(section)) throw new Error(`[ConfigManager] Section "${section}" is a runtime setting — use setSection() to persist via the settings-store, not update() which writes to config.yaml`);
8347
7696
  let raw = {};
8348
- if (node_fs.existsSync(this.configPath)) raw = (0, _camstack_types.asJsonObject)(load$1(node_fs.readFileSync(this.configPath, "utf-8"))) ?? {};
8349
- const existing = (0, _camstack_types.asJsonObject)(raw[section]) ?? {};
7697
+ if (node_fs.existsSync(this.configPath)) raw = (0, _camstack_types_addon.asJsonObject)(load$1(node_fs.readFileSync(this.configPath, "utf-8"))) ?? {};
7698
+ const existing = (0, _camstack_types_addon.asJsonObject)(raw[section]) ?? {};
8350
7699
  raw[section] = {
8351
7700
  ...existing,
8352
7701
  ...data
@@ -8366,8 +7715,8 @@ var ConfigManager = class ConfigManager {
8366
7715
  * Deep-set a value in a nested plain object using a dot-notation path.
8367
7716
  * Returns a new object (immutable).
8368
7717
  */
8369
- setNested(obj, path, value) {
8370
- const [head, ...rest] = path.split(".");
7718
+ setNested(obj, configPath, value) {
7719
+ const [head, ...rest] = configPath.split(".");
8371
7720
  if (!head) return obj;
8372
7721
  if (rest.length === 0) return {
8373
7722
  ...obj,
@@ -8407,8 +7756,8 @@ var ConfigManager = class ConfigManager {
8407
7756
  warnDefaultCredentials() {
8408
7757
  if (this.bootstrapConfig.auth.adminPassword === "changeme") console.warn("[ConfigManager] Warning: Using default admin password \"changeme\". Set auth.adminPassword in your config.yaml or the CAMSTACK_ADMIN_PASS env var.");
8409
7758
  }
8410
- getFromBootstrap(path) {
8411
- const keys = path.split(".");
7759
+ getFromBootstrap(configPath) {
7760
+ const keys = configPath.split(".");
8412
7761
  let current = this.bootstrapConfig;
8413
7762
  for (const key of keys) {
8414
7763
  if (!isRecord(current)) return void 0;
@@ -8416,8 +7765,8 @@ var ConfigManager = class ConfigManager {
8416
7765
  }
8417
7766
  return current;
8418
7767
  }
8419
- getFromRuntimeDefaults(path) {
8420
- const prefix = path + ".";
7768
+ getFromRuntimeDefaults(configPath) {
7769
+ const prefix = configPath + ".";
8421
7770
  const result = {};
8422
7771
  let found = false;
8423
7772
  for (const [key, value] of Object.entries(_camstack_types.RUNTIME_DEFAULTS)) if (key.startsWith(prefix)) {
@@ -8432,10 +7781,10 @@ var ConfigManager = class ConfigManager {
8432
7781
  * e.g. path='features' matches keys 'features.streaming', 'features.notifications', etc.
8433
7782
  * Returns an object keyed by the sub-key, or undefined if nothing is found.
8434
7783
  */
8435
- getNestedFromSystemSettings(path) {
7784
+ getNestedFromSystemSettings(configPath) {
8436
7785
  if (this.settingsStore === null) return null;
8437
7786
  const all = this.settingsStore.getAllSystem();
8438
- const prefix = path + ".";
7787
+ const prefix = configPath + ".";
8439
7788
  const result = {};
8440
7789
  let found = false;
8441
7790
  for (const [key, value] of Object.entries(all)) if (key.startsWith(prefix)) {
@@ -8447,13 +7796,13 @@ var ConfigManager = class ConfigManager {
8447
7796
  loadRuntimeState() {
8448
7797
  if (!node_fs.existsSync(this.runtimeStatePath)) return EMPTY_RUNTIME_STATE;
8449
7798
  try {
8450
- const parsed = (0, _camstack_types.asJsonObject)((0, _camstack_types.parseJsonUnknown)(node_fs.readFileSync(this.runtimeStatePath, "utf-8")));
7799
+ const parsed = (0, _camstack_types_addon.asJsonObject)((0, _camstack_types_addon.parseJsonUnknown)(node_fs.readFileSync(this.runtimeStatePath, "utf-8")));
8451
7800
  if (parsed === null) return EMPTY_RUNTIME_STATE;
8452
- const systemActivation = (0, _camstack_types.asJsonObject)(parsed.systemActivation) ?? {};
8453
- const deviceActivationRaw = (0, _camstack_types.asJsonObject)(parsed.deviceActivation) ?? {};
7801
+ const systemActivation = (0, _camstack_types_addon.asJsonObject)(parsed.systemActivation) ?? {};
7802
+ const deviceActivationRaw = (0, _camstack_types_addon.asJsonObject)(parsed.deviceActivation) ?? {};
8454
7803
  const deviceActivation = {};
8455
7804
  for (const [deviceId, entry] of Object.entries(deviceActivationRaw)) {
8456
- const nested = (0, _camstack_types.asJsonObject)(entry);
7805
+ const nested = (0, _camstack_types_addon.asJsonObject)(entry);
8457
7806
  if (nested === null) continue;
8458
7807
  const bools = {};
8459
7808
  for (const [k, v] of Object.entries(nested)) if (typeof v === "boolean") bools[k] = v;
@@ -28768,8 +28117,8 @@ var require_utils$3 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, mod
28768
28117
  * @param {*} value
28769
28118
  * @returns {Object}
28770
28119
  */
28771
- dotSet(obj, path$45, value) {
28772
- const parts = path$45.split(".");
28120
+ dotSet(obj, path$44, value) {
28121
+ const parts = path$44.split(".");
28773
28122
  const part = parts.shift();
28774
28123
  if (part && parts.length > 0) {
28775
28124
  if (!Object.prototype.hasOwnProperty.call(obj, part)) obj[part] = {};
@@ -28778,7 +28127,7 @@ var require_utils$3 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, mod
28778
28127
  obj[part] = utils.dotSet(obj[part], parts.join("."), value);
28779
28128
  return obj;
28780
28129
  }
28781
- obj[path$45] = value;
28130
+ obj[path$44] = value;
28782
28131
  return obj;
28783
28132
  },
28784
28133
  /**
@@ -42944,8 +42293,8 @@ var require_utimes = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, modu
42944
42293
  else if (timestamp instanceof Date) return /* @__PURE__ */ new Date(Math.floor(timestamp.getTime() / 1e3) * 1e3);
42945
42294
  else throw new Error("fs-extra: timeRemoveMillis() unknown parameter type");
42946
42295
  }
42947
- function utimesMillis(path$43, atime, mtime, callback) {
42948
- fs.open(path$43, "r+", (err, fd) => {
42296
+ function utimesMillis(path$42, atime, mtime, callback) {
42297
+ fs.open(path$42, "r+", (err, fd) => {
42949
42298
  if (err) return callback(err);
42950
42299
  fs.futimes(fd, atime, mtime, (futimesErr) => {
42951
42300
  fs.close(fd, (closeErr) => {
@@ -42954,8 +42303,8 @@ var require_utimes = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, modu
42954
42303
  });
42955
42304
  });
42956
42305
  }
42957
- function utimesMillisSync(path$44, atime, mtime) {
42958
- const fd = fs.openSync(path$44, "r+");
42306
+ function utimesMillisSync(path$43, atime, mtime) {
42307
+ const fd = fs.openSync(path$43, "r+");
42959
42308
  fs.futimesSync(fd, atime, mtime);
42960
42309
  return fs.closeSync(fd);
42961
42310
  }
@@ -43958,10 +43307,10 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
43958
43307
  }
43959
43308
  if (typeof options === "string") options = { encoding: options };
43960
43309
  options = options || {};
43961
- var fs$21 = options.fs || _fs;
43310
+ var fs$19 = options.fs || _fs;
43962
43311
  var shouldThrow = true;
43963
43312
  if ("throws" in options) shouldThrow = options.throws;
43964
- fs$21.readFile(file, options, function(err, data) {
43313
+ fs$19.readFile(file, options, function(err, data) {
43965
43314
  if (err) return callback(err);
43966
43315
  data = stripBom(data);
43967
43316
  var obj;
@@ -43979,11 +43328,11 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
43979
43328
  function readFileSync(file, options) {
43980
43329
  options = options || {};
43981
43330
  if (typeof options === "string") options = { encoding: options };
43982
- var fs$22 = options.fs || _fs;
43331
+ var fs$20 = options.fs || _fs;
43983
43332
  var shouldThrow = true;
43984
43333
  if ("throws" in options) shouldThrow = options.throws;
43985
43334
  try {
43986
- var content = fs$22.readFileSync(file, options);
43335
+ var content = fs$20.readFileSync(file, options);
43987
43336
  content = stripBom(content);
43988
43337
  return JSON.parse(content, options.reviver);
43989
43338
  } catch (err) {
@@ -44008,7 +43357,7 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
44008
43357
  options = {};
44009
43358
  }
44010
43359
  options = options || {};
44011
- var fs$23 = options.fs || _fs;
43360
+ var fs$21 = options.fs || _fs;
44012
43361
  var str = "";
44013
43362
  try {
44014
43363
  str = stringify(obj, options);
@@ -44016,13 +43365,13 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
44016
43365
  if (callback) callback(err, null);
44017
43366
  return;
44018
43367
  }
44019
- fs$23.writeFile(file, str, options, callback);
43368
+ fs$21.writeFile(file, str, options, callback);
44020
43369
  }
44021
43370
  function writeFileSync(file, obj, options) {
44022
43371
  options = options || {};
44023
- var fs$24 = options.fs || _fs;
43372
+ var fs$22 = options.fs || _fs;
44024
43373
  var str = stringify(obj, options);
44025
- return fs$24.writeFileSync(file, str, options);
43374
+ return fs$22.writeFileSync(file, str, options);
44026
43375
  }
44027
43376
  function stripBom(content) {
44028
43377
  if (Buffer.isBuffer(content)) content = content.toString("utf8");
@@ -86620,11 +85969,11 @@ var require_main = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module
86620
85969
  }
86621
85970
  let lastError;
86622
85971
  const parsedAll = {};
86623
- for (const path$42 of optionPaths) try {
86624
- const parsed = DotenvModule.parse(fs$4.readFileSync(path$42, { encoding }));
85972
+ for (const path$41 of optionPaths) try {
85973
+ const parsed = DotenvModule.parse(fs$4.readFileSync(path$41, { encoding }));
86625
85974
  DotenvModule.populate(parsedAll, parsed, options);
86626
85975
  } catch (e) {
86627
- if (debug) _debug(`failed to load ${path$42} ${e.message}`);
85976
+ if (debug) _debug(`failed to load ${path$41} ${e.message}`);
86628
85977
  lastError = e;
86629
85978
  }
86630
85979
  const populated = DotenvModule.populate(processEnv, parsedAll, options);
@@ -91596,7 +90945,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
91596
90945
  respawned.restartCount = prevRestartCount + 1;
91597
90946
  });
91598
90947
  restarted.push(name);
91599
- } catch (err) {
90948
+ } catch {
91600
90949
  failed.push(name);
91601
90950
  }
91602
90951
  return {
@@ -91991,8 +91340,8 @@ var LifecycleJobEngine = class {
91991
91340
  } finally {
91992
91341
  clearTimeout(timer);
91993
91342
  }
91994
- if (packages.length === 0) throw new Error("stageFramework returned no packages");
91995
91343
  const [firstPackage] = packages;
91344
+ if (!firstPackage) throw new Error("stageFramework returned no packages");
91996
91345
  this.advance(job.jobId, task, "staged", { stagedPath: firstPackage.stagedPath });
91997
91346
  requestFrameworkSwap({
91998
91347
  jobId: job.jobId,
@@ -92015,14 +91364,15 @@ var LifecycleJobEngine = class {
92015
91364
  */
92016
91365
  async fetchAddonsBounded(job, addonTasks) {
92017
91366
  const limit = Math.max(1, this.deps.fetchConcurrency ?? DEFAULT_FETCH_CONCURRENCY);
92018
- const outcomes = new Array(addonTasks.length);
91367
+ const outcomes = Array.from({ length: addonTasks.length });
92019
91368
  let nextIndex = 0;
92020
91369
  const worker = async () => {
92021
91370
  for (;;) {
92022
91371
  const index = nextIndex;
92023
91372
  nextIndex += 1;
92024
- if (index >= addonTasks.length) return;
92025
- outcomes[index] = await this.fetchAddonTask(job, addonTasks[index]);
91373
+ const addonTask = addonTasks[index];
91374
+ if (addonTask === void 0) return;
91375
+ outcomes[index] = await this.fetchAddonTask(job, addonTask);
92026
91376
  }
92027
91377
  };
92028
91378
  const workerCount = Math.min(limit, addonTasks.length);
@@ -92174,7 +91524,7 @@ exports.ConfigManager = ConfigManager;
92174
91524
  exports.ConfigStore = require_builtins_sqlite_storage_index.ConfigStore$1;
92175
91525
  exports.ConsoleDestination = require_builtins_console_logging_index.ConsoleDestination$1;
92176
91526
  exports.ConsoleLoggingAddon = require_builtins_console_logging_index.ConsoleLoggingAddon$1;
92177
- exports.CustomActionRegistry = require_manifest_python_deps.CustomActionRegistry;
91527
+ exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
92178
91528
  exports.DEFAULT_DATA_PATH = DEFAULT_DATA_PATH;
92179
91529
  exports.DataPlaneRegistry = DataPlaneRegistry;
92180
91530
  exports.DeviceManagerAddon = require_builtins_device_manager_device_manager_addon.DeviceManagerAddon;
@@ -92206,7 +91556,7 @@ exports.LocalChildClient = require_manifest_python_deps.LocalChildClient;
92206
91556
  exports.LocalChildRegistry = require_manifest_python_deps.LocalChildRegistry;
92207
91557
  exports.LogManager = LogManager;
92208
91558
  exports.LogRingBuffer = LogRingBuffer;
92209
- exports.ModelDownloadService = ModelDownloadService;
91559
+ exports.ModelDownloadService = require_model_download_service.ModelDownloadService;
92210
91560
  exports.NATIVE_PROVIDER_SERVICE_INFIX = require_manifest_python_deps.NATIVE_PROVIDER_SERVICE_INFIX;
92211
91561
  exports.NativeMetricsAddon = require_builtins_native_metrics_native_metrics_addon.default;
92212
91562
  exports.NativeMetricsProvider = require_builtins_native_metrics_native_metrics_addon.NativeMetricsProvider;
@@ -92286,16 +91636,16 @@ exports.capServiceName = require_manifest_python_deps.capServiceName;
92286
91636
  exports.classifyCapRoute = require_manifest_python_deps.classifyCapRoute;
92287
91637
  exports.clearPendingRestart = clearPendingRestart;
92288
91638
  exports.clusterSecretMatches = clusterSecretMatches;
92289
- exports.contentTypeFor = contentTypeFor;
91639
+ exports.contentTypeFor = require_model_download_service.contentTypeFor;
92290
91640
  exports.copyDirRecursive = copyDirRecursive;
92291
91641
  exports.copyExtraFileDirs = copyExtraFileDirs;
92292
91642
  exports.createAddonContext = require_manifest_python_deps.createAddonContext;
92293
91643
  exports.createAddonService = require_manifest_python_deps.createAddonService;
92294
- exports.createAuthenticatedFileServer = createAuthenticatedFileServer;
91644
+ exports.createAuthenticatedFileServer = require_model_download_service.createAuthenticatedFileServer;
92295
91645
  exports.createBroker = createBroker;
92296
91646
  exports.createBrokerDeviceManagerApi = require_manifest_python_deps.createBrokerDeviceManagerApi;
92297
91647
  exports.createCoreCapService = createCoreCapService;
92298
- exports.createFileDataPlaneHandler = createFileDataPlaneHandler;
91648
+ exports.createFileDataPlaneHandler = require_model_download_service.createFileDataPlaneHandler;
92299
91649
  exports.createHubService = createHubService;
92300
91650
  exports.createHwAccelService = require_manifest_python_deps.createHwAccelService;
92301
91651
  exports.createKernelHwAccel = require_manifest_python_deps.createKernelHwAccel;
@@ -92311,7 +91661,7 @@ exports.createUdsEventBridge = require_manifest_python_deps.createUdsEventBridge
92311
91661
  exports.createUdsEventBus = require_manifest_python_deps.createUdsEventBus;
92312
91662
  exports.createUdsLogger = require_manifest_python_deps.createUdsLogger;
92313
91663
  exports.createUdsLoggerWithControl = require_manifest_python_deps.createUdsLoggerWithControl;
92314
- exports.deleteModelFromDisk = deleteModelFromDisk;
91664
+ exports.deleteModelFromDisk = require_model_download_service.deleteModelFromDisk;
92315
91665
  exports.deriveAgentListenPort = deriveAgentListenPort;
92316
91666
  exports.describeProviderKindDrift = describeProviderKindDrift;
92317
91667
  exports.detectWorkspacePackagesDir = detectWorkspacePackagesDir;
@@ -92321,8 +91671,8 @@ Object.defineProperty(exports, "downloadBinary", {
92321
91671
  return _camstack_types_node.downloadBinary;
92322
91672
  }
92323
91673
  });
92324
- exports.downloadFile = downloadFile;
92325
- exports.downloadModel = downloadModel;
91674
+ exports.downloadFile = require_model_download_service.downloadFile;
91675
+ exports.downloadModel = require_model_download_service.downloadModel;
92326
91676
  Object.defineProperty(exports, "emitDownForOwnedCaps", {
92327
91677
  enumerable: true,
92328
91678
  get: function() {
@@ -92344,7 +91694,7 @@ Object.defineProperty(exports, "ensureFfmpeg", {
92344
91694
  }
92345
91695
  });
92346
91696
  exports.ensureLibraryBuilt = ensureLibraryBuilt;
92347
- exports.ensureModel = ensureModel;
91697
+ exports.ensureModel = require_model_download_service.ensureModel;
92348
91698
  Object.defineProperty(exports, "ensurePython", {
92349
91699
  enumerable: true,
92350
91700
  get: function() {
@@ -92352,7 +91702,7 @@ Object.defineProperty(exports, "ensurePython", {
92352
91702
  }
92353
91703
  });
92354
91704
  exports.ensureTlsCert = ensureTlsCert;
92355
- exports.fetchJson = fetchJson;
91705
+ exports.fetchJson = require_model_download_service.fetchJson;
92356
91706
  Object.defineProperty(exports, "findInPath", {
92357
91707
  enumerable: true,
92358
91708
  get: function() {
@@ -92368,7 +91718,7 @@ Object.defineProperty(exports, "getFfmpegDownloadUrl", {
92368
91718
  return _camstack_types_node.getFfmpegDownloadUrl;
92369
91719
  }
92370
91720
  });
92371
- exports.getModelFilePath = getModelFilePath;
91721
+ exports.getModelFilePath = require_model_download_service.getModelFilePath;
92372
91722
  exports.getOrInitReadinessRegistry = require_manifest_python_deps.getOrInitReadinessRegistry;
92373
91723
  exports.getOrInitReadinessRegistryForClient = require_manifest_python_deps.getOrInitReadinessRegistryForClient;
92374
91724
  exports.getPidStats = require_resource_monitor.getPidStats;
@@ -92390,7 +91740,7 @@ exports.getWorkerDeviceRegistry = require_manifest_python_deps.getWorkerDeviceRe
92390
91740
  exports.hashClusterSecret = hashClusterSecret;
92391
91741
  exports.installManifestNativeDeps = require_manifest_python_deps.installManifestNativeDeps;
92392
91742
  exports.installManifestPythonDeps = require_manifest_python_deps.installManifestPythonDeps;
92393
- exports.installPackageFromNpmSync = installPackageFromNpmSync;
91743
+ exports.installPackageFromNpm = installPackageFromNpm;
92394
91744
  Object.defineProperty(exports, "installPythonPackages", {
92395
91745
  enumerable: true,
92396
91746
  get: function() {
@@ -92407,15 +91757,15 @@ exports.ipcChildLink = require_manifest_python_deps.ipcChildLink;
92407
91757
  exports.ipcParentLink = require_manifest_python_deps.ipcParentLink;
92408
91758
  exports.isClusterSecretMismatchError = isClusterSecretMismatchError;
92409
91759
  exports.isInfraCapability = isInfraCapability;
92410
- exports.isModelDownloaded = isModelDownloaded;
91760
+ exports.isModelDownloaded = require_model_download_service.isModelDownloaded;
92411
91761
  exports.isSourceNewer = isSourceNewer;
92412
91762
  exports.loadTlsCert = loadTlsCert;
92413
91763
  exports.localEndpointPath = require_manifest_python_deps.localEndpointPath;
92414
91764
  exports.localProviderLink = require_manifest_python_deps.localProviderLink;
92415
91765
  exports.mountNativeCapService = require_manifest_python_deps.mountNativeCapService;
92416
91766
  exports.parseCapAction = require_manifest_python_deps.parseCapAction;
92417
- exports.parseRangeHeader = parseRangeHeader;
92418
- exports.parseTokenizedUrl = parseTokenizedUrl;
91767
+ exports.parseRangeHeader = require_model_download_service.parseRangeHeader;
91768
+ exports.parseTokenizedUrl = require_model_download_service.parseTokenizedUrl;
92419
91769
  exports.proxyToUpstream = proxyToUpstream;
92420
91770
  exports.readPendingRestart = readPendingRestart;
92421
91771
  Object.defineProperty(exports, "readinessKey", {
@@ -92425,7 +91775,7 @@ Object.defineProperty(exports, "readinessKey", {
92425
91775
  }
92426
91776
  });
92427
91777
  exports.registerEventBusService = require_manifest_python_deps.registerEventBusService;
92428
- exports.resolveFilePath = resolveFilePath;
91778
+ exports.resolveFilePath = require_model_download_service.resolveFilePath;
92429
91779
  exports.resolveHwAccel = require_manifest_python_deps.resolveHwAccel;
92430
91780
  exports.scheduleSelfRestart = scheduleSelfRestart;
92431
91781
  Object.defineProperty(exports, "scopeKey", {