@camstack/system 1.1.50 → 1.1.52

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 (64) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  9. package/dist/builtins/alerts/alerts.addon.js +1 -1
  10. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/console-logging/index.js +1 -1
  14. package/dist/builtins/console-logging/index.mjs +1 -1
  15. package/dist/builtins/device-manager/device-linked-devices.d.ts +47 -0
  16. package/dist/builtins/device-manager/device-manager.addon.js +1840 -1712
  17. package/dist/builtins/device-manager/device-manager.addon.mjs +1840 -1712
  18. package/dist/builtins/doorbell/index.d.ts +2 -0
  19. package/dist/builtins/doorbell/index.js +8 -0
  20. package/dist/builtins/doorbell/index.mjs +2 -0
  21. package/dist/builtins/doorbell/trigger-engine.d.ts +69 -0
  22. package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +87 -0
  23. package/dist/builtins/doorbell/virtual-doorbell.addon.js +489 -0
  24. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +483 -0
  25. package/dist/builtins/hub-forwarder/index.js +1 -1
  26. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  27. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  28. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  29. package/dist/builtins/local-network/local-network.addon.js +1 -1
  30. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  33. package/dist/builtins/platform-probe/index.js +1 -1
  34. package/dist/builtins/platform-probe/index.mjs +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  37. package/dist/builtins/snapshot/index.js +162 -12
  38. package/dist/builtins/snapshot/index.mjs +162 -12
  39. package/dist/builtins/snapshot/snapshot-media-handler.d.ts +38 -0
  40. package/dist/builtins/snapshot/snapshot.addon.d.ts +26 -0
  41. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  42. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  44. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  46. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  47. package/dist/builtins/system-config/system-config.addon.js +4 -4
  48. package/dist/builtins/system-config/system-config.addon.mjs +4 -4
  49. package/dist/builtins/winston-logging/index.js +1 -1
  50. package/dist/builtins/winston-logging/index.mjs +1 -1
  51. package/dist/{dist-CC3E-_3h.mjs → dist-B_PSvr-m.mjs} +237 -13
  52. package/dist/{dist-CQYo6Zp8.js → dist-BduBhv6N.js} +242 -12
  53. package/dist/http/file-data-plane.d.ts +8 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +35 -23
  56. package/dist/index.mjs +35 -24
  57. package/dist/kernel/addon-installer.d.ts +8 -0
  58. package/dist/kernel/deps/manifest-native-deps.d.ts +1 -1
  59. package/dist/kernel/deps/npm-command.d.ts +38 -0
  60. package/dist/{manifest-python-deps-DqATCsK0.mjs → manifest-python-deps-CIyvuHAB.mjs} +268 -124
  61. package/dist/{manifest-python-deps-Wy8dNJpr.js → manifest-python-deps-CPAMH5gV.js} +279 -123
  62. package/dist/{model-download-service-C-IHWnXx.mjs → model-download-service-Cp9f4dk6.mjs} +1 -1
  63. package/dist/{model-download-service-D-Umz4-L.js → model-download-service-hf0ookyy.js} +1 -1
  64. package/package.json +13 -1
@@ -1,4 +1,4 @@
1
- import { G as readNodePin } from "./dist-CC3E-_3h.mjs";
1
+ import { K as readNodePin } from "./dist-B_PSvr-m.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
  });
@@ -5747,122 +6007,6 @@ function createKernelInferenceEngine() {
5747
6007
  return { resolveHardware: async () => mapSignalsToInferenceHardware(probePlatform()) };
5748
6008
  }
5749
6009
  //#endregion
