@camstack/system 1.1.49 → 1.1.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  15. package/dist/builtins/hub-forwarder/index.js +1 -1
  16. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  17. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  18. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  19. package/dist/builtins/local-network/local-network.addon.js +1 -1
  20. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  21. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  22. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  23. package/dist/builtins/platform-probe/coral-accelerators.d.ts +22 -0
  24. package/dist/builtins/platform-probe/index.js +67 -2
  25. package/dist/builtins/platform-probe/index.mjs +67 -2
  26. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  27. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  28. package/dist/builtins/snapshot/index.js +162 -12
  29. package/dist/builtins/snapshot/index.mjs +162 -12
  30. package/dist/builtins/snapshot/snapshot-media-handler.d.ts +38 -0
  31. package/dist/builtins/snapshot/snapshot.addon.d.ts +26 -0
  32. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  33. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  34. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  35. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  36. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  37. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  38. package/dist/builtins/system-config/system-config.addon.js +1 -1
  39. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  40. package/dist/builtins/winston-logging/index.js +1 -1
  41. package/dist/builtins/winston-logging/index.mjs +1 -1
  42. package/dist/{dist-8phgmJ6b.mjs → dist-BWQX9yUj.mjs} +162 -8
  43. package/dist/{dist-BDV1WKRg.js → dist-C3uHEBtP.js} +173 -7
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +37 -23
  46. package/dist/index.mjs +37 -24
  47. package/dist/kernel/addon-installer.d.ts +8 -0
  48. package/dist/kernel/deps/manifest-native-deps.d.ts +1 -1
  49. package/dist/kernel/deps/npm-command.d.ts +38 -0
  50. package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +2 -0
  51. package/dist/{manifest-python-deps-B09I9ems.mjs → manifest-python-deps-BA6KA9If.mjs} +298 -127
  52. package/dist/{manifest-python-deps-DAA0ULwT.js → manifest-python-deps-CVcJ9hDX.js} +309 -126
  53. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_dist = require("./dist-BDV1WKRg.js");
2
+ const require_dist = require("./dist-C3uHEBtP.js");
3
3
  let _camstack_types_node = require("@camstack/types/node");
4
4
  let node_http = require("node:http");
5
5
  let node_fs = require("node:fs");
@@ -8,14 +8,130 @@ let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path);
9
9
  let node_crypto = require("node:crypto");
10
10
  node_crypto = require_chunk.__toESM(node_crypto);
11
+ let _camstack_types_addon = require("@camstack/types/addon");
11
12
  let node_child_process = require("node:child_process");
12
13
  let node_util = require("node:util");
13
14
  let node_os = require("node:os");
14
15
  node_os = require_chunk.__toESM(node_os);
15
16
  let node_fs_promises = require("node:fs/promises");
16
- let _camstack_types_addon = require("@camstack/types/addon");
17
17
  let _trpc_client = require("@trpc/client");
18
18
  let node_net = require("node:net");
