@camstack/system 1.0.7 → 1.1.0

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.
@@ -161,7 +161,7 @@ var StorageOrchestratorService = class {
161
161
  const resolver = this.nodeLocalResolver;
162
162
  if (!resolver) return;
163
163
  let stamped = 0;
164
- for (const [id, loc] of [...this.locations]) {
164
+ for (const [id, loc] of Array.from(this.locations)) {
165
165
  if (loc.nodeId) continue;
166
166
  if (resolver(loc.providerId) !== true) continue;
167
167
  const updated = {
@@ -301,7 +301,7 @@ var StorageOrchestratorService = class {
301
301
  this.logger.warn("storage-orchestrator: skipping prune — no location declarations loaded (fail-safe)");
302
302
  return;
303
303
  }
304
- for (const [id, loc] of [...this.locations]) {
304
+ for (const [id, loc] of Array.from(this.locations)) {
305
305
  if (this.registry.cardinalityOf(loc.type) !== null) continue;
306
306
  if (loc.isSystem) {
307
307
  this.locations.delete(id);
@@ -154,7 +154,7 @@ var StorageOrchestratorService = class {
154
154
  const resolver = this.nodeLocalResolver;
155
155
  if (!resolver) return;
156
156
  let stamped = 0;
157
- for (const [id, loc] of [...this.locations]) {
157
+ for (const [id, loc] of Array.from(this.locations)) {
158
158
  if (loc.nodeId) continue;
159
159
  if (resolver(loc.providerId) !== true) continue;
160
160
  const updated = {
@@ -294,7 +294,7 @@ var StorageOrchestratorService = class {
294
294
  this.logger.warn("storage-orchestrator: skipping prune — no location declarations loaded (fail-safe)");
295
295
  return;
296
296
  }
297
- for (const [id, loc] of [...this.locations]) {
297
+ for (const [id, loc] of Array.from(this.locations)) {
298
298
  if (this.registry.cardinalityOf(loc.type) !== null) continue;
299
299
  if (loc.isSystem) {
300
300
  this.locations.delete(id);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_model_download_service = require("./model-download-service-JtVQtbb6.js");
3
+ const require_model_download_service = require("./model-download-service-1eEOkNeS.js");
4
4
  const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
5
5
  const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
6
6
  const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
@@ -20,7 +20,7 @@ const require_builtins_local_auth_local_auth_addon = require("./builtins/local-a
20
20
  require("./builtins/local-auth/index.js");
21
21
  const require_builtins_device_manager_device_manager_addon = require("./builtins/device-manager/device-manager.addon.js");
22
22
  require("./builtins/device-manager/index.js");
23
- const require_manifest_python_deps = require("./manifest-python-deps-eBDj5HEY.js");
23
+ const require_manifest_python_deps = require("./manifest-python-deps-BWURo7dc.js");
24
24
  const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
25
25
  const require_graceful_fs$1 = require("./graceful-fs-lg19SZNz.js");
26
26
  let _camstack_types_node = require("@camstack/types/node");
@@ -183,7 +183,7 @@ function proxyToUpstream(opts) {
183
183
  }
184
184
  //#endregion
185
185
  //#region src/python/python-env-manager.ts
186
- var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
186
+ var execFileAsync$3 = (0, node_util.promisify)(node_child_process.execFile);
187
187
  var PythonEnvManager = class {
188
188
  venvPath;
189
189
  cachedProbe = null;
@@ -193,12 +193,12 @@ var PythonEnvManager = class {
193
193
  async probe() {
194
194
  if (this.cachedProbe) return this.cachedProbe;
195
195
  for (const cmd of ["python3", "python"]) try {
196
- const { stdout } = await execFileAsync$2(cmd, ["--version"]);
196
+ const { stdout } = await execFileAsync$3(cmd, ["--version"]);
197
197
  const version = stdout.trim().replace("Python ", "");
198
198
  const major = parseInt(version.split(".")[0] ?? "0", 10);
199
199
  const minor = parseInt(version.split(".")[1] ?? "0", 10);
200
200
  if (major < 3 || major === 3 && minor < 10) continue;
201
- const { stdout: pathOut } = await execFileAsync$2(cmd, ["-c", "import sys; print(sys.executable)"]);
201
+ const { stdout: pathOut } = await execFileAsync$3(cmd, ["-c", "import sys; print(sys.executable)"]);
202
202
  this.cachedProbe = {
203
203
  available: true,
204
204
  version,
@@ -214,13 +214,13 @@ var PythonEnvManager = class {
214
214
  async ensure(options) {
215
215
  const probe = await this.probe();
216
216
  if (!probe.available || !probe.path) throw new Error("Python 3.10+ is required but not found on this system");
217
- if (!node_fs.existsSync(node_path.join(this.venvPath, "bin", "python"))) await execFileAsync$2(probe.path, [
217
+ if (!node_fs.existsSync(node_path.join(this.venvPath, "bin", "python"))) await execFileAsync$3(probe.path, [
218
218
  "-m",
219
219
  "venv",
220
220
  this.venvPath
221
221
  ]);
222
222
  const venvPython = node_path.join(this.venvPath, "bin", "python");
223
- if (options.packages.length > 0) await execFileAsync$2(venvPython, [
223
+ if (options.packages.length > 0) await execFileAsync$3(venvPython, [
224
224
  "-m",
225
225
  "pip",
226
226
  "install",
@@ -1419,12 +1419,11 @@ var StorageManager = class {
1419
1419
  return namespace ? this.createNamespacedLocation(location, namespace) : location;
1420
1420
  }
1421
1421
  createLegacyShim() {
1422
- const self = this;
1423
1422
  return {
1424
1423
  async initialize() {},
1425
1424
  async shutdown() {},
1426
- getLocation(name) {
1427
- return self.getLocation(name);
1425
+ getLocation: (name) => {
1426
+ return this.getLocation(name);
1428
1427
  }
1429
1428
  };
1430
1429
  }
@@ -1700,7 +1699,7 @@ function matchPath(pattern, path) {
1700
1699
  }
1701
1700
  //#endregion
1702
1701
  //#region src/tls/cert-manager.ts
1703
- var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
1702
+ var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
1704
1703
  /**
1705
1704
  * Ensure a self-signed TLS certificate exists in the given directory.
1706
1705
  * Generates one if missing. Returns paths to cert and key files.
@@ -1737,7 +1736,7 @@ async function ensureTlsCert(dataDir, options) {
1737
1736
  for (const dns of sanDns) sanParts.push(`DNS:${dns}`);
1738
1737
  for (const ip of sanIps) sanParts.push(`IP:${ip}`);
1739
1738
  const sanString = sanParts.join(",");
1740
- await execFileAsync$1("openssl", [
1739
+ await execFileAsync$2("openssl", [
1741
1740
  "req",
1742
1741
  "-x509",
1743
1742
  "-newkey",
@@ -2570,6 +2569,7 @@ var AddonEngineManager = class {
2570
2569
  };
2571
2570
  //#endregion
2572
2571
  //#region src/kernel/fs-utils.ts
2572
+ var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
2573
2573
  /**
2574
2574
  * Ensure a directory exists (recursive).
2575
2575
  * Single source of truth — replaces scattered mkdirSync calls.
@@ -2578,18 +2578,20 @@ function ensureDir(dirPath) {
2578
2578
  node_fs.mkdirSync(dirPath, { recursive: true });
2579
2579
  }
2580
2580
  /**
2581
- * Copy a directory recursively.
2582
- * Single source of truth — extracted from addon-installer + first-boot-installer.
2581
+ * Copy a directory recursively — ASYNC so it never blocks the event loop.
2582
+ *
2583
+ * The hub installs addons into `/data/addons`, which on Unraid is a slow
2584
+ * shfs/FUSE mount. The former synchronous `fs.copyFileSync`-per-file loop
2585
+ * blocked the Node event loop for the whole copy of a large bundle (e.g.
2586
+ * `addon-pipeline`), which froze the hub's HTTP listener mid-`camstack deploy`
2587
+ * (accept backlog piling up, existing connections stuck in CLOSE_WAIT). Using
2588
+ * `fs.promises.cp` keeps the I/O off the event loop. (Node ≥18 stable.)
2583
2589
  */
2584
- function copyDirRecursive(src, dest) {
2585
- ensureDir(dest);
2586
- const entries = node_fs.readdirSync(src, { withFileTypes: true });
2587
- for (const entry of entries) {
2588
- const srcPath = node_path.join(src, entry.name);
2589
- const destPath = node_path.join(dest, entry.name);
2590
- if (entry.isDirectory()) copyDirRecursive(srcPath, destPath);
2591
- else node_fs.copyFileSync(srcPath, destPath);
2592
- }
2590
+ async function copyDirRecursive(src, dest) {
2591
+ await node_fs.promises.cp(src, dest, {
2592
+ recursive: true,
2593
+ force: true
2594
+ });
2593
2595
  }
2594
2596
  /**
2595
2597
  * Strip @camstack/* dependencies and devDependencies from a package.json object.
@@ -2619,7 +2621,7 @@ function stripCamstackDeps(pkg) {
2619
2621
  * Copies directories (not individual files) from source to destination.
2620
2622
  * Skips "dist" (already handled) and glob patterns.
2621
2623
  */
2622
- function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
2624
+ async function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
2623
2625
  const rawFiles = pkgJson.files;
2624
2626
  if (!Array.isArray(rawFiles)) return;
2625
2627
  for (const fileEntry of rawFiles) {
@@ -2628,11 +2630,11 @@ function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
2628
2630
  const srcPath = node_path.join(sourceDir, fileEntry);
2629
2631
  if (!node_fs.existsSync(srcPath)) continue;
2630
2632
  const destPath = node_path.join(destDir, fileEntry);
2631
- const stat = node_fs.statSync(srcPath);
2632
- if (stat.isDirectory()) copyDirRecursive(srcPath, destPath);
2633
+ const stat = await node_fs.promises.stat(srcPath);
2634
+ if (stat.isDirectory()) await copyDirRecursive(srcPath, destPath);
2633
2635
  else if (stat.isFile()) {
2634
2636
  ensureDir(node_path.dirname(destPath));
2635
- node_fs.copyFileSync(srcPath, destPath);
2637
+ await node_fs.promises.copyFile(srcPath, destPath);
2636
2638
  }
2637
2639
  }
2638
2640
  }
@@ -2677,12 +2679,16 @@ function ensureLibraryBuilt(packageName, packagesDir) {
2677
2679
  /**
2678
2680
  * Install a single npm package into a target directory (package.json + dist/).
2679
2681
  * No validation on camstack.addons -- works for any @camstack/* package.
2680
- * Uses synchronous child_process calls (suitable for first-boot and update paths).
2682
+ *
2683
+ * ASYNC: uses `execFile`/`fs.promises` throughout so a package install on a
2684
+ * live node never blocks the event loop (the `npm pack` + tar extract + copy to
2685
+ * the slow shfs/FUSE `/data` would otherwise freeze the HTTP listener). See
2686
+ * `copyDirRecursive` for the wedge this prevents.
2681
2687
  */
2682
- function installPackageFromNpmSync(packageName, targetDir) {
2683
- const tmpDir = node_fs.mkdtempSync(node_path.join(node_os.tmpdir(), "camstack-install-"));
2688
+ async function installPackageFromNpm(packageName, targetDir) {
2689
+ const tmpDir = await node_fs.promises.mkdtemp(node_path.join(node_os.tmpdir(), "camstack-install-"));
2684
2690
  try {
2685
- const tgzFilename = (0, node_child_process.execFileSync)("npm", [
2691
+ const { stdout } = await execFileAsync$1("npm", [
2686
2692
  "pack",
2687
2693
  packageName,
2688
2694
  "--pack-destination",
@@ -2690,12 +2696,13 @@ function installPackageFromNpmSync(packageName, targetDir) {
2690
2696
  ], {
2691
2697
  timeout: 12e4,
2692
2698
  encoding: "utf-8"
2693
- }).trim().split("\n").pop()?.trim();
2699
+ });
2700
+ const tgzFilename = stdout.trim().split("\n").pop()?.trim();
2694
2701
  if (!tgzFilename) throw new Error("npm pack produced no output");
2695
2702
  const tgzPath = node_path.join(tmpDir, tgzFilename);
2696
2703
  const extractDir = node_path.join(tmpDir, "extracted");
2697
2704
  ensureDir(extractDir);
2698
- (0, node_child_process.execFileSync)("tar", [
2705
+ await execFileAsync$1("tar", [
2699
2706
  "-xzf",
2700
2707
  tgzPath,
2701
2708
  "-C",
@@ -2703,20 +2710,20 @@ function installPackageFromNpmSync(packageName, targetDir) {
2703
2710
  ], { timeout: 3e4 });
2704
2711
  const packageSubDir = node_path.join(extractDir, "package");
2705
2712
  const srcPkgJsonDir = node_fs.existsSync(node_path.join(packageSubDir, "package.json")) ? packageSubDir : extractDir;
2706
- node_fs.rmSync(targetDir, {
2713
+ await node_fs.promises.rm(targetDir, {
2707
2714
  recursive: true,
2708
2715
  force: true
2709
2716
  });
2710
2717
  ensureDir(targetDir);
2711
- node_fs.copyFileSync(node_path.join(srcPkgJsonDir, "package.json"), node_path.join(targetDir, "package.json"));
2718
+ await node_fs.promises.copyFile(node_path.join(srcPkgJsonDir, "package.json"), node_path.join(targetDir, "package.json"));
2712
2719
  const distSrc = node_path.join(srcPkgJsonDir, "dist");
2713
- if (node_fs.existsSync(distSrc)) copyDirRecursive(distSrc, node_path.join(targetDir, "dist"));
2720
+ if (node_fs.existsSync(distSrc)) await copyDirRecursive(distSrc, node_path.join(targetDir, "dist"));
2714
2721
  try {
2715
- const npmPkg = (0, _camstack_types.asJsonObject)((0, _camstack_types.parseJsonUnknown)(node_fs.readFileSync(node_path.join(srcPkgJsonDir, "package.json"), "utf-8")));
2716
- if (npmPkg) copyExtraFileDirs(npmPkg, srcPkgJsonDir, targetDir);
2722
+ const npmPkg = (0, _camstack_types.asJsonObject)((0, _camstack_types.parseJsonUnknown)(await node_fs.promises.readFile(node_path.join(srcPkgJsonDir, "package.json"), "utf-8")));
2723
+ if (npmPkg) await copyExtraFileDirs(npmPkg, srcPkgJsonDir, targetDir);
2717
2724
  } catch {}
2718
2725
  } finally {
2719
- node_fs.rmSync(tmpDir, {
2726
+ await node_fs.promises.rm(tmpDir, {
2720
2727
  recursive: true,
2721
2728
  force: true
2722
2729
  });
@@ -3166,15 +3173,15 @@ var AddonInstaller = class AddonInstaller {
3166
3173
  await this.ensureBuilt(packageName, sourceDir, pkgData);
3167
3174
  const distDir = node_path.join(sourceDir, "dist");
3168
3175
  if (!node_fs.existsSync(distDir)) throw new Error(`${packageName} has no dist/ after build`);
3169
- node_fs.rmSync(targetDir, {
3176
+ await node_fs.promises.rm(targetDir, {
3170
3177
  recursive: true,
3171
3178
  force: true
3172
3179
  });
3173
3180
  ensureDir(targetDir);
3174
- node_fs.writeFileSync(node_path.join(targetDir, "package.json"), JSON.stringify(stripCamstackDeps(pkgData), null, 2));
3175
- copyDirRecursive(distDir, node_path.join(targetDir, "dist"));
3176
- copyExtraFileDirs(pkgData, sourceDir, targetDir);
3177
- node_fs.writeFileSync(node_path.join(targetDir, ".install-source"), "local");
3181
+ await node_fs.promises.writeFile(node_path.join(targetDir, "package.json"), JSON.stringify(stripCamstackDeps(pkgData), null, 2));
3182
+ await copyDirRecursive(distDir, node_path.join(targetDir, "dist"));
3183
+ await copyExtraFileDirs(pkgData, sourceDir, targetDir);
3184
+ await node_fs.promises.writeFile(node_path.join(targetDir, ".install-source"), "local");
3178
3185
  const localPkgVersion = (0, _camstack_types.asString)(pkgData.version, "0.0.0");
3179
3186
  this.manifest.upsert(packageName, {
3180
3187
  version: localPkgVersion,
@@ -3277,6 +3284,66 @@ var AddonInstaller = class AddonInstaller {
3277
3284
  node_fs.writeFileSync(tgzPath, buf);
3278
3285
  return tgzPath;
3279
3286
  }
3287
+ /**
3288
+ * Evict an existing install dir by atomic rename-aside, then best-effort
3289
+ * delete the aside copy.
3290
+ *
3291
+ * Why not a plain `fs.promises.rm(targetDir, {recursive})`: during a
3292
+ * `camstack deploy` the addon's live runner still holds its native
3293
+ * prebuilds (`.node`/`.so`) open. On the hub's shfs/FUSE `/data/addons`,
3294
+ * a recursive rm of a held subdir fails `ENOTEMPTY` mid-walk and leaves
3295
+ * the dir gutted. A rename only touches the dirent (metadata), so it
3296
+ * succeeds with the inodes still open — the old runner keeps its fds on
3297
+ * the moved inodes until it restarts.
3298
+ *
3299
+ * The aside copy is removed best-effort: the held native libs are only
3300
+ * released once the post-install `restartAddon` recycles the runner, so a
3301
+ * failure to delete it now must NOT fail the install. Any residue is swept
3302
+ * on the next deploy.
3303
+ */
3304
+ async evictInstallDir(targetDir) {
3305
+ if (!node_fs.existsSync(targetDir)) return;
3306
+ const graveyard = node_path.join(this.addonsDir, ".evicting");
3307
+ ensureDir(graveyard);
3308
+ await this.sweepGraveyard(graveyard);
3309
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3310
+ const flatName = node_path.relative(this.addonsDir, targetDir).replace(/[/\\]/g, "-");
3311
+ const asideDir = node_path.join(graveyard, `${flatName}-${ts}`);
3312
+ try {
3313
+ node_fs.renameSync(targetDir, asideDir);
3314
+ } catch (renameErr) {
3315
+ if (renameErr.code === "EXDEV") {
3316
+ await node_fs.promises.rm(targetDir, {
3317
+ recursive: true,
3318
+ force: true
3319
+ });
3320
+ return;
3321
+ }
3322
+ throw renameErr;
3323
+ }
3324
+ await node_fs.promises.rm(asideDir, {
3325
+ recursive: true,
3326
+ force: true
3327
+ }).catch((cleanupErr) => {
3328
+ this.logger.debug("evictInstallDir: aside cleanup deferred (likely held open)", { meta: {
3329
+ asideDir,
3330
+ error: (0, _camstack_types.errMsg)(cleanupErr)
3331
+ } });
3332
+ });
3333
+ }
3334
+ /** Best-effort removal of leftover `.evicting/*` dirs from earlier deploys. */
3335
+ async sweepGraveyard(graveyard) {
3336
+ let entries;
3337
+ try {
3338
+ entries = node_fs.readdirSync(graveyard);
3339
+ } catch {
3340
+ return;
3341
+ }
3342
+ await Promise.all(entries.map((name) => node_fs.promises.rm(node_path.join(graveyard, name), {
3343
+ recursive: true,
3344
+ force: true
3345
+ }).catch(() => void 0)));
3346
+ }
3280
3347
  /** Install addon from a tgz file (uploaded or downloaded) */
3281
3348
  async installFromTgz(tgzPath) {
3282
3349
  const tmpDir = node_fs.mkdtempSync(node_path.join(node_os.tmpdir(), "camstack-addon-install-"));
@@ -3294,17 +3361,14 @@ var AddonInstaller = class AddonInstaller {
3294
3361
  if (!pkgView) throw new Error(`Invalid package.json at ${pkgJsonPath}`);
3295
3362
  if (!pkgView.camstackAddons) throw new Error(`Package ${pkgView.name} has no camstack.addons manifest`);
3296
3363
  const targetDir = node_path.join(this.addonsDir, pkgView.name);
3297
- node_fs.rmSync(targetDir, {
3298
- recursive: true,
3299
- force: true
3300
- });
3364
+ await this.evictInstallDir(targetDir);
3301
3365
  ensureDir(targetDir);
3302
3366
  const sourceDir = node_path.dirname(pkgJsonPath);
3303
3367
  const strippedManifest = stripCamstackDeps(pkgView.raw);
3304
- node_fs.writeFileSync(node_path.join(targetDir, "package.json"), JSON.stringify(strippedManifest, null, 2));
3368
+ await node_fs.promises.writeFile(node_path.join(targetDir, "package.json"), JSON.stringify(strippedManifest, null, 2));
3305
3369
  const sourceDist = node_path.join(sourceDir, "dist");
3306
- if (node_fs.existsSync(sourceDist)) copyDirRecursive(sourceDist, node_path.join(targetDir, "dist"));
3307
- copyExtraFileDirs(pkgView.raw, sourceDir, targetDir);
3370
+ if (node_fs.existsSync(sourceDist)) await copyDirRecursive(sourceDist, node_path.join(targetDir, "dist"));
3371
+ await copyExtraFileDirs(pkgView.raw, sourceDir, targetDir);
3308
3372
  const strippedRuntimeDeps = strippedManifest["dependencies"];
3309
3373
  if (strippedRuntimeDeps != null && typeof strippedRuntimeDeps === "object" && Object.keys(strippedRuntimeDeps).length > 0) {
3310
3374
  this.logger.info(`${pkgView.name} — installing runtime dependencies`, { meta: { targetDir } });
@@ -3328,7 +3392,7 @@ var AddonInstaller = class AddonInstaller {
3328
3392
  try {
3329
3393
  await require_manifest_python_deps.installManifestNativeDeps(targetDir, pkgView.raw, this.logger, this.registry);
3330
3394
  } catch (nativeErr) {
3331
- node_fs.rmSync(targetDir, {
3395
+ await node_fs.promises.rm(targetDir, {
3332
3396
  recursive: true,
3333
3397
  force: true
3334
3398
  });
@@ -3343,7 +3407,7 @@ var AddonInstaller = class AddonInstaller {
3343
3407
  version: pkgView.version
3344
3408
  };
3345
3409
  } finally {
3346
- node_fs.rmSync(tmpDir, {
3410
+ await node_fs.promises.rm(tmpDir, {
3347
3411
  recursive: true,
3348
3412
  force: true
3349
3413
  });
@@ -4111,7 +4175,7 @@ var CapabilityRegistry = class CapabilityRegistry {
4111
4175
  const bare = this.bareAddonId(addonId);
4112
4176
  const nodeMap = this.singletonNodeOverrides.get(capabilityName);
4113
4177
  if (nodeMap) {
4114
- for (const [nodeId, ov] of [...nodeMap]) if (ov === bare) nodeMap.delete(nodeId);
4178
+ for (const [nodeId, ov] of Array.from(nodeMap)) if (ov === bare) nodeMap.delete(nodeId);
4115
4179
  if (nodeMap.size === 0) this.singletonNodeOverrides.delete(capabilityName);
4116
4180
  }
4117
4181
  this.logger.info("Provider unregistered from capability", {
@@ -7388,20 +7452,20 @@ var ConfigManager = class ConfigManager {
7388
7452
  setSettingsStore(store) {
7389
7453
  this.settingsStore = store;
7390
7454
  }
7391
- get(path) {
7392
- return this.resolveConfigValue(path);
7455
+ get(configPath) {
7456
+ return this.resolveConfigValue(configPath);
7393
7457
  }
7394
- resolveConfigValue(path) {
7395
- const bootstrapValue = this.getFromBootstrap(path);
7458
+ resolveConfigValue(configPath) {
7459
+ const bootstrapValue = this.getFromBootstrap(configPath);
7396
7460
  if (bootstrapValue !== void 0) return bootstrapValue;
7397
7461
  if (this.settingsStore !== null) {
7398
- const storeValue = this.settingsStore.getSystem(path);
7462
+ const storeValue = this.settingsStore.getSystem(configPath);
7399
7463
  if (storeValue !== void 0) return storeValue;
7400
- const nested = this.getNestedFromSystemSettings(path);
7464
+ const nested = this.getNestedFromSystemSettings(configPath);
7401
7465
  if (nested !== null) return nested;
7402
7466
  }
7403
- if (path in _camstack_types.RUNTIME_DEFAULTS) return _camstack_types.RUNTIME_DEFAULTS[path];
7404
- return this.getFromRuntimeDefaults(path) ?? void 0;
7467
+ if (configPath in _camstack_types.RUNTIME_DEFAULTS) return _camstack_types.RUNTIME_DEFAULTS[configPath];
7468
+ return this.getFromRuntimeDefaults(configPath) ?? void 0;
7405
7469
  }
7406
7470
  /**
7407
7471
  * Write a value to the settings-store.
@@ -7617,8 +7681,8 @@ var ConfigManager = class ConfigManager {
7617
7681
  };
7618
7682
  this.saveRuntimeState();
7619
7683
  }
7620
- getBootstrap(path) {
7621
- return this.getFromBootstrap(path);
7684
+ getBootstrap(configPath) {
7685
+ return this.getFromBootstrap(configPath);
7622
7686
  }
7623
7687
  /** Features accessor -- reads from settings-store when available, falls back to RUNTIME_DEFAULTS */
7624
7688
  get features() {
@@ -7708,8 +7772,8 @@ var ConfigManager = class ConfigManager {
7708
7772
  * Deep-set a value in a nested plain object using a dot-notation path.
7709
7773
  * Returns a new object (immutable).
7710
7774
  */
7711
- setNested(obj, path, value) {
7712
- const [head, ...rest] = path.split(".");
7775
+ setNested(obj, configPath, value) {
7776
+ const [head, ...rest] = configPath.split(".");
7713
7777
  if (!head) return obj;
7714
7778
  if (rest.length === 0) return {
7715
7779
  ...obj,
@@ -7749,8 +7813,8 @@ var ConfigManager = class ConfigManager {
7749
7813
  warnDefaultCredentials() {
7750
7814
  if (this.bootstrapConfig.auth.adminPassword === "changeme") console.warn("[ConfigManager] Warning: Using default admin password \"changeme\". Set auth.adminPassword in your config.yaml or the CAMSTACK_ADMIN_PASS env var.");
7751
7815
  }
7752
- getFromBootstrap(path) {
7753
- const keys = path.split(".");
7816
+ getFromBootstrap(configPath) {
7817
+ const keys = configPath.split(".");
7754
7818
  let current = this.bootstrapConfig;
7755
7819
  for (const key of keys) {
7756
7820
  if (!isRecord(current)) return void 0;
@@ -7758,8 +7822,8 @@ var ConfigManager = class ConfigManager {
7758
7822
  }
7759
7823
  return current;
7760
7824
  }
7761
- getFromRuntimeDefaults(path) {
7762
- const prefix = path + ".";
7825
+ getFromRuntimeDefaults(configPath) {
7826
+ const prefix = configPath + ".";
7763
7827
  const result = {};
7764
7828
  let found = false;
7765
7829
  for (const [key, value] of Object.entries(_camstack_types.RUNTIME_DEFAULTS)) if (key.startsWith(prefix)) {
@@ -7774,10 +7838,10 @@ var ConfigManager = class ConfigManager {
7774
7838
  * e.g. path='features' matches keys 'features.streaming', 'features.notifications', etc.
7775
7839
  * Returns an object keyed by the sub-key, or undefined if nothing is found.
7776
7840
  */
7777
- getNestedFromSystemSettings(path) {
7841
+ getNestedFromSystemSettings(configPath) {
7778
7842
  if (this.settingsStore === null) return null;
7779
7843
  const all = this.settingsStore.getAllSystem();
7780
- const prefix = path + ".";
7844
+ const prefix = configPath + ".";
7781
7845
  const result = {};
7782
7846
  let found = false;
7783
7847
  for (const [key, value] of Object.entries(all)) if (key.startsWith(prefix)) {
@@ -89897,6 +89961,15 @@ function normalizeHubUrl(raw, defaultPort) {
89897
89961
  return `${/:\d+$/.test(hostPort) ? hostPort : `${hostPort}:${defaultPort}`}/${nodeId}`;
89898
89962
  }
89899
89963
  //#endregion
89964
+ //#region src/kernel/moleculer/addon-deploy-source.ts
89965
+ function isAddonDeploySource(v) {
89966
+ if (!v || typeof v !== "object") return false;
89967
+ const o = v;
89968
+ if (o["kind"] === "npm") return typeof o["version"] === "string";
89969
+ if (o["kind"] === "hub-http") return typeof o["url"] === "string" && typeof o["token"] === "string" && typeof o["sha256"] === "string" && typeof o["bytes"] === "number";
89970
+ return false;
89971
+ }
89972
+ //#endregion
89900
89973
  //#region src/kernel/moleculer/core-cap-service.ts
89901
89974
  /**
89902
89975
  * Default Moleculer service name for the hub core-capability bridge.
@@ -90938,7 +91011,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
90938
91011
  respawned.restartCount = prevRestartCount + 1;
90939
91012
  });
90940
91013
  restarted.push(name);
90941
- } catch (err) {
91014
+ } catch {
90942
91015
  failed.push(name);
90943
91016
  }
90944
91017
  return {
@@ -91333,8 +91406,8 @@ var LifecycleJobEngine = class {
91333
91406
  } finally {
91334
91407
  clearTimeout(timer);
91335
91408
  }
91336
- if (packages.length === 0) throw new Error("stageFramework returned no packages");
91337
91409
  const [firstPackage] = packages;
91410
+ if (!firstPackage) throw new Error("stageFramework returned no packages");
91338
91411
  this.advance(job.jobId, task, "staged", { stagedPath: firstPackage.stagedPath });
91339
91412
  requestFrameworkSwap({
91340
91413
  jobId: job.jobId,
@@ -91357,14 +91430,15 @@ var LifecycleJobEngine = class {
91357
91430
  */
91358
91431
  async fetchAddonsBounded(job, addonTasks) {
91359
91432
  const limit = Math.max(1, this.deps.fetchConcurrency ?? DEFAULT_FETCH_CONCURRENCY);
91360
- const outcomes = new Array(addonTasks.length);
91433
+ const outcomes = Array.from({ length: addonTasks.length });
91361
91434
  let nextIndex = 0;
91362
91435
  const worker = async () => {
91363
91436
  for (;;) {
91364
91437
  const index = nextIndex;
91365
91438
  nextIndex += 1;
91366
- if (index >= addonTasks.length) return;
91367
- outcomes[index] = await this.fetchAddonTask(job, addonTasks[index]);
91439
+ const addonTask = addonTasks[index];
91440
+ if (addonTask === void 0) return;
91441
+ outcomes[index] = await this.fetchAddonTask(job, addonTask);
91368
91442
  }
91369
91443
  };
91370
91444
  const workerCount = Math.min(limit, addonTasks.length);
@@ -91732,7 +91806,7 @@ exports.getWorkerDeviceRegistry = require_manifest_python_deps.getWorkerDeviceRe
91732
91806
  exports.hashClusterSecret = hashClusterSecret;
91733
91807
  exports.installManifestNativeDeps = require_manifest_python_deps.installManifestNativeDeps;
91734
91808
  exports.installManifestPythonDeps = require_manifest_python_deps.installManifestPythonDeps;
91735
- exports.installPackageFromNpmSync = installPackageFromNpmSync;
91809
+ exports.installPackageFromNpm = installPackageFromNpm;
91736
91810
  Object.defineProperty(exports, "installPythonPackages", {
91737
91811
  enumerable: true,
91738
91812
  get: function() {
@@ -91747,6 +91821,7 @@ Object.defineProperty(exports, "installPythonRequirements", {
91747
91821
  });
91748
91822
  exports.ipcChildLink = require_manifest_python_deps.ipcChildLink;
91749
91823
  exports.ipcParentLink = require_manifest_python_deps.ipcParentLink;
91824
+ exports.isAddonDeploySource = isAddonDeploySource;
91750
91825
  exports.isClusterSecretMismatchError = isClusterSecretMismatchError;
91751
91826
  exports.isInfraCapability = isInfraCapability;
91752
91827
  exports.isModelDownloaded = require_model_download_service.isModelDownloaded;