@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
@@ -0,0 +1,38 @@
1
+ import { IScopedLogger } from '@camstack/types';
2
+ /** How the caller must spawn npm: `execFile(command, [...argsPrefix, ...npmArgs])`. */
3
+ export interface NpmInvocation {
4
+ readonly command: string;
5
+ readonly argsPrefix: readonly string[];
6
+ }
7
+ export interface ResolveNpmInvocationOptions {
8
+ /** Directory that holds (or receives) the bootstrapped npm package. */
9
+ readonly cacheDir: string;
10
+ /** npm registry base URL; defaults to the public registry. */
11
+ readonly registry?: string | undefined;
12
+ readonly logger: IScopedLogger;
13
+ /** Test seams — production callers omit all of these. */
14
+ readonly probeSystemNpm?: () => Promise<boolean>;
15
+ readonly fetchJson?: (url: string) => Promise<unknown>;
16
+ readonly downloadTarball?: (url: string, destTgz: string) => Promise<void>;
17
+ readonly extractTarball?: (tgzPath: string, destDir: string) => Promise<void>;
18
+ }
19
+ /** Test-only: reset the process-wide memo. */
20
+ export declare function __clearNpmInvocationCacheForTests(): void;
21
+ export declare function resolveNpmInvocation(options: ResolveNpmInvocationOptions): Promise<NpmInvocation>;
22
+ export interface RunNpmOptions {
23
+ readonly cacheDir: string;
24
+ readonly registry?: string | undefined;
25
+ readonly logger: IScopedLogger;
26
+ readonly cwd?: string;
27
+ readonly timeout?: number;
28
+ }
29
+ /**
30
+ * Resolve a working npm (system → bootstrapped → bootstrap) and run it with
31
+ * `args`. The ONE entry point every installer code path uses instead of a raw
32
+ * `execFile('npm', ...)` — so "npm missing on this host" is either solved
33
+ * (bootstrap) or a LOUD, actionable error, never a swallowed ENOENT.
34
+ */
35
+ export declare function runNpm(args: readonly string[], options: RunNpmOptions): Promise<{
36
+ stdout: string;
37
+ stderr: string;
38
+ }>;
@@ -8,6 +8,8 @@ export interface PlatformSignals {
8
8
  readonly hasRenderNode: boolean;
9
9
  /** Intel NPU (Meteor Lake / Core Ultra "AI Boost") — exposed as /dev/accel/accel0. */
10
10
  readonly hasIntelNpu: boolean;
11
+ /** Coral USB Edge TPU — a discrete USB accelerator (gates the `edgetpu` runtime). */
12
+ readonly hasCoral: boolean;
11
13
  }
12
14
  export declare function probePlatform(): PlatformSignals;
