@camstack/system 1.0.4 → 1.0.6

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.
@@ -7,6 +7,9 @@ let node_path = require("node:path");
7
7
  node_path = require_chunk.__toESM(node_path);
8
8
  let _camstack_types = require("@camstack/types");
9
9
  let node_crypto = require("node:crypto");
10
+ node_crypto = require_chunk.__toESM(node_crypto);
11
+ let node_child_process = require("node:child_process");
12
+ let node_util = require("node:util");
10
13
  let node_os = require("node:os");
11
14
  node_os = require_chunk.__toESM(node_os);
12
15
  let node_fs_promises = require("node:fs/promises");
@@ -52,6 +55,255 @@ function resolveAddonClass(mod) {
52
55
  if (namedAddon) return namedAddon;
53
56
  }
54
57
  //#endregion
58
+ //#region src/kernel/deps/manifest-native-deps.ts
59
+ var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
60
+ /**
61
+ * Native node modules an addon needs at runtime but cannot be bundled
62
+ * (`.node` binary files require ABI-matched compilation). Mirror of the
63
+ * Python `requirements.txt` pattern in `manifest-python-deps.ts` —
64
+ * declared in the addon's `package.json` under `camstack.nativeDependencies`,
65
+ * installed per-addon at install time so the host's regular `npm install`
66
+ * doesn't pull in the union of every camera driver's native deps.
67
+ *
68
+ * Phase E of the bundles + builder modernization spec
69
+ * (`docs/superpowers/specs/2026-05-09-bundles-and-builder-modernization-design.md`).
70
+ *
71
+ * Idempotent: hashes the `nativeDependencies` map and writes a marker
72
+ * to `<addonDir>/.camstack-native-deps-installed`. Re-installing only
73
+ * happens when the declared set changes.
74
+ *
75
+ * Failure modes:
76
+ * - manifest doesn't declare `nativeDependencies` (or declares empty) → no-op
77
+ * - `npm install` fails → throws (caller decides whether to abort install)
78
+ * - rebuild step fails → logs warning + continues (install may still
79
+ * work if prebuilt binaries shipped in the package cover the host
80
+ * ABI; surface a clear error at first `import` of the native module
81
+ * otherwise).
82
+ */
83
+ async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
84
+ const native = readNativeDeps(pkgRaw);
85
+ if (native == null || Object.keys(native).length === 0) return;
86
+ const markerFile = node_path.join(addonDir, ".camstack-native-deps-installed");
87
+ const markerHash = hashDeclaration(native);
88
+ if (markerMatches(markerFile, markerHash)) {
89
+ logger.debug("Native deps already installed (marker matches)", { meta: {
90
+ addonDir,
91
+ count: Object.keys(native).length
92
+ } });
93
+ return;
94
+ }
95
+ const pending = Object.entries(native).filter(([name]) => !copyPrebuiltNativeDep(name, addonDir, logger));
96
+ if (pending.length === 0) {
97
+ logger.info("Native deps satisfied from prebuilt host modules (offline)", { meta: {
98
+ addonDir,
99
+ count: Object.keys(native).length
100
+ } });
101
+ try {
102
+ node_fs.writeFileSync(markerFile, markerHash);
103
+ } catch (err) {
104
+ logger.warn("Failed to write native deps marker", { meta: {
105
+ markerFile,
106
+ error: (0, _camstack_types.errMsg)(err)
107
+ } });
108
+ }
109
+ return;
110
+ }
111
+ const specs = pending.map(([name, range]) => `${name}@${range}`);
112
+ logger.info("Installing native dependencies", { meta: {
113
+ addonDir,
114
+ specs
115
+ } });
116
+ const args = [
117
+ "install",
118
+ "--no-save",
119
+ "--no-package-lock",
120
+ "--no-audit",
121
+ "--no-fund",
122
+ "--omit=dev",
123
+ "--omit=peer",
124
+ ...registry ? ["--registry", registry] : [],
125
+ ...specs
126
+ ];
127
+ try {
128
+ await execFileAsync("npm", args, {
129
+ cwd: addonDir,
130
+ timeout: 3e5
131
+ });
132
+ } catch (err) {
133
+ throw new Error(`npm install of native deps failed for ${addonDir}: ${(0, _camstack_types.errMsg)(err)}`, { cause: err });
134
+ }
135
+ await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger);
136
+ try {
137
+ node_fs.writeFileSync(markerFile, markerHash);
138
+ } catch (err) {
139
+ logger.warn("Failed to write native deps marker", { meta: {
140
+ markerFile,
141
+ error: (0, _camstack_types.errMsg)(err)
142
+ } });
143
+ }
144
+ }
145
+ /**
146
+ * Satisfy a native dep from an already-built copy on the host instead of a
147
+ * runtime `npm install`. Searches NODE_PATH and the node_modules dirs above
148
+ * `addonDir` for `<name>` carrying a compiled binary, and copies the whole
149
+ * package into `<addonDir>/node_modules/<name>`. Safe: returns false on any
150
+ * miss/error so the caller falls back to the network install.
151
+ */
152
+ function copyPrebuiltNativeDep(name, addonDir, logger) {
153
+ const target = node_path.join(addonDir, "node_modules", name);
154
+ if (hasBuiltBinary(target)) return true;
155
+ for (const source of candidateHoistedDirs(name, addonDir)) {
156
+ if (source === target) continue;
157
+ if (!hasBuiltBinary(source)) continue;
158
+ try {
159
+ node_fs.rmSync(target, {
160
+ recursive: true,
161
+ force: true
162
+ });
163
+ node_fs.mkdirSync(node_path.dirname(target), { recursive: true });
164
+ node_fs.cpSync(source, target, {
165
+ recursive: true,
166
+ dereference: true
167
+ });
168
+ logger.info("Native dep copied from prebuilt host module (offline)", { meta: {
169
+ name,
170
+ source,
171
+ target
172
+ } });
173
+ return true;
174
+ } catch (err) {
175
+ logger.warn("Prebuilt native dep copy failed — will try npm install", { meta: {
176
+ name,
177
+ source,
178
+ error: (0, _camstack_types.errMsg)(err)
179
+ } });
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+ /** A package dir is "built" when it carries a compiled `.node` or a prebuilds tree. */
185
+ function hasBuiltBinary(pkgDir) {
186
+ if (!node_fs.existsSync(node_path.join(pkgDir, "package.json"))) return false;
187
+ const releaseDir = node_path.join(pkgDir, "build", "Release");
188
+ try {
189
+ if (node_fs.existsSync(releaseDir) && node_fs.readdirSync(releaseDir).some((f) => f.endsWith(".node"))) return true;
190
+ } catch {}
191
+ return node_fs.existsSync(node_path.join(pkgDir, "prebuilds"));
192
+ }
193
+ /** Candidate hoisted locations for `<name>`: NODE_PATH entries + ancestors of `addonDir`. */
194
+ function candidateHoistedDirs(name, addonDir) {
195
+ const dirs = [];
196
+ const nodePath = process.env["NODE_PATH"];
197
+ if (nodePath) {
198
+ for (const p of nodePath.split(node_path.delimiter)) if (p) dirs.push(node_path.join(p, name));
199
+ }
200
+ let cur = addonDir;
201
+ for (let i = 0; i < 10; i++) {
202
+ dirs.push(node_path.join(cur, "node_modules", name));
203
+ const parent = node_path.dirname(cur);
204
+ if (parent === cur) break;
205
+ cur = parent;
206
+ }
207
+ return dirs;
208
+ }
209
+ /** Read & validate `camstack.nativeDependencies`. Returns null when absent. */
210
+ function readNativeDeps(pkgRaw) {
211
+ const camstack = (0, _camstack_types.asJsonObject)(pkgRaw["camstack"]);
212
+ if (!camstack) return null;
213
+ const native = (0, _camstack_types.asJsonObject)(camstack["nativeDependencies"]);
214
+ if (!native) return null;
215
+ const out = {};
216
+ for (const [k, v] of Object.entries(native)) {
217
+ const range = (0, _camstack_types.asString)(v);
218
+ if (range) out[k] = range;
219
+ }
220
+ return Object.keys(out).length > 0 ? out : null;
221
+ }
222
+ /** SHA-256 of the canonical-keyed declaration — drives marker idempotency. */
223
+ function hashDeclaration(deps) {
224
+ const canonical = Object.keys(deps).toSorted().map((k) => `${k}@${deps[k]}`).join("\n");
225
+ return node_crypto.createHash("sha256").update(canonical).digest("hex");
226
+ }
227
+ function markerMatches(markerFile, expected) {
228
+ try {
229
+ return node_fs.readFileSync(markerFile, "utf-8").trim() === expected;
230
+ } catch {
231
+ return false;
232
+ }
233
+ }
234
+ /**
235
+ * Rebuild native modules against the host's runtime ABI.
236
+ *
237
+ * Detection: `process.versions.electron` is set when running inside
238
+ * Electron's main/renderer process (set even when `ELECTRON_RUN_AS_NODE`
239
+ * isn't), giving us a reliable signal. Plain Node leaves it undefined.
240
+ *
241
+ * Electron path: tries `@electron/rebuild` programmatically, falling
242
+ * back to `npx electron-rebuild` if the package isn't directly available.
243
+ * Node path: `npm rebuild --prefix <addonDir>` re-runs the install
244
+ * scripts of every package under that directory.
245
+ *
246
+ * Failures here are non-fatal: many native packages ship prebuilt
247
+ * binaries that cover both ABIs, so the rebuild may be unnecessary.
248
+ * The first `import` of an actually-mismatched binary throws a clear
249
+ * error that points the operator at this rebuild step.
250
+ */
251
+ async function rebuildNativeDeps(addonDir, packageNames, logger) {
252
+ if (typeof process.versions.electron === "string") {
253
+ const electronVersion = process.versions.electron;
254
+ logger.info("Rebuilding native deps for Electron", { meta: {
255
+ addonDir,
256
+ electronVersion,
257
+ packages: packageNames
258
+ } });
259
+ try {
260
+ const rebuildModule = await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./main-B_G1JH3Q.js").default)).catch(() => null);
261
+ if (rebuildModule?.rebuild) {
262
+ await rebuildModule.rebuild({
263
+ buildPath: addonDir,
264
+ electronVersion,
265
+ onlyModules: packageNames
266
+ });
267
+ return;
268
+ }
269
+ logger.warn("@electron/rebuild not available — falling back to npx", { meta: { addonDir } });
270
+ await execFileAsync("npx", [
271
+ "--yes",
272
+ "electron-rebuild",
273
+ "-m",
274
+ addonDir,
275
+ "-v",
276
+ electronVersion
277
+ ], {
278
+ cwd: addonDir,
279
+ timeout: 6e5
280
+ });
281
+ } catch (err) {
282
+ logger.warn("Electron rebuild failed (continuing — prebuilt binary may be present)", { meta: {
283
+ addonDir,
284
+ error: (0, _camstack_types.errMsg)(err)
285
+ } });
286
+ }
287
+ return;
288
+ }
289
+ logger.info("Rebuilding native deps for Node", { meta: {
290
+ addonDir,
291
+ nodeAbi: process.versions.modules,
292
+ packages: packageNames
293
+ } });
294
+ try {
295
+ await execFileAsync("npm", ["rebuild", ...packageNames], {
296
+ cwd: addonDir,
297
+ timeout: 6e5
298
+ });
299
+ } catch (err) {
300
+ logger.warn("npm rebuild failed (continuing — prebuilt binary may be present)", { meta: {
301
+ addonDir,
302
+ error: (0, _camstack_types.errMsg)(err)
303
+ } });
304
+ }
305
+ }
306
+ //#endregion
55
307
  //#region src/kernel/capability-handle.ts
56
308
  var CapabilityUnavailableError = class extends Error {
57
309
  capName;
@@ -6183,6 +6435,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
6183
6435
  localNodeId: nodeId,
6184
6436
  storage: storage ?? void 0,
6185
6437
  capabilityRegistry: options?.capabilityRegistry,
6438
+ ...options?.listStorageLocationDeclarations ? { listStorageLocationDeclarations: options.listStorageLocationDeclarations } : {},
6186
6439
  readinessRegistry: readinessRegistry(),
6187
6440
  deviceRegistry: workerDeviceRegistry,
6188
6441
  devices: createBrokerDeviceManagerApi({
@@ -6632,6 +6885,12 @@ Object.defineProperty(exports, "getWorkerNativeCapSnapshot", {
6632
6885
  return getWorkerNativeCapSnapshot;
6633
6886
  }
6634
6887
  });
6888
+ Object.defineProperty(exports, "installManifestNativeDeps", {
6889
+ enumerable: true,
6890
+ get: function() {
6891
+ return installManifestNativeDeps;
6892
+ }
6893
+ });
6635
6894
  Object.defineProperty(exports, "installManifestPythonDeps", {
6636
6895
  enumerable: true,
6637
6896
  get: function() {
@@ -1,10 +1,14 @@
1
+ import { o as __toESM } from "./chunk-CNf5ZN-e.mjs";
1
2
  import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
2
3
  import { createServer } from "node:http";
3
4
  import * as fs from "node:fs";
4
5
  import * as path$1 from "node:path";
5
6
  import { isAbsolute, join } from "node:path";
6
- import { DATAPLANE_SECRET_HEADER, DeviceType, DisposerChain, EventCategory, ReadinessRegistry, createDeviceProxy, deviceOpsCapability, emitReadiness, expandCapMethods, scopeKey, sleep } from "@camstack/types";
7
+ import { DATAPLANE_SECRET_HEADER, DeviceType, DisposerChain, EventCategory, ReadinessRegistry, asJsonObject, asString, createDeviceProxy, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, scopeKey, sleep } from "@camstack/types";
8
+ import * as crypto$1 from "node:crypto";
7
9
  import { randomBytes, randomUUID } from "node:crypto";
10
+ import { execFile } from "node:child_process";
11
+ import { promisify } from "node:util";
8
12
  import * as os from "node:os";
9
13
  import { tmpdir } from "node:os";
10
14
  import { unlink } from "node:fs/promises";
@@ -50,6 +54,255 @@ function resolveAddonClass(mod) {
50
54
  if (namedAddon) return namedAddon;
51
55
  }
52
56
  //#endregion
57
+ //#region src/kernel/deps/manifest-native-deps.ts
58
+ var execFileAsync = promisify(execFile);
59
+ /**
60
+ * Native node modules an addon needs at runtime but cannot be bundled
61
+ * (`.node` binary files require ABI-matched compilation). Mirror of the
62
+ * Python `requirements.txt` pattern in `manifest-python-deps.ts` —
63
+ * declared in the addon's `package.json` under `camstack.nativeDependencies`,
64
+ * installed per-addon at install time so the host's regular `npm install`
65
+ * doesn't pull in the union of every camera driver's native deps.
66
+ *
67
+ * Phase E of the bundles + builder modernization spec
68
+ * (`docs/superpowers/specs/2026-05-09-bundles-and-builder-modernization-design.md`).
69
+ *
70
+ * Idempotent: hashes the `nativeDependencies` map and writes a marker
71
+ * to `<addonDir>/.camstack-native-deps-installed`. Re-installing only
72
+ * happens when the declared set changes.
73
+ *
74
+ * Failure modes:
75
+ * - manifest doesn't declare `nativeDependencies` (or declares empty) → no-op
76
+ * - `npm install` fails → throws (caller decides whether to abort install)
77
+ * - rebuild step fails → logs warning + continues (install may still
78
+ * work if prebuilt binaries shipped in the package cover the host
79
+ * ABI; surface a clear error at first `import` of the native module
80
+ * otherwise).
81
+ */
82
+ async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
83
+ const native = readNativeDeps(pkgRaw);
84
+ if (native == null || Object.keys(native).length === 0) return;
85
+ const markerFile = path$1.join(addonDir, ".camstack-native-deps-installed");
86
+ const markerHash = hashDeclaration(native);
87
+ if (markerMatches(markerFile, markerHash)) {
88
+ logger.debug("Native deps already installed (marker matches)", { meta: {
89
+ addonDir,
90
+ count: Object.keys(native).length
91
+ } });
92
+ return;
93
+ }
94
+ const pending = Object.entries(native).filter(([name]) => !copyPrebuiltNativeDep(name, addonDir, logger));
95
+ if (pending.length === 0) {
96
+ logger.info("Native deps satisfied from prebuilt host modules (offline)", { meta: {
97
+ addonDir,
98
+ count: Object.keys(native).length
99
+ } });
100
+ try {
101
+ fs.writeFileSync(markerFile, markerHash);
102
+ } catch (err) {
103
+ logger.warn("Failed to write native deps marker", { meta: {
104
+ markerFile,
105
+ error: errMsg(err)
106
+ } });
107
+ }
108
+ return;
109
+ }
110
+ const specs = pending.map(([name, range]) => `${name}@${range}`);
111
+ logger.info("Installing native dependencies", { meta: {
112
+ addonDir,
113
+ specs
114
+ } });
115
+ const args = [
116
+ "install",
117
+ "--no-save",
118
+ "--no-package-lock",
119
+ "--no-audit",
120
+ "--no-fund",
121
+ "--omit=dev",
122
+ "--omit=peer",
123
+ ...registry ? ["--registry", registry] : [],
124
+ ...specs
125
+ ];
126
+ try {
127
+ await execFileAsync("npm", args, {
128
+ cwd: addonDir,
129
+ timeout: 3e5
130
+ });
131
+ } catch (err) {
132
+ throw new Error(`npm install of native deps failed for ${addonDir}: ${errMsg(err)}`, { cause: err });
133
+ }
134
+ await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger);
135
+ try {
136
+ fs.writeFileSync(markerFile, markerHash);
137
+ } catch (err) {
138
+ logger.warn("Failed to write native deps marker", { meta: {
139
+ markerFile,
140
+ error: errMsg(err)
141
+ } });
142
+ }
143
+ }
144
+ /**
145
+ * Satisfy a native dep from an already-built copy on the host instead of a
146
+ * runtime `npm install`. Searches NODE_PATH and the node_modules dirs above
147
+ * `addonDir` for `<name>` carrying a compiled binary, and copies the whole
148
+ * package into `<addonDir>/node_modules/<name>`. Safe: returns false on any
149
+ * miss/error so the caller falls back to the network install.
150
+ */
151
+ function copyPrebuiltNativeDep(name, addonDir, logger) {
152
+ const target = path$1.join(addonDir, "node_modules", name);
153
+ if (hasBuiltBinary(target)) return true;
154
+ for (const source of candidateHoistedDirs(name, addonDir)) {
155
+ if (source === target) continue;
156
+ if (!hasBuiltBinary(source)) continue;
157
+ try {
158
+ fs.rmSync(target, {
159
+ recursive: true,
160
+ force: true
161
+ });
162
+ fs.mkdirSync(path$1.dirname(target), { recursive: true });
163
+ fs.cpSync(source, target, {
164
+ recursive: true,
165
+ dereference: true
166
+ });
167
+ logger.info("Native dep copied from prebuilt host module (offline)", { meta: {
168
+ name,
169
+ source,
170
+ target
171
+ } });
172
+ return true;
173
+ } catch (err) {
174
+ logger.warn("Prebuilt native dep copy failed — will try npm install", { meta: {
175
+ name,
176
+ source,
177
+ error: errMsg(err)
178
+ } });
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+ /** A package dir is "built" when it carries a compiled `.node` or a prebuilds tree. */
184
+ function hasBuiltBinary(pkgDir) {
185
+ if (!fs.existsSync(path$1.join(pkgDir, "package.json"))) return false;
186
+ const releaseDir = path$1.join(pkgDir, "build", "Release");
187
+ try {
188
+ if (fs.existsSync(releaseDir) && fs.readdirSync(releaseDir).some((f) => f.endsWith(".node"))) return true;
189
+ } catch {}
190
+ return fs.existsSync(path$1.join(pkgDir, "prebuilds"));
191
+ }
192
+ /** Candidate hoisted locations for `<name>`: NODE_PATH entries + ancestors of `addonDir`. */
193
+ function candidateHoistedDirs(name, addonDir) {
194
+ const dirs = [];
195
+ const nodePath = process.env["NODE_PATH"];
196
+ if (nodePath) {
197
+ for (const p of nodePath.split(path$1.delimiter)) if (p) dirs.push(path$1.join(p, name));
198
+ }
199
+ let cur = addonDir;
200
+ for (let i = 0; i < 10; i++) {
201
+ dirs.push(path$1.join(cur, "node_modules", name));
202
+ const parent = path$1.dirname(cur);
203
+ if (parent === cur) break;
204
+ cur = parent;
205
+ }
206
+ return dirs;
207
+ }
208
+ /** Read & validate `camstack.nativeDependencies`. Returns null when absent. */
209
+ function readNativeDeps(pkgRaw) {
210
+ const camstack = asJsonObject(pkgRaw["camstack"]);
211
+ if (!camstack) return null;
212
+ const native = asJsonObject(camstack["nativeDependencies"]);
213
+ if (!native) return null;
214
+ const out = {};
215
+ for (const [k, v] of Object.entries(native)) {
216
+ const range = asString(v);
217
+ if (range) out[k] = range;
218
+ }
219
+ return Object.keys(out).length > 0 ? out : null;
220
+ }
221
+ /** SHA-256 of the canonical-keyed declaration — drives marker idempotency. */
222
+ function hashDeclaration(deps) {
223
+ const canonical = Object.keys(deps).toSorted().map((k) => `${k}@${deps[k]}`).join("\n");
224
+ return crypto$1.createHash("sha256").update(canonical).digest("hex");
225
+ }
226
+ function markerMatches(markerFile, expected) {
227
+ try {
228
+ return fs.readFileSync(markerFile, "utf-8").trim() === expected;
229
+ } catch {
230
+ return false;
231
+ }
232
+ }
233
+ /**
234
+ * Rebuild native modules against the host's runtime ABI.
235
+ *
236
+ * Detection: `process.versions.electron` is set when running inside
237
+ * Electron's main/renderer process (set even when `ELECTRON_RUN_AS_NODE`
238
+ * isn't), giving us a reliable signal. Plain Node leaves it undefined.
239
+ *
240
+ * Electron path: tries `@electron/rebuild` programmatically, falling
241
+ * back to `npx electron-rebuild` if the package isn't directly available.
242
+ * Node path: `npm rebuild --prefix <addonDir>` re-runs the install
243
+ * scripts of every package under that directory.
244
+ *
245
+ * Failures here are non-fatal: many native packages ship prebuilt
246
+ * binaries that cover both ABIs, so the rebuild may be unnecessary.
247
+ * The first `import` of an actually-mismatched binary throws a clear
248
+ * error that points the operator at this rebuild step.
249
+ */
250
+ async function rebuildNativeDeps(addonDir, packageNames, logger) {
251
+ if (typeof process.versions.electron === "string") {
252
+ const electronVersion = process.versions.electron;
253
+ logger.info("Rebuilding native deps for Electron", { meta: {
254
+ addonDir,
255
+ electronVersion,
256
+ packages: packageNames
257
+ } });
258
+ try {
259
+ const rebuildModule = await import("./main-BOG1xxwD.mjs").then((m) => /* @__PURE__ */ __toESM(m.default)).catch(() => null);
260
+ if (rebuildModule?.rebuild) {
261
+ await rebuildModule.rebuild({
262
+ buildPath: addonDir,
263
+ electronVersion,
264
+ onlyModules: packageNames
265
+ });
266
+ return;
267
+ }
268
+ logger.warn("@electron/rebuild not available — falling back to npx", { meta: { addonDir } });
269
+ await execFileAsync("npx", [
270
+ "--yes",
271
+ "electron-rebuild",
272
+ "-m",
273
+ addonDir,
274
+ "-v",
275
+ electronVersion
276
+ ], {
277
+ cwd: addonDir,
278
+ timeout: 6e5
279
+ });
280
+ } catch (err) {
281
+ logger.warn("Electron rebuild failed (continuing — prebuilt binary may be present)", { meta: {
282
+ addonDir,
283
+ error: errMsg(err)
284
+ } });
285
+ }
286
+ return;
287
+ }
288
+ logger.info("Rebuilding native deps for Node", { meta: {
289
+ addonDir,
290
+ nodeAbi: process.versions.modules,
291
+ packages: packageNames
292
+ } });
293
+ try {
294
+ await execFileAsync("npm", ["rebuild", ...packageNames], {
295
+ cwd: addonDir,
296
+ timeout: 6e5
297
+ });
298
+ } catch (err) {
299
+ logger.warn("npm rebuild failed (continuing — prebuilt binary may be present)", { meta: {
300
+ addonDir,
301
+ error: errMsg(err)
302
+ } });
303
+ }
304
+ }
305
+ //#endregion
53
306
  //#region src/kernel/capability-handle.ts
54
307
  var CapabilityUnavailableError = class extends Error {
55
308
  capName;
@@ -6181,6 +6434,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
6181
6434
  localNodeId: nodeId,
6182
6435
  storage: storage ?? void 0,
6183
6436
  capabilityRegistry: options?.capabilityRegistry,
6437
+ ...options?.listStorageLocationDeclarations ? { listStorageLocationDeclarations: options.listStorageLocationDeclarations } : {},
6184
6438
  readinessRegistry: readinessRegistry(),
6185
6439
  deviceRegistry: workerDeviceRegistry,
6186
6440
  devices: createBrokerDeviceManagerApi({
@@ -6312,4 +6566,4 @@ async function installManifestPythonDeps(declaration, addonDir, deps, logger) {
6312
6566
  await deps.installPythonRequirements(reqAbs);
6313
6567
  }
6314
6568
  //#endregion
6315
- export { setWorkerNativeCapsChangeListener as $, createUdsLoggerWithControl as A, createLocalTransport as B, ipcParentLink as C, createUdsEventBus as D, createUdsEventBridge as E, CapRouteError as F, FrameDecoder as G, UdsLocalTransportServer as H, classifyCapRoute as I, buildUdsNativeCapProxy as J, encodeFrame as K, LocalChildClient as L, AGENT_CAP_FWD_SERVICE as M, CapRouteResolver as N, udsChildLogToWorkerEntry as O, callWithServiceDiscovery as P, mountNativeCapService as Q, LocalChildRegistry as R, ipcChildLink as S, createParentUnownedCallHandler as T, SocketChannel as U, UdsLocalTransportClient as V, localEndpointPath as W, getWorkerNativeCapProvider as X, createBrokerDeviceManagerApi as Y, getWorkerNativeCapSnapshot as Z, __resetCapUsageRegistryForTests as _, getWorkerDeviceRegistry as a, capBareAction as at, brokerTransportLink as b, setHubConnected as c, deserializeTypedArrays as ct, registerEventBusService as d, CustomActionRegistry as dt, createAddonService as et, AddonDepsManager as f, CapabilityHandle as ft, CapUsageRegistry as g, createHwAccelService as h, createUdsAddonContext as i, capActionSuffix as it, AGENT_CAP_FWD_ACTION as j, createUdsLogger as k, EVENT_TOPIC_PREFIX as l, serializeTypedArrays as lt, resolveHwAccel as m, resolveAddonClass as mt, adaptBrokerToCluster as n, NATIVE_PROVIDER_SERVICE_INFIX as nt, getOrInitReadinessRegistry as o, capServiceName as ot, createKernelHwAccel as p, CapabilityUnavailableError as pt, buildNativeCapProxy as q, createAddonContext as r, capActionName as rt, getOrInitReadinessRegistryForClient as s, parseCapAction as st, installManifestPythonDeps as t, validateProviderRegistrations as tt, getBrokerEventBus as u, DeviceRegistry as ut, getCapUsageRegistry as v, localProviderLink as w, buildLinkChain as x, brokerCallForCap as y, UDS_NO_ROUTE_PREFIX as z };
6569
+ export { setWorkerNativeCapsChangeListener as $, createUdsLoggerWithControl as A, createLocalTransport as B, ipcParentLink as C, createUdsEventBus as D, createUdsEventBridge as E, CapRouteError as F, FrameDecoder as G, UdsLocalTransportServer as H, classifyCapRoute as I, buildUdsNativeCapProxy as J, encodeFrame as K, LocalChildClient as L, AGENT_CAP_FWD_SERVICE as M, CapRouteResolver as N, udsChildLogToWorkerEntry as O, callWithServiceDiscovery as P, mountNativeCapService as Q, LocalChildRegistry as R, ipcChildLink as S, createParentUnownedCallHandler as T, SocketChannel as U, UdsLocalTransportClient as V, localEndpointPath as W, getWorkerNativeCapProvider as X, createBrokerDeviceManagerApi as Y, getWorkerNativeCapSnapshot as Z, __resetCapUsageRegistryForTests as _, getWorkerDeviceRegistry as a, capBareAction as at, brokerTransportLink as b, setHubConnected as c, deserializeTypedArrays as ct, registerEventBusService as d, CustomActionRegistry as dt, createAddonService as et, AddonDepsManager as f, CapabilityHandle as ft, CapUsageRegistry as g, createHwAccelService as h, resolveAddonClass as ht, createUdsAddonContext as i, capActionSuffix as it, AGENT_CAP_FWD_ACTION as j, createUdsLogger as k, EVENT_TOPIC_PREFIX as l, serializeTypedArrays as lt, resolveHwAccel as m, installManifestNativeDeps as mt, adaptBrokerToCluster as n, NATIVE_PROVIDER_SERVICE_INFIX as nt, getOrInitReadinessRegistry as o, capServiceName as ot, createKernelHwAccel as p, CapabilityUnavailableError as pt, buildNativeCapProxy as q, createAddonContext as r, capActionName as rt, getOrInitReadinessRegistryForClient as s, parseCapAction as st, installManifestPythonDeps as t, validateProviderRegistrations as tt, getBrokerEventBus as u, DeviceRegistry as ut, getCapUsageRegistry as v, localProviderLink as w, buildLinkChain as x, brokerCallForCap as y, UDS_NO_ROUTE_PREFIX as z };