19
+ //#region src/kernel/moleculer/addon-data-plane-facility.ts
20
+ /**
21
+ * Kernel side of the addon HTTP **data-plane** (see {@link AddonDataPlane}).
22
+ *
23
+ * Each addon context gets one facility. On the first `serve()`, it binds a
24
+ * `127.0.0.1` HTTP listener IN THE ADDON'S PROCESS and multiplexes registered
25
+ * prefixes → the addon's real `(req, res)` handlers (longest-prefix wins). Every
26
+ * request must carry the per-listener shared secret (injected by the hub's
27
+ * reverse-proxy) — anything else gets 403, so no other local process can reach
28
+ * the listener. The facility reports its live endpoints through a `sink` so the
29
+ * runner can hand them to the hub (which pulls them when it mounts the addon).
30
+ *
31
+ * The addon never sees the secret, the port, or any transport detail — it just
32
+ * streams to `res`. The hub authenticates and reverse-proxies in front.
33
+ */
34
+ function trimSlashes(s) {
35
+ return s.replace(/^\/+/, "").replace(/\/+$/, "");
36
+ }
37
+ function createAddonDataPlaneFacility(args) {
38
+ const bindHost = args.bindHost ?? "127.0.0.1";
39
+ const secret = (0, node_crypto.randomBytes)(24).toString("hex");
40
+ const served = /* @__PURE__ */ new Map();
41
+ let server = null;
42
+ let baseUrl = "";
43
+ const route = (req, res) => {
44
+ if (req.headers[_camstack_types_addon.DATAPLANE_SECRET_HEADER] !== secret) {
45
+ res.writeHead(403).end();
46
+ return;
47
+ }
48
+ const normalized = `/${trimSlashes((req.url ?? "/").split("?")[0] ?? "/")}`;
49
+ let best = null;
50
+ for (const [prefix, entry] of served) {
51
+ const p = `/${prefix}`;
52
+ if (normalized === p || normalized.startsWith(`${p}/`)) {
53
+ if (best === null || p.length > `/${best.prefix}`.length) best = {
54
+ prefix,
55
+ entry,
56
+ rest: normalized.slice(p.length) || "/"
57
+ };
58
+ }
59
+ }
60
+ if (best === null) {
61
+ res.writeHead(404).end();
62
+ return;
63
+ }
64
+ const original = req.url ?? "";
65
+ const qIdx = original.indexOf("?");
66
+ req.url = best.rest + (qIdx >= 0 ? original.slice(qIdx) : "");
67
+ Promise.resolve(best.entry.handler(req, res)).catch((err) => {
68
+ args.logger.warn("data-plane handler threw", { meta: {
69
+ prefix: best.prefix,
70
+ error: err instanceof Error ? err.message : String(err)
71
+ } });
72
+ if (!res.headersSent) res.writeHead(500).end();
73
+ else res.end();
74
+ });
75
+ };
76
+ const ensureServer = async () => {
77
+ if (server) return;
78
+ const s = (0, node_http.createServer)((req, res) => route(req, res));
79
+ await new Promise((resolve, reject) => {
80
+ s.once("error", reject);
81
+ s.listen(0, bindHost, () => resolve());
82
+ });
83
+ const addr = s.address();
84
+ const port = typeof addr === "object" && addr ? addr.port : 0;
85
+ server = s;
86
+ baseUrl = `http://${bindHost}:${port}`;
87
+ };
88
+ const publish = () => {
89
+ const endpoints = [...served.entries()].map(([prefix, entry]) => ({
90
+ prefix,
91
+ access: entry.access,
92
+ baseUrl,
93
+ secret
94
+ }));
95
+ args.sink?.set(args.addonId, endpoints);
96
+ };
97
+ const dataPlane = { serve: async (options) => {
98
+ const prefix = trimSlashes(options.prefix);
99
+ if (prefix.length === 0) throw new Error("dataPlane.serve: a non-empty prefix is required");
100
+ await ensureServer();
101
+ served.set(prefix, {
102
+ access: options.access,
103
+ handler: options.handler
104
+ });
105
+ publish();
106
+ args.logger.info("data-plane endpoint served", { meta: {
107
+ prefix,
108
+ access: options.access,
109
+ baseUrl
110
+ } });
111
+ return {
112
+ baseUrl,
113
+ prefix,
114
+ dispose: async () => {
115
+ served.delete(prefix);
116
+ publish();
117
+ }
118
+ };
119
+ } };
120
+ const dispose = async () => {
121
+ served.clear();
122
+ args.sink?.set(args.addonId, []);
123
+ if (server) {
124
+ const s = server;
125
+ server = null;
126
+ await new Promise((resolve) => s.close(() => resolve()));
127
+ }
128
+ };
129
+ return {
130
+ dataPlane,
131
+ dispose
132
+ };
133
+ }
134
+ //#endregion
19
135
  //#region src/kernel/addon-class-resolver.ts