13
15
  /**
@@ -1,4 +1,4 @@
1
- import { U as readNodePin } from "./dist-8phgmJ6b.mjs";
1
+ import { G as readNodePin } from "./dist-BWQX9yUj.mjs";
2
2
  import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
3
3
  import { createServer } from "node:http";
4
4
  import * as fs from "node:fs";
@@ -6,14 +6,130 @@ import * as path$1 from "node:path";
6
6
  import { isAbsolute, join } from "node:path";
7
7
  import * as crypto$1 from "node:crypto";
8
8
  import { randomBytes, randomUUID } from "node:crypto";
9
+ import { DATAPLANE_SECRET_HEADER, DeviceType, DisposerChain, EventCategory, ReadinessRegistry, asJsonObject, asString, createDeviceProxy, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, scopeKey, sleep } from "@camstack/types/addon";
9
10
  import { execFile } from "node:child_process";
10
11
  import { promisify } from "node:util";
11
12
  import * as os from "node:os";
12
13
  import { tmpdir } from "node:os";
13
14
  import { unlink } from "node:fs/promises";
14
- import { DATAPLANE_SECRET_HEADER, DeviceType, DisposerChain, EventCategory, ReadinessRegistry, asJsonObject, asString, createDeviceProxy, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, scopeKey, sleep } from "@camstack/types/addon";
15
15
  import { TRPCClientError, createTRPCClient } from "@trpc/client";
16
16
  import { connect, createServer as createServer$1 } from "node:net";
17
+ //#region src/kernel/moleculer/addon-data-plane-facility.ts
18
+ /**
19
+ * Kernel side of the addon HTTP **data-plane** (see {@link AddonDataPlane}).
20
+ *
21
+ * Each addon context gets one facility. On the first `serve()`, it binds a
22
+ * `127.0.0.1` HTTP listener IN THE ADDON'S PROCESS and multiplexes registered
23
+ * prefixes → the addon's real `(req, res)` handlers (longest-prefix wins). Every
24
+ * request must carry the per-listener shared secret (injected by the hub's
25
+ * reverse-proxy) — anything else gets 403, so no other local process can reach
26
+ * the listener. The facility reports its live endpoints through a `sink` so the
27
+ * runner can hand them to the hub (which pulls them when it mounts the addon).
28
+ *
29
+ * The addon never sees the secret, the port, or any transport detail — it just
30
+ * streams to `res`. The hub authenticates and reverse-proxies in front.
31
+ */
32
+ function trimSlashes(s) {
33
+ return s.replace(/^\/+/, "").replace(/\/+$/, "");
34
+ }
35
+ function createAddonDataPlaneFacility(args) {
36
+ const bindHost = args.bindHost ?? "127.0.0.1";
37
+ const secret = randomBytes(24).toString("hex");
38
+ const served = /* @__PURE__ */ new Map();
39
+ let server = null;
40
+ let baseUrl = "";
41
+ const route = (req, res) => {
42
+ if (req.headers[DATAPLANE_SECRET_HEADER] !== secret) {
43
+ res.writeHead(403).end();
44
+ return;
45
+ }
46
+ const normalized = `/${trimSlashes((req.url ?? "/").split("?")[0] ?? "/")}`;
47
+ let best = null;
48
+ for (const [prefix, entry] of served) {
49
+ const p = `/${prefix}`;
50
+ if (normalized === p || normalized.startsWith(`${p}/`)) {
51
+ if (best === null || p.length > `/${best.prefix}`.length) best = {
52
+ prefix,
53
+ entry,
54
+ rest: normalized.slice(p.length) || "/"
55
+ };
56
+ }
57
+ }
58
+ if (best === null) {
59
+ res.writeHead(404).end();
60
+ return;
61
+ }
62
+ const original = req.url ?? "";
63
+ const qIdx = original.indexOf("?");
64
+ req.url = best.rest + (qIdx >= 0 ? original.slice(qIdx) : "");
65
+ Promise.resolve(best.entry.handler(req, res)).catch((err) => {
66
+ args.logger.warn("data-plane handler threw", { meta: {
67
+ prefix: best.prefix,
68
+ error: err instanceof Error ? err.message : String(err)
69
+ } });
70
+ if (!res.headersSent) res.writeHead(500).end();
71
+ else res.end();
72
+ });
73
+ };
74
+ const ensureServer = async () => {
75
+ if (server) return;
76
+ const s = createServer((req, res) => route(req, res));
77
+ await new Promise((resolve, reject) => {
78
+ s.once("error", reject);
79
+ s.listen(0, bindHost, () => resolve());
80
+ });
81
+ const addr = s.address();
82
+ const port = typeof addr === "object" && addr ? addr.port : 0;
83
+ server = s;
84
+ baseUrl = `http://${bindHost}:${port}`;
85
+ };
86
+ const publish = () => {
87
+ const endpoints = [...served.entries()].map(([prefix, entry]) => ({
88
+ prefix,
89
+ access: entry.access,
90
+ baseUrl,
91
+ secret
92
+ }));
93
+ args.sink?.set(args.addonId, endpoints);
94
+ };
95
+ const dataPlane = { serve: async (options) => {
96
+ const prefix = trimSlashes(options.prefix);
97
+ if (prefix.length === 0) throw new Error("dataPlane.serve: a non-empty prefix is required");
98
+ await ensureServer();
99
+ served.set(prefix, {
100
+ access: options.access,
101
+ handler: options.handler
102
+ });
103
+ publish();
104
+ args.logger.info("data-plane endpoint served", { meta: {
105
+ prefix,
106
+ access: options.access,
107
+ baseUrl
108
+ } });
109
+ return {
110
+ baseUrl,
111
+ prefix,
112
+ dispose: async () => {
113
+ served.delete(prefix);
114
+ publish();
115
+ }
116
+ };
117
+ } };
118
+ const dispose = async () => {
119
+ served.clear();
120
+ args.sink?.set(args.addonId, []);
121
+ if (server) {
122
+ const s = server;
123
+ server = null;
124
+ await new Promise((resolve) => s.close(() => resolve()));
125
+ }
126
+ };
127
+ return {
128
+ dataPlane,
129
+ dispose
130
+ };
131
+ }
132
+ //#endregion
17
133
  //#region src/kernel/addon-class-resolver.ts
