@camstack/system 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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-eBDj5HEY.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,457 +182,6 @@ 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
186
  var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
845
187
  var PythonEnvManager = class {
@@ -8031,7 +7373,7 @@ var ConfigManager = class ConfigManager {
8031
7373
  constructor(configPath) {
8032
7374
  this.configPath = configPath;
8033
7375
  const rawYaml = this.loadYaml();
8034
- const merged = this.applyEnvOverrides((0, _camstack_types.asJsonObject)(rawYaml) ?? {});
7376
+ const merged = this.applyEnvOverrides((0, _camstack_types_addon.asJsonObject)(rawYaml) ?? {});
8035
7377
  this.bootstrapConfig = bootstrapSchema.parse(merged);
8036
7378
  this.warnDefaultCredentials();
8037
7379
  const dataPath = this.bootstrapConfig.server.dataPath ?? "camstack-data";
@@ -8086,9 +7428,9 @@ var ConfigManager = class ConfigManager {
8086
7428
  */
8087
7429
  getSection(section) {
8088
7430
  const merged = {};
8089
- const defaults = (0, _camstack_types.asJsonObject)(this.getFromRuntimeDefaults(section));
7431
+ const defaults = (0, _camstack_types_addon.asJsonObject)(this.getFromRuntimeDefaults(section));
8090
7432
  if (defaults) Object.assign(merged, defaults);
8091
- const bootstrapSection = (0, _camstack_types.asJsonObject)({ ...this.bootstrapConfig }[section]);
7433
+ const bootstrapSection = (0, _camstack_types_addon.asJsonObject)({ ...this.bootstrapConfig }[section]);
8092
7434
  if (bootstrapSection) Object.assign(merged, bootstrapSection);
8093
7435
  if (this.settingsStore !== null) {
8094
7436
  const nested = this.getNestedFromSystemSettings(section);
@@ -8107,7 +7449,7 @@ var ConfigManager = class ConfigManager {
8107
7449
  /** Read all config for an addon from addon_settings. */
8108
7450
  getAddonConfig(addonId) {
8109
7451
  if (this.settingsStore !== null) return this.settingsStore.getAllAddon(addonId);
8110
- return (0, _camstack_types.asJsonObject)(this.getFromBootstrap(`addons.${addonId}`)) ?? {};
7452
+ return (0, _camstack_types_addon.asJsonObject)(this.getFromBootstrap(`addons.${addonId}`)) ?? {};
8111
7453
  }
8112
7454
  /** Write (bulk-replace) config for an addon to addon_settings. */
8113
7455
  setAddonConfig(addonId, config) {
@@ -8345,8 +7687,8 @@ var ConfigManager = class ConfigManager {
8345
7687
  update(section, data) {
8346
7688
  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
7689
  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]) ?? {};
7690
+ if (node_fs.existsSync(this.configPath)) raw = (0, _camstack_types_addon.asJsonObject)(load$1(node_fs.readFileSync(this.configPath, "utf-8"))) ?? {};
7691
+ const existing = (0, _camstack_types_addon.asJsonObject)(raw[section]) ?? {};
8350
7692
  raw[section] = {
8351
7693
  ...existing,
8352
7694
  ...data
@@ -8447,13 +7789,13 @@ var ConfigManager = class ConfigManager {
8447
7789
  loadRuntimeState() {
8448
7790
  if (!node_fs.existsSync(this.runtimeStatePath)) return EMPTY_RUNTIME_STATE;
8449
7791
  try {
8450
- const parsed = (0, _camstack_types.asJsonObject)((0, _camstack_types.parseJsonUnknown)(node_fs.readFileSync(this.runtimeStatePath, "utf-8")));
7792
+ const parsed = (0, _camstack_types_addon.asJsonObject)((0, _camstack_types_addon.parseJsonUnknown)(node_fs.readFileSync(this.runtimeStatePath, "utf-8")));
8451
7793
  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) ?? {};
7794
+ const systemActivation = (0, _camstack_types_addon.asJsonObject)(parsed.systemActivation) ?? {};
7795
+ const deviceActivationRaw = (0, _camstack_types_addon.asJsonObject)(parsed.deviceActivation) ?? {};
8454
7796
  const deviceActivation = {};
8455
7797
  for (const [deviceId, entry] of Object.entries(deviceActivationRaw)) {
8456
- const nested = (0, _camstack_types.asJsonObject)(entry);
7798
+ const nested = (0, _camstack_types_addon.asJsonObject)(entry);
8457
7799
  if (nested === null) continue;
8458
7800
  const bools = {};
8459
7801
  for (const [k, v] of Object.entries(nested)) if (typeof v === "boolean") bools[k] = v;
@@ -28768,8 +28110,8 @@ var require_utils$3 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, mod
28768
28110
  * @param {*} value
28769
28111
  * @returns {Object}
28770
28112
  */
28771
- dotSet(obj, path$45, value) {
28772
- const parts = path$45.split(".");
28113
+ dotSet(obj, path$44, value) {
28114
+ const parts = path$44.split(".");
28773
28115
  const part = parts.shift();
28774
28116
  if (part && parts.length > 0) {
28775
28117
  if (!Object.prototype.hasOwnProperty.call(obj, part)) obj[part] = {};
@@ -28778,7 +28120,7 @@ var require_utils$3 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, mod
28778
28120
  obj[part] = utils.dotSet(obj[part], parts.join("."), value);
28779
28121
  return obj;
28780
28122
  }
28781
- obj[path$45] = value;
28123
+ obj[path$44] = value;
28782
28124
  return obj;
28783
28125
  },
28784
28126
  /**
@@ -42944,8 +42286,8 @@ var require_utimes = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, modu
42944
42286
  else if (timestamp instanceof Date) return /* @__PURE__ */ new Date(Math.floor(timestamp.getTime() / 1e3) * 1e3);
42945
42287
  else throw new Error("fs-extra: timeRemoveMillis() unknown parameter type");
42946
42288
  }
42947
- function utimesMillis(path$43, atime, mtime, callback) {
42948
- fs.open(path$43, "r+", (err, fd) => {
42289
+ function utimesMillis(path$42, atime, mtime, callback) {
42290
+ fs.open(path$42, "r+", (err, fd) => {
42949
42291
  if (err) return callback(err);
42950
42292
  fs.futimes(fd, atime, mtime, (futimesErr) => {
42951
42293
  fs.close(fd, (closeErr) => {
@@ -42954,8 +42296,8 @@ var require_utimes = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, modu
42954
42296
  });
42955
42297
  });
42956
42298
  }
42957
- function utimesMillisSync(path$44, atime, mtime) {
42958
- const fd = fs.openSync(path$44, "r+");
42299
+ function utimesMillisSync(path$43, atime, mtime) {
42300
+ const fd = fs.openSync(path$43, "r+");
42959
42301
  fs.futimesSync(fd, atime, mtime);
42960
42302
  return fs.closeSync(fd);
42961
42303
  }
@@ -43958,10 +43300,10 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
43958
43300
  }
43959
43301
  if (typeof options === "string") options = { encoding: options };
43960
43302
  options = options || {};
43961
- var fs$21 = options.fs || _fs;
43303
+ var fs$19 = options.fs || _fs;
43962
43304
  var shouldThrow = true;
43963
43305
  if ("throws" in options) shouldThrow = options.throws;
43964
- fs$21.readFile(file, options, function(err, data) {
43306
+ fs$19.readFile(file, options, function(err, data) {
43965
43307
  if (err) return callback(err);
43966
43308
  data = stripBom(data);
43967
43309
  var obj;
@@ -43979,11 +43321,11 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
43979
43321
  function readFileSync(file, options) {
43980
43322
  options = options || {};
43981
43323
  if (typeof options === "string") options = { encoding: options };
43982
- var fs$22 = options.fs || _fs;
43324
+ var fs$20 = options.fs || _fs;
43983
43325
  var shouldThrow = true;
43984
43326
  if ("throws" in options) shouldThrow = options.throws;
43985
43327
  try {
43986
- var content = fs$22.readFileSync(file, options);
43328
+ var content = fs$20.readFileSync(file, options);
43987
43329
  content = stripBom(content);
43988
43330
  return JSON.parse(content, options.reviver);
43989
43331
  } catch (err) {
@@ -44008,7 +43350,7 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
44008
43350
  options = {};
44009
43351
  }
44010
43352
  options = options || {};
44011
- var fs$23 = options.fs || _fs;
43353
+ var fs$21 = options.fs || _fs;
44012
43354
  var str = "";
44013
43355
  try {
44014
43356
  str = stringify(obj, options);
@@ -44016,13 +43358,13 @@ var require_jsonfile$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
44016
43358
  if (callback) callback(err, null);
44017
43359
  return;
44018
43360
  }
44019
- fs$23.writeFile(file, str, options, callback);
43361
+ fs$21.writeFile(file, str, options, callback);
44020
43362
  }
44021
43363
  function writeFileSync(file, obj, options) {
44022
43364
  options = options || {};
44023
- var fs$24 = options.fs || _fs;
43365
+ var fs$22 = options.fs || _fs;
44024
43366
  var str = stringify(obj, options);
44025
- return fs$24.writeFileSync(file, str, options);
43367
+ return fs$22.writeFileSync(file, str, options);
44026
43368
  }
44027
43369
  function stripBom(content) {
44028
43370
  if (Buffer.isBuffer(content)) content = content.toString("utf8");
@@ -86620,11 +85962,11 @@ var require_main = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module
86620
85962
  }
86621
85963
  let lastError;
86622
85964
  const parsedAll = {};
86623
- for (const path$42 of optionPaths) try {
86624
- const parsed = DotenvModule.parse(fs$4.readFileSync(path$42, { encoding }));
85965
+ for (const path$41 of optionPaths) try {
85966
+ const parsed = DotenvModule.parse(fs$4.readFileSync(path$41, { encoding }));
86625
85967
  DotenvModule.populate(parsedAll, parsed, options);
86626
85968
  } catch (e) {
86627
- if (debug) _debug(`failed to load ${path$42} ${e.message}`);
85969
+ if (debug) _debug(`failed to load ${path$41} ${e.message}`);
86628
85970
  lastError = e;
86629
85971
  }
86630
85972
  const populated = DotenvModule.populate(processEnv, parsedAll, options);
@@ -92174,7 +91516,7 @@ exports.ConfigManager = ConfigManager;
92174
91516
  exports.ConfigStore = require_builtins_sqlite_storage_index.ConfigStore$1;
92175
91517
  exports.ConsoleDestination = require_builtins_console_logging_index.ConsoleDestination$1;
92176
91518
  exports.ConsoleLoggingAddon = require_builtins_console_logging_index.ConsoleLoggingAddon$1;
92177
- exports.CustomActionRegistry = require_manifest_python_deps.CustomActionRegistry;
91519
+ exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
92178
91520
  exports.DEFAULT_DATA_PATH = DEFAULT_DATA_PATH;
92179
91521
  exports.DataPlaneRegistry = DataPlaneRegistry;
92180
91522
  exports.DeviceManagerAddon = require_builtins_device_manager_device_manager_addon.DeviceManagerAddon;
@@ -92206,7 +91548,7 @@ exports.LocalChildClient = require_manifest_python_deps.LocalChildClient;
92206
91548
  exports.LocalChildRegistry = require_manifest_python_deps.LocalChildRegistry;
92207
91549
  exports.LogManager = LogManager;
92208
91550
  exports.LogRingBuffer = LogRingBuffer;
92209
- exports.ModelDownloadService = ModelDownloadService;
91551
+ exports.ModelDownloadService = require_model_download_service.ModelDownloadService;
92210
91552
  exports.NATIVE_PROVIDER_SERVICE_INFIX = require_manifest_python_deps.NATIVE_PROVIDER_SERVICE_INFIX;
92211
91553
  exports.NativeMetricsAddon = require_builtins_native_metrics_native_metrics_addon.default;
92212
91554
  exports.NativeMetricsProvider = require_builtins_native_metrics_native_metrics_addon.NativeMetricsProvider;
@@ -92286,16 +91628,16 @@ exports.capServiceName = require_manifest_python_deps.capServiceName;
92286
91628
  exports.classifyCapRoute = require_manifest_python_deps.classifyCapRoute;
92287
91629
  exports.clearPendingRestart = clearPendingRestart;
92288
91630
  exports.clusterSecretMatches = clusterSecretMatches;
92289
- exports.contentTypeFor = contentTypeFor;
91631
+ exports.contentTypeFor = require_model_download_service.contentTypeFor;
92290
91632
  exports.copyDirRecursive = copyDirRecursive;
92291
91633
  exports.copyExtraFileDirs = copyExtraFileDirs;
92292
91634
  exports.createAddonContext = require_manifest_python_deps.createAddonContext;
92293
91635
  exports.createAddonService = require_manifest_python_deps.createAddonService;
92294
- exports.createAuthenticatedFileServer = createAuthenticatedFileServer;
91636
+ exports.createAuthenticatedFileServer = require_model_download_service.createAuthenticatedFileServer;
92295
91637
  exports.createBroker = createBroker;
92296
91638
  exports.createBrokerDeviceManagerApi = require_manifest_python_deps.createBrokerDeviceManagerApi;
92297
91639
  exports.createCoreCapService = createCoreCapService;
92298
- exports.createFileDataPlaneHandler = createFileDataPlaneHandler;
91640
+ exports.createFileDataPlaneHandler = require_model_download_service.createFileDataPlaneHandler;
92299
91641
  exports.createHubService = createHubService;
92300
91642
  exports.createHwAccelService = require_manifest_python_deps.createHwAccelService;
92301
91643
  exports.createKernelHwAccel = require_manifest_python_deps.createKernelHwAccel;
@@ -92311,7 +91653,7 @@ exports.createUdsEventBridge = require_manifest_python_deps.createUdsEventBridge
92311
91653
  exports.createUdsEventBus = require_manifest_python_deps.createUdsEventBus;
92312
91654
  exports.createUdsLogger = require_manifest_python_deps.createUdsLogger;
92313
91655
  exports.createUdsLoggerWithControl = require_manifest_python_deps.createUdsLoggerWithControl;
92314
- exports.deleteModelFromDisk = deleteModelFromDisk;
91656
+ exports.deleteModelFromDisk = require_model_download_service.deleteModelFromDisk;
92315
91657
  exports.deriveAgentListenPort = deriveAgentListenPort;
92316
91658
  exports.describeProviderKindDrift = describeProviderKindDrift;
92317
91659
  exports.detectWorkspacePackagesDir = detectWorkspacePackagesDir;
@@ -92321,8 +91663,8 @@ Object.defineProperty(exports, "downloadBinary", {
92321
91663
  return _camstack_types_node.downloadBinary;
92322
91664
  }
92323
91665
  });
92324
- exports.downloadFile = downloadFile;
92325
- exports.downloadModel = downloadModel;
91666
+ exports.downloadFile = require_model_download_service.downloadFile;
91667
+ exports.downloadModel = require_model_download_service.downloadModel;
92326
91668
  Object.defineProperty(exports, "emitDownForOwnedCaps", {
92327
91669
  enumerable: true,
92328
91670
  get: function() {
@@ -92344,7 +91686,7 @@ Object.defineProperty(exports, "ensureFfmpeg", {
92344
91686
  }
92345
91687
  });
92346
91688
  exports.ensureLibraryBuilt = ensureLibraryBuilt;
92347
- exports.ensureModel = ensureModel;
91689
+ exports.ensureModel = require_model_download_service.ensureModel;
92348
91690
  Object.defineProperty(exports, "ensurePython", {
92349
91691
  enumerable: true,
92350
91692
  get: function() {
@@ -92352,7 +91694,7 @@ Object.defineProperty(exports, "ensurePython", {
92352
91694
  }
92353
91695
  });
92354
91696
  exports.ensureTlsCert = ensureTlsCert;
92355
- exports.fetchJson = fetchJson;
91697
+ exports.fetchJson = require_model_download_service.fetchJson;
92356
91698
  Object.defineProperty(exports, "findInPath", {
92357
91699
  enumerable: true,
92358
91700
  get: function() {
@@ -92368,7 +91710,7 @@ Object.defineProperty(exports, "getFfmpegDownloadUrl", {
92368
91710
  return _camstack_types_node.getFfmpegDownloadUrl;
92369
91711
  }
92370
91712
  });
92371
- exports.getModelFilePath = getModelFilePath;
91713
+ exports.getModelFilePath = require_model_download_service.getModelFilePath;
92372
91714
  exports.getOrInitReadinessRegistry = require_manifest_python_deps.getOrInitReadinessRegistry;
92373
91715
  exports.getOrInitReadinessRegistryForClient = require_manifest_python_deps.getOrInitReadinessRegistryForClient;
92374
91716
  exports.getPidStats = require_resource_monitor.getPidStats;
@@ -92407,15 +91749,15 @@ exports.ipcChildLink = require_manifest_python_deps.ipcChildLink;
92407
91749
  exports.ipcParentLink = require_manifest_python_deps.ipcParentLink;
92408
91750
  exports.isClusterSecretMismatchError = isClusterSecretMismatchError;
92409
91751
  exports.isInfraCapability = isInfraCapability;
92410
- exports.isModelDownloaded = isModelDownloaded;
91752
+ exports.isModelDownloaded = require_model_download_service.isModelDownloaded;
92411
91753
  exports.isSourceNewer = isSourceNewer;
92412
91754
  exports.loadTlsCert = loadTlsCert;
92413
91755
  exports.localEndpointPath = require_manifest_python_deps.localEndpointPath;
92414
91756
  exports.localProviderLink = require_manifest_python_deps.localProviderLink;
92415
91757
  exports.mountNativeCapService = require_manifest_python_deps.mountNativeCapService;
92416
91758
  exports.parseCapAction = require_manifest_python_deps.parseCapAction;
92417
- exports.parseRangeHeader = parseRangeHeader;
92418
- exports.parseTokenizedUrl = parseTokenizedUrl;
91759
+ exports.parseRangeHeader = require_model_download_service.parseRangeHeader;
91760
+ exports.parseTokenizedUrl = require_model_download_service.parseTokenizedUrl;
92419
91761
  exports.proxyToUpstream = proxyToUpstream;
92420
91762
  exports.readPendingRestart = readPendingRestart;
92421
91763
  Object.defineProperty(exports, "readinessKey", {
@@ -92425,7 +91767,7 @@ Object.defineProperty(exports, "readinessKey", {
92425
91767
  }
92426
91768
  });
92427
91769
  exports.registerEventBusService = require_manifest_python_deps.registerEventBusService;
92428
- exports.resolveFilePath = resolveFilePath;
91770
+ exports.resolveFilePath = require_model_download_service.resolveFilePath;
92429
91771
  exports.resolveHwAccel = require_manifest_python_deps.resolveHwAccel;
92430
91772
  exports.scheduleSelfRestart = scheduleSelfRestart;
92431
91773
  Object.defineProperty(exports, "scopeKey", {