20
136
  function isRecord(value) {
21
137
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -56,9 +172,147 @@ function resolveAddonClass(mod) {
56
172
  if (namedAddon) return namedAddon;
57
173
  }
58
174
  //#endregion
175
+ //#region src/kernel/deps/npm-command.ts
176
+ /**
177
+ * npm invocation resolver — makes "is npm available?" a solved problem for
178
+ * every installer code path that shells out to npm.
179
+ *
180
+ * Why this exists (Mac mini incident, silent for days until 2026-07-16): the
181
+ * packaged Electron agent ships a BARE node binary — `Resources/node/bin/node`
182
+ * with no npm — so every `execFileAsync('npm', ...)` died with ENOENT inside a
183
+ * "non-fatal" catch. Addons with runtime deps installed WITHOUT their
184
+ * node_modules, reported "running", and failed at first import
185
+ * (`Cannot find module 'sharp'`): the detection group ran 1/3 members and the
186
+ * node silently fell out of the balancer pool.
187
+ *
188
+ * Resolution order:
189
+ * 1. system `npm` on PATH (probed once per process) — hub image, dev
190
+ * machines, Linux agent containers;
191
+ * 2. a previously-bootstrapped copy under `cacheDir` — run its `npm-cli.js`
192
+ * with the CURRENT node binary (`process.execPath`), which always exists;
193
+ * 3. BOOTSTRAP: fetch `<registry>/npm/latest` (the npm package is fully
194
+ * self-contained — its tarball bundles every dependency), download +
195
+ * extract into `cacheDir`, then use it as in (2).
196
+ *
197
+ * Failures THROW with an actionable message — a caller must never proceed as
198
+ * if dependencies were installed when they were not.
199
+ */
200
+ var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
201
+ var DEFAULT_REGISTRY = "https://registry.npmjs.org";
202
+ /** Where the bootstrap lands inside `cacheDir` (npm tgz carries a `package/` prefix). */
203
+ function bootstrappedCliPath(cacheDir) {
204
+ return node_path.join(cacheDir, "package", "bin", "npm-cli.js");
205
+ }
206
+ async function defaultProbeSystemNpm() {
207
+ try {
208
+ await execFileAsync$1("npm", ["--version"], { timeout: 15e3 });
209
+ return true;
210
+ } catch {
211
+ return false;
212
+ }
213
+ }
214
+ async function defaultFetchJson(url) {
215
+ const res = await fetch(url);
216
+ if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
217
+ return res.json();
218
+ }
219
+ async function defaultDownloadTarball(url, destTgz) {
220
+ const res = await fetch(url);
221
+ if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} downloading ${url}`);
222
+ const bytes = Buffer.from(await res.arrayBuffer());
223
+ await node_fs.promises.writeFile(destTgz, bytes);
224
+ }
225
+ async function defaultExtractTarball(tgzPath, destDir) {
226
+ await execFileAsync$1("tar", [
227
+ "-xzf",
228
+ tgzPath,
229
+ "-C",
230
+ destDir
231
+ ], { timeout: 12e4 });
232
+ }
233
+ /** The `dist.tarball` URL out of a registry version-metadata document. */
234
+ function tarballUrlOf(metadata) {
235
+ if (typeof metadata !== "object" || metadata === null) return null;
236
+ const dist = metadata["dist"];
237
+ if (typeof dist !== "object" || dist === null) return null;
238
+ const tarball = dist["tarball"];
239
+ return typeof tarball === "string" && tarball.length > 0 ? tarball : null;
240
+ }
241
+ /**
242
+ * Process-wide memo: resolution is stable for the process lifetime (npm does
243
+ * not appear/disappear mid-run, and the bootstrap is idempotent on disk).
244
+ */
245
+ var cachedInvocation = null;
246
+ async function resolveNpmInvocation(options) {
247
+ cachedInvocation ??= resolveUncached(options).catch((err) => {
248
+ cachedInvocation = null;
249
+ throw err;
250
+ });
251
+ return cachedInvocation;
252
+ }
253
+ /**
254
+ * Resolve a working npm (system → bootstrapped → bootstrap) and run it with
255
+ * `args`. The ONE entry point every installer code path uses instead of a raw
256
+ * `execFile('npm', ...)` — so "npm missing on this host" is either solved
257
+ * (bootstrap) or a LOUD, actionable error, never a swallowed ENOENT.
258
+ */
259
+ async function runNpm(args, options) {
260
+ const invocation = await resolveNpmInvocation({
261
+ cacheDir: options.cacheDir,
262
+ registry: options.registry,
263
+ logger: options.logger
264
+ });
265
+ return execFileAsync$1(invocation.command, [...invocation.argsPrefix, ...args], {
266
+ ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
267
+ timeout: options.timeout ?? 3e5
268
+ });
269
+ }
270
+ async function resolveUncached(options) {
271
+ if (await (options.probeSystemNpm ?? defaultProbeSystemNpm)()) return {
272
+ command: "npm",
273
+ argsPrefix: []
274
+ };
275
+ const cliPath = bootstrappedCliPath(options.cacheDir);
276
+ if (node_fs.existsSync(cliPath)) {
277
+ options.logger.info("system npm unavailable — using bootstrapped npm", { meta: { cliPath } });
278
+ return {
279
+ command: process.execPath,
280
+ argsPrefix: [cliPath]
281
+ };
282
+ }
283
+ options.logger.warn("system npm unavailable — bootstrapping npm from the registry", { meta: { cacheDir: options.cacheDir } });
284
+ try {
285
+ const registry = (options.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
286
+ const tarballUrl = tarballUrlOf(await (options.fetchJson ?? defaultFetchJson)(`${registry}/npm/latest`));
287
+ if (!tarballUrl) throw new Error(`registry metadata for 'npm' carries no dist.tarball (${registry})`);
288
+ await node_fs.promises.mkdir(options.cacheDir, { recursive: true });
289
+ const tgzPath = node_path.join(options.cacheDir, "npm-bootstrap.tgz");
290
+ await (options.downloadTarball ?? defaultDownloadTarball)(tarballUrl, tgzPath);
291
+ await (options.extractTarball ?? defaultExtractTarball)(tgzPath, options.cacheDir);
292
+ await node_fs.promises.rm(tgzPath, { force: true });
293
+ if (!node_fs.existsSync(cliPath)) throw new Error(`extracted npm tarball is missing ${cliPath}`);
294
+ options.logger.info("npm bootstrapped", { meta: { cliPath } });
295
+ return {
296
+ command: process.execPath,
297
+ argsPrefix: [cliPath]
298
+ };
299
+ } catch (err) {
300
+ throw new Error(`no system npm on PATH and the npm bootstrap failed — cannot install addon dependencies on this node: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
301
+ }
302
+ }
303
+ //#endregion
59
304
  //#region src/kernel/deps/manifest-native-deps.ts