18
134
  function isRecord(value) {
19
135
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -54,9 +170,147 @@ function resolveAddonClass(mod) {
54
170
  if (namedAddon) return namedAddon;
55
171
  }
56
172
  //#endregion
173
+ //#region src/kernel/deps/npm-command.ts
174
+ /**
175
+ * npm invocation resolver — makes "is npm available?" a solved problem for
176
+ * every installer code path that shells out to npm.
177
+ *
178
+ * Why this exists (Mac mini incident, silent for days until 2026-07-16): the
179
+ * packaged Electron agent ships a BARE node binary — `Resources/node/bin/node`
180
+ * with no npm — so every `execFileAsync('npm', ...)` died with ENOENT inside a
181
+ * "non-fatal" catch. Addons with runtime deps installed WITHOUT their
182
+ * node_modules, reported "running", and failed at first import
183
+ * (`Cannot find module 'sharp'`): the detection group ran 1/3 members and the
184
+ * node silently fell out of the balancer pool.
185
+ *
186
+ * Resolution order:
187
+ * 1. system `npm` on PATH (probed once per process) — hub image, dev
188
+ * machines, Linux agent containers;
189
+ * 2. a previously-bootstrapped copy under `cacheDir` — run its `npm-cli.js`
190
+ * with the CURRENT node binary (`process.execPath`), which always exists;
191
+ * 3. BOOTSTRAP: fetch `<registry>/npm/latest` (the npm package is fully
192
+ * self-contained — its tarball bundles every dependency), download +
193
+ * extract into `cacheDir`, then use it as in (2).
194
+ *
195
+ * Failures THROW with an actionable message — a caller must never proceed as
196
+ * if dependencies were installed when they were not.
197
+ */
198
+ var execFileAsync$1 = promisify(execFile);
199
+ var DEFAULT_REGISTRY = "https://registry.npmjs.org";
200
+ /** Where the bootstrap lands inside `cacheDir` (npm tgz carries a `package/` prefix). */
201
+ function bootstrappedCliPath(cacheDir) {
202
+ return path$1.join(cacheDir, "package", "bin", "npm-cli.js");
203
+ }
204
+ async function defaultProbeSystemNpm() {
205
+ try {
206
+ await execFileAsync$1("npm", ["--version"], { timeout: 15e3 });
207
+ return true;
208
+ } catch {
209
+ return false;
210
+ }
211
+ }
212
+ async function defaultFetchJson(url) {
213
+ const res = await fetch(url);
214
+ if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
215
+ return res.json();
216
+ }
217
+ async function defaultDownloadTarball(url, destTgz) {
218
+ const res = await fetch(url);
219
+ if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} downloading ${url}`);
220
+ const bytes = Buffer.from(await res.arrayBuffer());
221
+ await fs.promises.writeFile(destTgz, bytes);
222
+ }
223
+ async function defaultExtractTarball(tgzPath, destDir) {
224
+ await execFileAsync$1("tar", [
225
+ "-xzf",
226
+ tgzPath,
227
+ "-C",
228
+ destDir
229
+ ], { timeout: 12e4 });
230
+ }
231
+ /** The `dist.tarball` URL out of a registry version-metadata document. */
232
+ function tarballUrlOf(metadata) {
233
+ if (typeof metadata !== "object" || metadata === null) return null;
234
+ const dist = metadata["dist"];
235
+ if (typeof dist !== "object" || dist === null) return null;
236
+ const tarball = dist["tarball"];
237
+ return typeof tarball === "string" && tarball.length > 0 ? tarball : null;
238
+ }
239
+ /**
240
+ * Process-wide memo: resolution is stable for the process lifetime (npm does
241
+ * not appear/disappear mid-run, and the bootstrap is idempotent on disk).
242
+ */
243
+ var cachedInvocation = null;
244
+ async function resolveNpmInvocation(options) {
245
+ cachedInvocation ??= resolveUncached(options).catch((err) => {
246
+ cachedInvocation = null;
247
+ throw err;
248
+ });
249
+ return cachedInvocation;
250
+ }
251
+ /**
252
+ * Resolve a working npm (system → bootstrapped → bootstrap) and run it with
253
+ * `args`. The ONE entry point every installer code path uses instead of a raw
254
+ * `execFile('npm', ...)` — so "npm missing on this host" is either solved
255
+ * (bootstrap) or a LOUD, actionable error, never a swallowed ENOENT.
256
+ */
257
+ async function runNpm(args, options) {
258
+ const invocation = await resolveNpmInvocation({
259
+ cacheDir: options.cacheDir,
260
+ registry: options.registry,
261
+ logger: options.logger
262
+ });
263
+ return execFileAsync$1(invocation.command, [...invocation.argsPrefix, ...args], {
264
+ ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
265
+ timeout: options.timeout ?? 3e5
266
+ });
267
+ }
268
+ async function resolveUncached(options) {
269
+ if (await (options.probeSystemNpm ?? defaultProbeSystemNpm)()) return {
270
+ command: "npm",
271
+ argsPrefix: []
272
+ };
273
+ const cliPath = bootstrappedCliPath(options.cacheDir);
274
+ if (fs.existsSync(cliPath)) {
275
+ options.logger.info("system npm unavailable — using bootstrapped npm", { meta: { cliPath } });
276
+ return {
277
+ command: process.execPath,
278
+ argsPrefix: [cliPath]
279
+ };
280
+ }
281
+ options.logger.warn("system npm unavailable — bootstrapping npm from the registry", { meta: { cacheDir: options.cacheDir } });
282
+ try {
283
+ const registry = (options.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
284
+ const tarballUrl = tarballUrlOf(await (options.fetchJson ?? defaultFetchJson)(`${registry}/npm/latest`));
285
+ if (!tarballUrl) throw new Error(`registry metadata for 'npm' carries no dist.tarball (${registry})`);
286
+ await fs.promises.mkdir(options.cacheDir, { recursive: true });
287
+ const tgzPath = path$1.join(options.cacheDir, "npm-bootstrap.tgz");
288
+ await (options.downloadTarball ?? defaultDownloadTarball)(tarballUrl, tgzPath);
289
+ await (options.extractTarball ?? defaultExtractTarball)(tgzPath, options.cacheDir);
290
+ await fs.promises.rm(tgzPath, { force: true });
291
+ if (!fs.existsSync(cliPath)) throw new Error(`extracted npm tarball is missing ${cliPath}`);
292
+ options.logger.info("npm bootstrapped", { meta: { cliPath } });
293
+ return {
294
+ command: process.execPath,
295
+ argsPrefix: [cliPath]
296
+ };
297
+ } catch (err) {
298
+ 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 });
299
+ }
300
+ }
301
+ //#endregion
57
302
  //#region src/kernel/deps/manifest-native-deps.ts
58
303
  var execFileAsync = promisify(execFile);
59
304
  /**
305
+ * Fallback bootstrap-cache location when the caller doesn't thread one
306
+ * through (the forked addon-runner's spawn-time install). tmpdir survives the
307
+ * process; a reboot just re-bootstraps. Callers with a persistent root (the
308
+ * installer) pass their own.
309
+ */
310
+ function defaultNpmCacheDir() {
311
+ return path$1.join(os.tmpdir(), "camstack-npm-bootstrap");
312
+ }
313
+ /**
60
314
  * Native node modules an addon needs at runtime but cannot be bundled
61
315
  * (`.node` binary files require ABI-matched compilation). Mirror of the
62
316
  * Python `requirements.txt` pattern in `manifest-python-deps.ts` —
@@ -79,7 +333,7 @@ var execFileAsync = promisify(execFile);
79
333
  * ABI; surface a clear error at first `import` of the native module
80
334
  * otherwise).
81
335
  */
82
- async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
336
+ async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry, npmCacheDir) {
83
337
  const native = readNativeDeps(pkgRaw);
84
338
  if (native == null || Object.keys(native).length === 0) return;
85
339
  const markerFile = path$1.join(addonDir, ".camstack-native-deps-installed");
@@ -124,14 +378,17 @@ async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
124
378
  ...specs
125
379
  ];
126
380
  try {
127
- await execFileAsync("npm", args, {
381
+ await runNpm(args, {
382
+ cacheDir: npmCacheDir ?? defaultNpmCacheDir(),
383
+ registry,
384
+ logger,
128
385
  cwd: addonDir,
129
386
  timeout: 3e5
130
387
  });
131
388
  } catch (err) {
132
389
  throw new Error(`npm install of native deps failed for ${addonDir}: ${errMsg(err)}`, { cause: err });
133
390
  }
134
- await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger);
391
+ await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger, npmCacheDir ?? defaultNpmCacheDir(), registry);
135
392
  try {
136
393
  fs.writeFileSync(markerFile, markerHash);
137
394
  } catch (err) {
@@ -247,7 +504,7 @@ function markerMatches(markerFile, expected) {
247
504
  * The first `import` of an actually-mismatched binary throws a clear
248
505
  * error that points the operator at this rebuild step.
249
506
  */
250
- async function rebuildNativeDeps(addonDir, packageNames, logger) {
507
+ async function rebuildNativeDeps(addonDir, packageNames, logger, npmCacheDir, registry) {
251
508
  if (typeof process.versions.electron === "string") {
252
509
  const electronVersion = process.versions.electron;
253
510
  logger.info("Rebuilding native deps for Electron", { meta: {
@@ -291,7 +548,10 @@ async function rebuildNativeDeps(addonDir, packageNames, logger) {
291
548
  packages: packageNames
292
549
  } });
293
550
  try {
294
- await execFileAsync("npm", ["rebuild", ...packageNames], {
551
+ await runNpm(["rebuild", ...packageNames], {
552
+ cacheDir: npmCacheDir,
553
+ registry,
554
+ logger,
295
555
  cwd: addonDir,
296
556
  timeout: 6e5
297
557
  });
@@ -5583,6 +5843,30 @@ function safeExistsSync(path) {
5583
5843
  return false;
5584
5844
  }
5585
5845
  }
5846
+ /**
5847
+ * Detect a Coral USB Edge TPU: a matching USB device (`1a6e:089a` pre-init or
5848
+ * `18d1:9302` post-init) OR the installed libedgetpu delegate (the hub
5849
+ * provisions it out-of-band; its presence is a reliable "Coral is wired"
5850
+ * signal even where the container's `/sys` USB view is limited).
5851
+ */
5852
+ function detectCoralUsb() {
5853
+ const CORAL_IDS = [{
5854
+ vendor: "1a6e",
5855
+ product: "089a"
5856
+ }, {
5857
+ vendor: "18d1",
5858
+ product: "9302"
5859
+ }];
5860
+ try {
5861
+ for (const device of fs.readdirSync("/sys/bus/usb/devices")) try {
5862
+ const base = `/sys/bus/usb/devices/${device}`;
5863
+ const vendor = fs.readFileSync(`${base}/idVendor`, "utf8").trim().toLowerCase();
5864
+ const product = fs.readFileSync(`${base}/idProduct`, "utf8").trim().toLowerCase();
5865
+ if (CORAL_IDS.some((id) => id.vendor === vendor && id.product === product)) return true;
5866
+ } catch {}
5867
+ } catch {}
5868
+ return safeExistsSync(process.env["CAMSTACK_EDGETPU_LIB"] ?? "/data/deps/edgetpu/lib/libedgetpu.so.1");
5869
+ }
5586
5870
  /** Probe Linux GPU vendors by parsing /proc/bus/pci/devices or /sys/class/drm. */
5587
5871
  function probeLinuxGpus() {
5588
5872
  const signals = {
@@ -5590,7 +5874,8 @@ function probeLinuxGpus() {
5590
5874
  hasAmdGpu: false,
5591
5875
  hasIntelGpu: false,
5592
5876
  hasRenderNode: false,
5593
- hasIntelNpu: safeExistsSync("/dev/accel/accel0")
5877
+ hasIntelNpu: safeExistsSync("/dev/accel/accel0"),
5878
+ hasCoral: detectCoralUsb()
5594
5879
  };
5595
5880
  signals.hasRenderNode = safeExistsSync("/dev/dri/renderD128");
5596
5881
  if (safeExistsSync("/dev/nvidia0") || safeExistsSync("/proc/driver/nvidia/version")) signals.hasNvidiaGpu = true;
@@ -5623,7 +5908,8 @@ function probePlatform() {
5623
5908
  hasAmdGpu: false,
5624
5909
  hasIntelGpu: false,
5625
5910
  hasRenderNode: false,
5626
- hasIntelNpu: false
5911
+ hasIntelNpu: false,
5912
+ hasCoral: safeExistsSync(process.env["CAMSTACK_EDGETPU_LIB"] ?? "/data/deps/edgetpu/lib/libedgetpu.so.1")
5627
5913
  };
5628
5914
  }
5629
5915
  function pickBackendsForPlatform(signals) {
@@ -5708,7 +5994,8 @@ function createKernelHwAccel() {
5708
5994
  function mapSignalsToInferenceHardware(signals) {
5709
5995
  return {
5710
5996
  gpu: signals.hasNvidiaGpu ? { type: "nvidia" } : signals.hasIntelGpu || signals.hasRenderNode ? { type: "intel" } : null,
5711
- npu: signals.platform === "darwin" && signals.arch === "arm64" ? { type: "apple-ane" } : signals.hasIntelNpu ? { type: "intel-npu" } : null
5997
+ npu: signals.platform === "darwin" && signals.arch === "arm64" ? { type: "apple-ane" } : signals.hasIntelNpu ? { type: "intel-npu" } : null,
5998
+ coral: signals.hasCoral ? { type: "coral-edgetpu" } : null
5712
5999
  };
5713
6000
  }
5714
6001
  /**
@@ -5720,122 +6007,6 @@ function createKernelInferenceEngine() {
5720
6007
  return { resolveHardware: async () => mapSignalsToInferenceHardware(probePlatform()) };
5721
6008
  }
5722
6009
  //#endregion
5723
- //#region src/kernel/moleculer/addon-data-plane-facility.ts
5724
- /**
5725
- * Kernel side of the addon HTTP **data-plane** (see {@link AddonDataPlane}).
5726
- *
5727
- * Each addon context gets one facility. On the first `serve()`, it binds a
5728
- * `127.0.0.1` HTTP listener IN THE ADDON'S PROCESS and multiplexes registered
5729
- * prefixes → the addon's real `(req, res)` handlers (longest-prefix wins). Every
5730
- * request must carry the per-listener shared secret (injected by the hub's
5731
- * reverse-proxy) — anything else gets 403, so no other local process can reach
5732
- * the listener. The facility reports its live endpoints through a `sink` so the
5733
- * runner can hand them to the hub (which pulls them when it mounts the addon).
5734
- *
5735
- * The addon never sees the secret, the port, or any transport detail — it just
5736
- * streams to `res`. The hub authenticates and reverse-proxies in front.
5737
- */
5738
- function trimSlashes(s) {
5739
- return s.replace(/^\/+/, "").replace(/\/+$/, "");
5740
- }
5741
- function createAddonDataPlaneFacility(args) {
5742
- const bindHost = args.bindHost ?? "127.0.0.1";
5743
- const secret = randomBytes(24).toString("hex");
5744
- const served = /* @__PURE__ */ new Map();
5745
- let server = null;
5746
- let baseUrl = "";
5747
- const route = (req, res) => {
5748
- if (req.headers[DATAPLANE_SECRET_HEADER] !== secret) {
5749
- res.writeHead(403).end();
5750
- return;
5751
- }
5752
- const normalized = `/${trimSlashes((req.url ?? "/").split("?")[0] ?? "/")}`;
5753
- let best = null;
5754
- for (const [prefix, entry] of served) {
5755
- const p = `/${prefix}`;
5756
- if (normalized === p || normalized.startsWith(`${p}/`)) {
5757
- if (best === null || p.length > `/${best.prefix}`.length) best = {
5758
- prefix,
5759
- entry,
5760
- rest: normalized.slice(p.length) || "/"
5761
- };
5762
- }
5763
- }
5764
- if (best === null) {
5765
- res.writeHead(404).end();
5766
- return;
5767
- }
5768
- const original = req.url ?? "";
5769
- const qIdx = original.indexOf("?");
5770
- req.url = best.rest + (qIdx >= 0 ? original.slice(qIdx) : "");
5771
- Promise.resolve(best.entry.handler(req, res)).catch((err) => {
5772
- args.logger.warn("data-plane handler threw", { meta: {
5773
- prefix: best.prefix,
5774
- error: err instanceof Error ? err.message : String(err)
5775
- } });
5776
- if (!res.headersSent) res.writeHead(500).end();
5777
- else res.end();
5778
- });
5779
- };
5780
- const ensureServer = async () => {
5781
- if (server) return;
5782
- const s = createServer((req, res) => route(req, res));
5783
- await new Promise((resolve, reject) => {
5784
- s.once("error", reject);
5785
- s.listen(0, bindHost, () => resolve());
5786
- });
5787
- const addr = s.address();
5788
- const port = typeof addr === "object" && addr ? addr.port : 0;
5789
- server = s;
5790
- baseUrl = `http://${bindHost}:${port}`;
5791
- };
5792
- const publish = () => {
5793
- const endpoints = [...served.entries()].map(([prefix, entry]) => ({
5794
- prefix,
5795
- access: entry.access,
5796
- baseUrl,
5797
- secret
5798
- }));
5799
- args.sink?.set(args.addonId, endpoints);
5800
- };
5801
- const dataPlane = { serve: async (options) => {
5802
- const prefix = trimSlashes(options.prefix);
5803
- if (prefix.length === 0) throw new Error("dataPlane.serve: a non-empty prefix is required");
5804
- await ensureServer();
5805
- served.set(prefix, {
5806
- access: options.access,
5807
- handler: options.handler
5808
- });
5809
- publish();
5810
- args.logger.info("data-plane endpoint served", { meta: {
5811
- prefix,
5812
- access: options.access,
5813
- baseUrl
5814
- } });
5815
- return {
5816
- baseUrl,
5817
- prefix,
5818
- dispose: async () => {
5819
- served.delete(prefix);
5820
- publish();
5821
- }
5822
- };
5823
- } };
5824
- const dispose = async () => {
5825
- served.clear();
5826
- args.sink?.set(args.addonId, []);
5827
- if (server) {
5828
- const s = server;
5829
- server = null;
5830
- await new Promise((resolve) => s.close(() => resolve()));
5831
- }
5832
- };
5833
- return {
5834
- dataPlane,
5835
- dispose
5836
- };
5837
- }
5838
- //#endregion
5839
6010
  //#region src/kernel/deps/addon-deps-manager.ts
