@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/addon-runner.js +38 -21
- package/dist/addon-runner.mjs +18 -2
- package/dist/addon-utils.d.ts +20 -0
- package/dist/addon-utils.js +11 -0
- package/dist/addon-utils.mjs +3 -0
- package/dist/builtins/native-metrics/native-metrics.addon.d.ts +8 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +50 -3
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +50 -3
- package/dist/custom-action-registry-BEXwC-oo.mjs +38 -0
- package/dist/custom-action-registry-vLYEFTtv.js +43 -0
- package/dist/index.js +48 -706
- package/dist/index.mjs +18 -676
- package/dist/kernel/moleculer/device-cap-proxy.d.ts +2 -1
- package/dist/kernel/moleculer/readiness-context.d.ts +2 -1
- package/dist/{manifest-python-deps-CXbKrOdk.mjs → manifest-python-deps-CoJXeb9u.mjs} +2 -39
- package/dist/{manifest-python-deps-B4BmMoGT.js → manifest-python-deps-eBDj5HEY.js} +29 -72
- package/dist/model-download-service-C7AjBsX9.mjs +668 -0
- package/dist/model-download-service-JtVQtbb6.js +752 -0
- package/dist/process/resource-monitor.d.ts +9 -0
- package/dist/{resource-monitor-ClDGFyf6.mjs → resource-monitor-BkP504Vq.mjs} +20 -1
- package/dist/{resource-monitor-IIEanuJt.js → resource-monitor-DNNomR-i.js} +21 -1
- 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
|
|
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-
|
|
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,
|
|
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,
|
|
7431
|
+
const defaults = (0, _camstack_types_addon.asJsonObject)(this.getFromRuntimeDefaults(section));
|
|
8090
7432
|
if (defaults) Object.assign(merged, defaults);
|
|
8091
|
-
const bootstrapSection = (0,
|
|
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,
|
|
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,
|
|
8349
|
-
const existing = (0,
|
|
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,
|
|
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,
|
|
8453
|
-
const deviceActivationRaw = (0,
|
|
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,
|
|
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$
|
|
28772
|
-
const parts = path$
|
|
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$
|
|
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$
|
|
42948
|
-
fs.open(path$
|
|
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$
|
|
42958
|
-
const fd = fs.openSync(path$
|
|
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$
|
|
43303
|
+
var fs$19 = options.fs || _fs;
|
|
43962
43304
|
var shouldThrow = true;
|
|
43963
43305
|
if ("throws" in options) shouldThrow = options.throws;
|
|
43964
|
-
fs$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
43361
|
+
fs$21.writeFile(file, str, options, callback);
|
|
44020
43362
|
}
|
|
44021
43363
|
function writeFileSync(file, obj, options) {
|
|
44022
43364
|
options = options || {};
|
|
44023
|
-
var fs$
|
|
43365
|
+
var fs$22 = options.fs || _fs;
|
|
44024
43366
|
var str = stringify(obj, options);
|
|
44025
|
-
return fs$
|
|
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$
|
|
86624
|
-
const parsed = DotenvModule.parse(fs$4.readFileSync(path$
|
|
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$
|
|
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 =
|
|
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", {
|