60
305
  var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
61
306
  /**
307
+ * Fallback bootstrap-cache location when the caller doesn't thread one
308
+ * through (the forked addon-runner's spawn-time install). tmpdir survives the
309
+ * process; a reboot just re-bootstraps. Callers with a persistent root (the
310
+ * installer) pass their own.
311
+ */
312
+ function defaultNpmCacheDir() {
313
+ return node_path.join(node_os.tmpdir(), "camstack-npm-bootstrap");
314
+ }
315
+ /**
62
316
  * Native node modules an addon needs at runtime but cannot be bundled
63
317
  * (`.node` binary files require ABI-matched compilation). Mirror of the
64
318
  * Python `requirements.txt` pattern in `manifest-python-deps.ts` —
@@ -81,7 +335,7 @@ var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
81
335
  * ABI; surface a clear error at first `import` of the native module
82
336
  * otherwise).
83
337
  */
84
- async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
338
+ async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry, npmCacheDir) {
85
339
  const native = readNativeDeps(pkgRaw);
86
340
  if (native == null || Object.keys(native).length === 0) return;
87
341
  const markerFile = node_path.join(addonDir, ".camstack-native-deps-installed");
@@ -126,14 +380,17 @@ async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
126
380
  ...specs
127
381
  ];
128
382
  try {
129
- await execFileAsync("npm", args, {
383
+ await runNpm(args, {
384
+ cacheDir: npmCacheDir ?? defaultNpmCacheDir(),
385
+ registry,
386
+ logger,
130
387
  cwd: addonDir,
131
388
  timeout: 3e5
132
389
  });
133
390
  } catch (err) {
134
391
  throw new Error(`npm install of native deps failed for ${addonDir}: ${(0, _camstack_types_addon.errMsg)(err)}`, { cause: err });
135
392
  }
136
- await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger);
393
+ await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger, npmCacheDir ?? defaultNpmCacheDir(), registry);
137
394
  try {
138
395
  node_fs.writeFileSync(markerFile, markerHash);
139
396
  } catch (err) {
@@ -249,7 +506,7 @@ function markerMatches(markerFile, expected) {
249
506
  * The first `import` of an actually-mismatched binary throws a clear
250
507
  * error that points the operator at this rebuild step.
251
508
  */
252
- async function rebuildNativeDeps(addonDir, packageNames, logger) {
509
+ async function rebuildNativeDeps(addonDir, packageNames, logger, npmCacheDir, registry) {
253
510
  if (typeof process.versions.electron === "string") {
254
511
  const electronVersion = process.versions.electron;
255
512
  logger.info("Rebuilding native deps for Electron", { meta: {
@@ -293,7 +550,10 @@ async function rebuildNativeDeps(addonDir, packageNames, logger) {
293
550
  packages: packageNames
294
551
  } });
295
552
  try {
296
- await execFileAsync("npm", ["rebuild", ...packageNames], {
553
+ await runNpm(["rebuild", ...packageNames], {
554
+ cacheDir: npmCacheDir,
555
+ registry,
556
+ logger,
297
557
  cwd: addonDir,
298
558
  timeout: 6e5
299
559
  });
@@ -5585,6 +5845,30 @@ function safeExistsSync(path) {
5585
5845
  return false;
5586
5846
  }
5587
5847
  }
5848
+ /**
5849
+ * Detect a Coral USB Edge TPU: a matching USB device (`1a6e:089a` pre-init or
5850
+ * `18d1:9302` post-init) OR the installed libedgetpu delegate (the hub
5851
+ * provisions it out-of-band; its presence is a reliable "Coral is wired"
5852
+ * signal even where the container's `/sys` USB view is limited).
5853
+ */
5854
+ function detectCoralUsb() {
5855
+ const CORAL_IDS = [{
5856
+ vendor: "1a6e",
5857
+ product: "089a"
5858
+ }, {
5859
+ vendor: "18d1",
5860
+ product: "9302"
5861
+ }];
5862
+ try {
5863
+ for (const device of node_fs.readdirSync("/sys/bus/usb/devices")) try {
5864
+ const base = `/sys/bus/usb/devices/${device}`;
5865
+ const vendor = node_fs.readFileSync(`${base}/idVendor`, "utf8").trim().toLowerCase();
5866
+ const product = node_fs.readFileSync(`${base}/idProduct`, "utf8").trim().toLowerCase();
5867
+ if (CORAL_IDS.some((id) => id.vendor === vendor && id.product === product)) return true;
5868
+ } catch {}
5869
+ } catch {}
5870
+ return safeExistsSync(process.env["CAMSTACK_EDGETPU_LIB"] ?? "/data/deps/edgetpu/lib/libedgetpu.so.1");
5871
+ }
5588
5872
  /** Probe Linux GPU vendors by parsing /proc/bus/pci/devices or /sys/class/drm. */
5589
5873
  function probeLinuxGpus() {
5590
5874
  const signals = {
@@ -5592,7 +5876,8 @@ function probeLinuxGpus() {
5592
5876
  hasAmdGpu: false,
5593
5877
  hasIntelGpu: false,
5594
5878
  hasRenderNode: false,
5595
- hasIntelNpu: safeExistsSync("/dev/accel/accel0")
5879
+ hasIntelNpu: safeExistsSync("/dev/accel/accel0"),
5880
+ hasCoral: detectCoralUsb()
5596
5881
  };
5597
5882
  signals.hasRenderNode = safeExistsSync("/dev/dri/renderD128");
5598
5883
  if (safeExistsSync("/dev/nvidia0") || safeExistsSync("/proc/driver/nvidia/version")) signals.hasNvidiaGpu = true;
@@ -5625,7 +5910,8 @@ function probePlatform() {
5625
5910
  hasAmdGpu: false,
5626
5911
  hasIntelGpu: false,
5627
5912
  hasRenderNode: false,
5628
- hasIntelNpu: false
5913
+ hasIntelNpu: false,
5914
+ hasCoral: safeExistsSync(process.env["CAMSTACK_EDGETPU_LIB"] ?? "/data/deps/edgetpu/lib/libedgetpu.so.1")
5629
5915
  };
5630
5916
  }
5631
5917
  function pickBackendsForPlatform(signals) {
@@ -5710,7 +5996,8 @@ function createKernelHwAccel() {
5710
5996
  function mapSignalsToInferenceHardware(signals) {
5711
5997
  return {
5712
5998
  gpu: signals.hasNvidiaGpu ? { type: "nvidia" } : signals.hasIntelGpu || signals.hasRenderNode ? { type: "intel" } : null,
5713
- npu: signals.platform === "darwin" && signals.arch === "arm64" ? { type: "apple-ane" } : signals.hasIntelNpu ? { type: "intel-npu" } : null
5999
+ npu: signals.platform === "darwin" && signals.arch === "arm64" ? { type: "apple-ane" } : signals.hasIntelNpu ? { type: "intel-npu" } : null,
6000
+ coral: signals.hasCoral ? { type: "coral-edgetpu" } : null
5714
6001
  };
5715
6002
  }
5716
6003
  /**
@@ -5722,122 +6009,6 @@ function createKernelInferenceEngine() {
5722
6009
  return { resolveHardware: async () => mapSignalsToInferenceHardware(probePlatform()) };
5723
6010
  }
5724
6011
  //#endregion
5725
- //#region src/kernel/moleculer/addon-data-plane-facility.ts
5726
- /**
5727
- * Kernel side of the addon HTTP **data-plane** (see {@link AddonDataPlane}).
5728
- *
5729
- * Each addon context gets one facility. On the first `serve()`, it binds a
5730
- * `127.0.0.1` HTTP listener IN THE ADDON'S PROCESS and multiplexes registered
5731
- * prefixes → the addon's real `(req, res)` handlers (longest-prefix wins). Every
5732
- * request must carry the per-listener shared secret (injected by the hub's
5733
- * reverse-proxy) — anything else gets 403, so no other local process can reach
5734
- * the listener. The facility reports its live endpoints through a `sink` so the
5735
- * runner can hand them to the hub (which pulls them when it mounts the addon).
5736
- *
5737
- * The addon never sees the secret, the port, or any transport detail — it just
5738
- * streams to `res`. The hub authenticates and reverse-proxies in front.
5739
- */
5740
- function trimSlashes(s) {
5741
- return s.replace(/^\/+/, "").replace(/\/+$/, "");
5742
- }
5743
- function createAddonDataPlaneFacility(args) {
5744
- const bindHost = args.bindHost ?? "127.0.0.1";
5745
- const secret = (0, node_crypto.randomBytes)(24).toString("hex");
5746
- const served = /* @__PURE__ */ new Map();
5747
- let server = null;
5748
- let baseUrl = "";
5749
- const route = (req, res) => {
5750
- if (req.headers[_camstack_types_addon.DATAPLANE_SECRET_HEADER] !== secret) {
5751
- res.writeHead(403).end();
5752
- return;
5753
- }
5754
- const normalized = `/${trimSlashes((req.url ?? "/").split("?")[0] ?? "/")}`;
5755
- let best = null;
5756
- for (const [prefix, entry] of served) {
5757
- const p = `/${prefix}`;
5758
- if (normalized === p || normalized.startsWith(`${p}/`)) {
5759
- if (best === null || p.length > `/${best.prefix}`.length) best = {
5760
- prefix,
5761
- entry,
5762
- rest: normalized.slice(p.length) || "/"
5763
- };
5764
- }
5765
- }
5766
- if (best === null) {
5767
- res.writeHead(404).end();
5768
- return;
5769
- }
5770
- const original = req.url ?? "";
5771
- const qIdx = original.indexOf("?");
5772
- req.url = best.rest + (qIdx >= 0 ? original.slice(qIdx) : "");
5773
- Promise.resolve(best.entry.handler(req, res)).catch((err) => {
5774
- args.logger.warn("data-plane handler threw", { meta: {
5775
- prefix: best.prefix,
5776
- error: err instanceof Error ? err.message : String(err)
5777
- } });
5778
- if (!res.headersSent) res.writeHead(500).end();
5779
- else res.end();
5780
- });
5781
- };
5782
- const ensureServer = async () => {
5783
- if (server) return;
5784
- const s = (0, node_http.createServer)((req, res) => route(req, res));
5785
- await new Promise((resolve, reject) => {
5786
- s.once("error", reject);
5787
- s.listen(0, bindHost, () => resolve());
5788
- });
5789
- const addr = s.address();
5790
- const port = typeof addr === "object" && addr ? addr.port : 0;
5791
- server = s;
5792
- baseUrl = `http://${bindHost}:${port}`;
5793
- };
5794
- const publish = () => {
5795
- const endpoints = [...served.entries()].map(([prefix, entry]) => ({
5796
- prefix,
5797
- access: entry.access,
5798
- baseUrl,
5799
- secret
5800
- }));
5801
- args.sink?.set(args.addonId, endpoints);
5802
- };
5803
- const dataPlane = { serve: async (options) => {
5804
- const prefix = trimSlashes(options.prefix);
5805
- if (prefix.length === 0) throw new Error("dataPlane.serve: a non-empty prefix is required");
5806
- await ensureServer();
5807
- served.set(prefix, {
5808
- access: options.access,
5809
- handler: options.handler
5810
- });
5811
- publish();
5812
- args.logger.info("data-plane endpoint served", { meta: {
5813
- prefix,
5814
- access: options.access,
5815
- baseUrl
5816
- } });
5817
- return {
5818
- baseUrl,
5819
- prefix,
5820
- dispose: async () => {
5821
- served.delete(prefix);
5822
- publish();
5823
- }
5824
- };
5825
- } };
5826
- const dispose = async () => {
5827
- served.clear();
5828
- args.sink?.set(args.addonId, []);
5829
- if (server) {
5830
- const s = server;
5831
- server = null;
5832
- await new Promise((resolve) => s.close(() => resolve()));
5833
- }
5834
- };
5835
- return {
5836
- dataPlane,
5837
- dispose
5838
- };
5839
- }
5840
- //#endregion
5841
6012
  //#region src/kernel/deps/addon-deps-manager.ts
5842
6013
  /**
5843
6014
  * Canonical implementation of `IAddonDepsManager`, injected into every
@@ -7329,6 +7500,12 @@ Object.defineProperty(exports, "createAddonContext", {
7329
7500
  return createAddonContext;
7330
7501
  }
7331
7502
  });
7503
+ Object.defineProperty(exports, "createAddonDataPlaneFacility", {
7504
+ enumerable: true,
7505
+ get: function() {
7506
+ return createAddonDataPlaneFacility;
7507
+ }
7508
+ });
7332
7509
  Object.defineProperty(exports, "createAddonService", {
7333
7510
  enumerable: true,
7334
7511
  get: function() {
@@ -7521,6 +7698,12 @@ Object.defineProperty(exports, "resolveHwAccel", {
7521
7698
  return resolveHwAccel;
7522
7699
  }
7523
7700
  });
7701
+ Object.defineProperty(exports, "runNpm", {
7702
+ enumerable: true,
7703
+ get: function() {
7704
+ return runNpm;
7705
+ }
7706
+ });
7524
7707
  Object.defineProperty(exports, "serializeTypedArrays", {
7525
7708
  enumerable: true,
7526
7709
  get: function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.1.49",
3
+ "version": "1.1.51",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",