5840
6011
  /**
5841
6012
  * Canonical implementation of `IAddonDepsManager`, injected into every
@@ -7123,4 +7294,4 @@ async function installManifestPythonDeps(declaration, addonDir, deps, logger) {
7123
7294
  await deps.installPythonRequirements(reqAbs);
7124
7295
  }
7125
7296
  //#endregion
7126
- export { buildUdsNativeCapProxy as $, createParentUnownedCallHandler as A, AGENT_CAP_FWD_SERVICE as B, buildLinkChain as C, HUB_CAP_FWD_ACTION as D, localProviderLink as E, createUdsLoggerWithControl as F, createLocalTransport as G, CapRouteError as H, LocalChildClient as I, SocketChannel as J, UdsLocalTransportClient as K, LocalChildRegistry as L, createUdsEventBus as M, udsChildLogToWorkerEntry as N, HUB_CAP_FWD_SERVICE as O, createUdsLogger as P, buildNativeCapProxy as Q, UDS_NO_ROUTE_PREFIX as R, brokerTransportLink as S, ipcParentLink as T, classifyCapRoute as U, CapRouteResolver as V, callWithServiceDiscovery as W, FrameDecoder as X, localEndpointPath as Y, encodeFrame as Z, resolveHwAccel as _, CapabilityUnavailableError as _t, getWorkerDeviceRegistry as a, createAddonService as at, getCapUsageRegistry as b, setHubConnected as c, capActionName as ct, getMoleculerEventStats as d, capServiceName as dt, createBrokerDeviceManagerApi as et, registerEventBusService as f, parseCapAction as ft, createKernelHwAccel as g, CapabilityHandle as gt, AddonDepsManager as h, DeviceRegistry as ht, createUdsAddonContext as i, setWorkerNativeCapsChangeListener as it, createUdsEventBridge as j, createHubCapForwardService as k, EVENT_TOPIC_PREFIX as l, capActionSuffix as lt, subscribePassthrough as m, serializeTypedArrays as mt, adaptBrokerToCluster as n, getWorkerNativeCapSnapshot as nt, getOrInitReadinessRegistry as o, validateProviderRegistrations as ot, setNodeEventInterest as p, deserializeTypedArrays as pt, UdsLocalTransportServer as q, createAddonContext as r, mountNativeCapService as rt, getOrInitReadinessRegistryForClient as s, NATIVE_PROVIDER_SERVICE_INFIX as st, installManifestPythonDeps as t, getWorkerNativeCapProvider as tt, getBrokerEventBus as u, capBareAction as ut, CapUsageRegistry as v, installManifestNativeDeps as vt, ipcChildLink as w, brokerCallForCap as x, __resetCapUsageRegistryForTests as y, resolveAddonClass as yt, AGENT_CAP_FWD_ACTION as z };
7297
+ export { buildUdsNativeCapProxy as $, createParentUnownedCallHandler as A, AGENT_CAP_FWD_SERVICE as B, buildLinkChain as C, HUB_CAP_FWD_ACTION as D, localProviderLink as E, createUdsLoggerWithControl as F, createLocalTransport as G, CapRouteError as H, LocalChildClient as I, SocketChannel as J, UdsLocalTransportClient as K, LocalChildRegistry as L, createUdsEventBus as M, udsChildLogToWorkerEntry as N, HUB_CAP_FWD_SERVICE as O, createUdsLogger as P, buildNativeCapProxy as Q, UDS_NO_ROUTE_PREFIX as R, brokerTransportLink as S, ipcParentLink as T, classifyCapRoute as U, CapRouteResolver as V, callWithServiceDiscovery as W, FrameDecoder as X, localEndpointPath as Y, encodeFrame as Z, resolveHwAccel as _, CapabilityUnavailableError as _t, getWorkerDeviceRegistry as a, createAddonService as at, getCapUsageRegistry as b, resolveAddonClass as bt, setHubConnected as c, capActionName as ct, getMoleculerEventStats as d, capServiceName as dt, createBrokerDeviceManagerApi as et, registerEventBusService as f, parseCapAction as ft, createKernelHwAccel as g, CapabilityHandle as gt, AddonDepsManager as h, DeviceRegistry as ht, createUdsAddonContext as i, setWorkerNativeCapsChangeListener as it, createUdsEventBridge as j, createHubCapForwardService as k, EVENT_TOPIC_PREFIX as l, capActionSuffix as lt, subscribePassthrough as m, serializeTypedArrays as mt, adaptBrokerToCluster as n, getWorkerNativeCapSnapshot as nt, getOrInitReadinessRegistry as o, validateProviderRegistrations as ot, setNodeEventInterest as p, deserializeTypedArrays as pt, UdsLocalTransportServer as q, createAddonContext as r, mountNativeCapService as rt, getOrInitReadinessRegistryForClient as s, NATIVE_PROVIDER_SERVICE_INFIX as st, installManifestPythonDeps as t, getWorkerNativeCapProvider as tt, getBrokerEventBus as u, capBareAction as ut, CapUsageRegistry as v, installManifestNativeDeps as vt, ipcChildLink as w, brokerCallForCap as x, createAddonDataPlaneFacility as xt, __resetCapUsageRegistryForTests as y, runNpm as yt, AGENT_CAP_FWD_ACTION as z };