5750
- //#region src/kernel/moleculer/addon-data-plane-facility.ts
5751
- /**
5752
- * Kernel side of the addon HTTP **data-plane** (see {@link AddonDataPlane}).
5753
- *
5754
- * Each addon context gets one facility. On the first `serve()`, it binds a
5755
- * `127.0.0.1` HTTP listener IN THE ADDON'S PROCESS and multiplexes registered
5756
- * prefixes → the addon's real `(req, res)` handlers (longest-prefix wins). Every
5757
- * request must carry the per-listener shared secret (injected by the hub's
5758
- * reverse-proxy) — anything else gets 403, so no other local process can reach
5759
- * the listener. The facility reports its live endpoints through a `sink` so the
5760
- * runner can hand them to the hub (which pulls them when it mounts the addon).
5761
- *
5762
- * The addon never sees the secret, the port, or any transport detail — it just
5763
- * streams to `res`. The hub authenticates and reverse-proxies in front.
5764
- */
5765
- function trimSlashes(s) {
5766
- return s.replace(/^\/+/, "").replace(/\/+$/, "");
5767
- }
5768
- function createAddonDataPlaneFacility(args) {
5769
- const bindHost = args.bindHost ?? "127.0.0.1";
5770
- const secret = randomBytes(24).toString("hex");
5771
- const served = /* @__PURE__ */ new Map();
5772
- let server = null;
5773
- let baseUrl = "";
5774
- const route = (req, res) => {
5775
- if (req.headers[DATAPLANE_SECRET_HEADER] !== secret) {
5776
- res.writeHead(403).end();
5777
- return;
5778
- }
5779
- const normalized = `/${trimSlashes((req.url ?? "/").split("?")[0] ?? "/")}`;
5780
- let best = null;
5781
- for (const [prefix, entry] of served) {
5782
- const p = `/${prefix}`;
5783
- if (normalized === p || normalized.startsWith(`${p}/`)) {
5784
- if (best === null || p.length > `/${best.prefix}`.length) best = {
5785
- prefix,
5786
- entry,
5787
- rest: normalized.slice(p.length) || "/"
5788
- };
5789
- }
5790
- }
5791
- if (best === null) {
5792
- res.writeHead(404).end();
5793
- return;
5794
- }
5795
- const original = req.url ?? "";
5796
- const qIdx = original.indexOf("?");
5797
- req.url = best.rest + (qIdx >= 0 ? original.slice(qIdx) : "");
5798
- Promise.resolve(best.entry.handler(req, res)).catch((err) => {
5799
- args.logger.warn("data-plane handler threw", { meta: {
5800
- prefix: best.prefix,
5801
- error: err instanceof Error ? err.message : String(err)
5802
- } });
5803
- if (!res.headersSent) res.writeHead(500).end();
5804
- else res.end();
5805
- });
5806
- };
5807
- const ensureServer = async () => {
5808
- if (server) return;
5809
- const s = createServer((req, res) => route(req, res));
5810
- await new Promise((resolve, reject) => {
5811
- s.once("error", reject);
5812
- s.listen(0, bindHost, () => resolve());
5813
- });
5814
- const addr = s.address();
5815
- const port = typeof addr === "object" && addr ? addr.port : 0;
5816
- server = s;
5817
- baseUrl = `http://${bindHost}:${port}`;
5818
- };
5819
- const publish = () => {
5820
- const endpoints = [...served.entries()].map(([prefix, entry]) => ({
5821
- prefix,
5822
- access: entry.access,
5823
- baseUrl,
5824
- secret
5825
- }));
5826
- args.sink?.set(args.addonId, endpoints);
5827
- };
5828
- const dataPlane = { serve: async (options) => {
5829
- const prefix = trimSlashes(options.prefix);
5830
- if (prefix.length === 0) throw new Error("dataPlane.serve: a non-empty prefix is required");
5831
- await ensureServer();
5832
- served.set(prefix, {
5833
- access: options.access,
5834
- handler: options.handler
5835
- });
5836
- publish();
5837
- args.logger.info("data-plane endpoint served", { meta: {
5838
- prefix,
5839
- access: options.access,
5840
- baseUrl
5841
- } });
5842
- return {
5843
- baseUrl,
5844
- prefix,
5845
- dispose: async () => {
5846
- served.delete(prefix);
5847
- publish();
5848
- }
5849
- };
5850
- } };
5851
- const dispose = async () => {
5852
- served.clear();
5853
- args.sink?.set(args.addonId, []);
5854
- if (server) {
5855
- const s = server;
5856
- server = null;
5857
- await new Promise((resolve) => s.close(() => resolve()));
5858
- }
5859
- };
5860
- return {
5861
- dataPlane,
5862
- dispose
5863
- };
5864
- }
5865
- //#endregion
5866
6010
  //#region src/kernel/deps/addon-deps-manager.ts
5867
6011
  /**
5868
6012
  * Canonical implementation of `IAddonDepsManager`, injected into every
@@ -7150,4 +7294,4 @@ async function installManifestPythonDeps(declaration, addonDir, deps, logger) {
7150
7294
  await deps.installPythonRequirements(reqAbs);
7151
7295
  }
7152
7296
  //#endregion
7153
- 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 };