@camstack/system 1.1.13 → 1.1.15
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.
- package/dist/builtins/device-manager/device-manager.addon.d.ts +9 -3
- package/dist/builtins/device-manager/device-manager.addon.js +1766 -1696
- package/dist/builtins/device-manager/device-manager.addon.mjs +1766 -1696
- package/dist/builtins/device-manager/device-meta-actions.d.ts +1 -1
- package/dist/index.js +72 -1
- package/dist/index.mjs +72 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AddonContext, deviceManagerCapability, InferProvider } from '@camstack/types';
|
|
2
2
|
import { DeviceManagerSettings, DeviceMetaStore } from './device-meta-store.js';
|
|
3
3
|
import { ProviderContext } from './device-provider-context.js';
|
|
4
4
|
type IDeviceManagerProvider = InferProvider<typeof deviceManagerCapability>;
|
package/dist/index.js
CHANGED
|
@@ -92343,6 +92343,76 @@ async function getPidStats$1(pids) {
|
|
|
92343
92343
|
});
|
|
92344
92344
|
});
|
|
92345
92345
|
}
|
|
92346
|
+
/**
|
|
92347
|
+
* Resolve the declared heap profile for one addon by reading its package.json manifest
|
|
92348
|
+
* (`camstack.addons[].execution.heapProfile`). `addonDir` points at the addon's built `dist`, so the
|
|
92349
|
+
* manifest is one level up; we also try `addonDir` itself for layouts where it IS the package root.
|
|
92350
|
+
* Result is cached per (dir,id) — spawns are infrequent but a runner may re-spawn on crash/restart.
|
|
92351
|
+
* Returns `undefined` when the manifest is unreadable or declares no profile → treated as light.
|
|
92352
|
+
*/
|
|
92353
|
+
var heapProfileCache = /* @__PURE__ */ new Map();
|
|
92354
|
+
function readAddonHeapProfile(spec) {
|
|
92355
|
+
const cacheKey = `${spec.addonDir}::${spec.addonId}`;
|
|
92356
|
+
const cached = heapProfileCache.get(cacheKey);
|
|
92357
|
+
if (cached !== void 0 || heapProfileCache.has(cacheKey)) return cached;
|
|
92358
|
+
let profile;
|
|
92359
|
+
for (const manifestPath of [node_path.join(spec.addonDir, "package.json"), node_path.join(node_path.dirname(spec.addonDir), "package.json")]) try {
|
|
92360
|
+
const raw = node_fs.readFileSync(manifestPath, "utf8");
|
|
92361
|
+
const parsed = JSON.parse(raw);
|
|
92362
|
+
profile = extractHeapProfile(parsed, spec.addonId);
|
|
92363
|
+
if (profile !== void 0) break;
|
|
92364
|
+
if (manifestHasAddon(parsed, spec.addonId)) break;
|
|
92365
|
+
} catch {}
|
|
92366
|
+
heapProfileCache.set(cacheKey, profile);
|
|
92367
|
+
return profile;
|
|
92368
|
+
}
|
|
92369
|
+
function manifestHasAddon(parsed, addonId) {
|
|
92370
|
+
return readManifestAddons(parsed).some((a) => a.id === addonId);
|
|
92371
|
+
}
|
|
92372
|
+
function extractHeapProfile(parsed, addonId) {
|
|
92373
|
+
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.heapProfile;
|
|
92374
|
+
return value === "heavy" || value === "light" ? value : void 0;
|
|
92375
|
+
}
|
|
92376
|
+
function readManifestAddons(parsed) {
|
|
92377
|
+
if (typeof parsed !== "object" || parsed === null) return [];
|
|
92378
|
+
const camstack = parsed.camstack;
|
|
92379
|
+
if (typeof camstack !== "object" || camstack === null) return [];
|
|
92380
|
+
const addons = camstack.addons;
|
|
92381
|
+
return Array.isArray(addons) ? addons : [];
|
|
92382
|
+
}
|
|
92383
|
+
/**
|
|
92384
|
+
* A runner is heavy if ANY co-located addon declares `heapProfile: 'heavy'` in its manifest.
|
|
92385
|
+
* Everything else (the un-annotated default) is a light control-plane runner. No hard-coded names.
|
|
92386
|
+
*/
|
|
92387
|
+
function isHeavyRunner(addons) {
|
|
92388
|
+
return addons.some((a) => readAddonHeapProfile(a) === "heavy");
|
|
92389
|
+
}
|
|
92390
|
+
/**
|
|
92391
|
+
* Per-placement V8 heap flags (Block D). LIGHT runners cap their young generation
|
|
92392
|
+
* (`--max-semi-space-size`) to reclaim V8's over-reservation — a measured ~15 MB/runner win.
|
|
92393
|
+
* HEAVY runners keep V8 defaults. The old-space cap is opt-in via env (unset = uncapped = no OOM
|
|
92394
|
+
* risk) so it can be enabled after before/after measurement on the real cluster.
|
|
92395
|
+
*
|
|
92396
|
+
* Kill-switch: `CAMSTACK_RUNNER_HEAP_TUNING=off` disables all flags (recover without a rebuild).
|
|
92397
|
+
* Overrides: `CAMSTACK_RUNNER_LIGHT_SEMI_SPACE_MB` (default 2), `CAMSTACK_RUNNER_LIGHT_MAX_OLD_MB`
|
|
92398
|
+
* (default unset = no old-space cap).
|
|
92399
|
+
*/
|
|
92400
|
+
function runnerHeapFlags(addons) {
|
|
92401
|
+
if (process.env["CAMSTACK_RUNNER_HEAP_TUNING"] === "off") return [];
|
|
92402
|
+
if (isHeavyRunner(addons)) return [];
|
|
92403
|
+
const semiMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_SEMI_SPACE_MB", 2);
|
|
92404
|
+
const oldMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_MAX_OLD_MB", 0);
|
|
92405
|
+
const flags = [];
|
|
92406
|
+
if (semiMb > 0) flags.push(`--max-semi-space-size=${semiMb}`);
|
|
92407
|
+
if (oldMb > 0) flags.push(`--max-old-space-size=${oldMb}`);
|
|
92408
|
+
return flags;
|
|
92409
|
+
}
|
|
92410
|
+
function positiveIntEnv(name, fallback) {
|
|
92411
|
+
const raw = process.env[name];
|
|
92412
|
+
if (raw === void 0) return fallback;
|
|
92413
|
+
const n = Number.parseInt(raw, 10);
|
|
92414
|
+
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
92415
|
+
}
|
|
92346
92416
|
var MAX_BACKOFF_MS = 3e4;
|
|
92347
92417
|
var CRASH_WINDOW_MS = 6e4;
|
|
92348
92418
|
var MAX_CRASHES_IN_WINDOW = 5;
|
|
@@ -92421,7 +92491,8 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
|
|
|
92421
92491
|
...parentUdsPath !== void 0 ? { CAMSTACK_PARENT_UDS_PATH: parentUdsPath } : {},
|
|
92422
92492
|
...env
|
|
92423
92493
|
};
|
|
92424
|
-
const
|
|
92494
|
+
const heapFlags = runnerHeapFlags(addons);
|
|
92495
|
+
const child = spawnFn(process.execPath, [...heapFlags, runnerPath], {
|
|
92425
92496
|
env: childEnv,
|
|
92426
92497
|
stdio: [
|
|
92427
92498
|
"ignore",
|
package/dist/index.mjs
CHANGED
|
@@ -92335,6 +92335,76 @@ async function getPidStats$1(pids) {
|
|
|
92335
92335
|
});
|
|
92336
92336
|
});
|
|
92337
92337
|
}
|
|
92338
|
+
/**
|
|
92339
|
+
* Resolve the declared heap profile for one addon by reading its package.json manifest
|
|
92340
|
+
* (`camstack.addons[].execution.heapProfile`). `addonDir` points at the addon's built `dist`, so the
|
|
92341
|
+
* manifest is one level up; we also try `addonDir` itself for layouts where it IS the package root.
|
|
92342
|
+
* Result is cached per (dir,id) — spawns are infrequent but a runner may re-spawn on crash/restart.
|
|
92343
|
+
* Returns `undefined` when the manifest is unreadable or declares no profile → treated as light.
|
|
92344
|
+
*/
|
|
92345
|
+
var heapProfileCache = /* @__PURE__ */ new Map();
|
|
92346
|
+
function readAddonHeapProfile(spec) {
|
|
92347
|
+
const cacheKey = `${spec.addonDir}::${spec.addonId}`;
|
|
92348
|
+
const cached = heapProfileCache.get(cacheKey);
|
|
92349
|
+
if (cached !== void 0 || heapProfileCache.has(cacheKey)) return cached;
|
|
92350
|
+
let profile;
|
|
92351
|
+
for (const manifestPath of [path$39.join(spec.addonDir, "package.json"), path$39.join(path$39.dirname(spec.addonDir), "package.json")]) try {
|
|
92352
|
+
const raw = fs$17.readFileSync(manifestPath, "utf8");
|
|
92353
|
+
const parsed = JSON.parse(raw);
|
|
92354
|
+
profile = extractHeapProfile(parsed, spec.addonId);
|
|
92355
|
+
if (profile !== void 0) break;
|
|
92356
|
+
if (manifestHasAddon(parsed, spec.addonId)) break;
|
|
92357
|
+
} catch {}
|
|
92358
|
+
heapProfileCache.set(cacheKey, profile);
|
|
92359
|
+
return profile;
|
|
92360
|
+
}
|
|
92361
|
+
function manifestHasAddon(parsed, addonId) {
|
|
92362
|
+
return readManifestAddons(parsed).some((a) => a.id === addonId);
|
|
92363
|
+
}
|
|
92364
|
+
function extractHeapProfile(parsed, addonId) {
|
|
92365
|
+
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.heapProfile;
|
|
92366
|
+
return value === "heavy" || value === "light" ? value : void 0;
|
|
92367
|
+
}
|
|
92368
|
+
function readManifestAddons(parsed) {
|
|
92369
|
+
if (typeof parsed !== "object" || parsed === null) return [];
|
|
92370
|
+
const camstack = parsed.camstack;
|
|
92371
|
+
if (typeof camstack !== "object" || camstack === null) return [];
|
|
92372
|
+
const addons = camstack.addons;
|
|
92373
|
+
return Array.isArray(addons) ? addons : [];
|
|
92374
|
+
}
|
|
92375
|
+
/**
|
|
92376
|
+
* A runner is heavy if ANY co-located addon declares `heapProfile: 'heavy'` in its manifest.
|
|
92377
|
+
* Everything else (the un-annotated default) is a light control-plane runner. No hard-coded names.
|
|
92378
|
+
*/
|
|
92379
|
+
function isHeavyRunner(addons) {
|
|
92380
|
+
return addons.some((a) => readAddonHeapProfile(a) === "heavy");
|
|
92381
|
+
}
|
|
92382
|
+
/**
|
|
92383
|
+
* Per-placement V8 heap flags (Block D). LIGHT runners cap their young generation
|
|
92384
|
+
* (`--max-semi-space-size`) to reclaim V8's over-reservation — a measured ~15 MB/runner win.
|
|
92385
|
+
* HEAVY runners keep V8 defaults. The old-space cap is opt-in via env (unset = uncapped = no OOM
|
|
92386
|
+
* risk) so it can be enabled after before/after measurement on the real cluster.
|
|
92387
|
+
*
|
|
92388
|
+
* Kill-switch: `CAMSTACK_RUNNER_HEAP_TUNING=off` disables all flags (recover without a rebuild).
|
|
92389
|
+
* Overrides: `CAMSTACK_RUNNER_LIGHT_SEMI_SPACE_MB` (default 2), `CAMSTACK_RUNNER_LIGHT_MAX_OLD_MB`
|
|
92390
|
+
* (default unset = no old-space cap).
|
|
92391
|
+
*/
|
|
92392
|
+
function runnerHeapFlags(addons) {
|
|
92393
|
+
if (process.env["CAMSTACK_RUNNER_HEAP_TUNING"] === "off") return [];
|
|
92394
|
+
if (isHeavyRunner(addons)) return [];
|
|
92395
|
+
const semiMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_SEMI_SPACE_MB", 2);
|
|
92396
|
+
const oldMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_MAX_OLD_MB", 0);
|
|
92397
|
+
const flags = [];
|
|
92398
|
+
if (semiMb > 0) flags.push(`--max-semi-space-size=${semiMb}`);
|
|
92399
|
+
if (oldMb > 0) flags.push(`--max-old-space-size=${oldMb}`);
|
|
92400
|
+
return flags;
|
|
92401
|
+
}
|
|
92402
|
+
function positiveIntEnv(name, fallback) {
|
|
92403
|
+
const raw = process.env[name];
|
|
92404
|
+
if (raw === void 0) return fallback;
|
|
92405
|
+
const n = Number.parseInt(raw, 10);
|
|
92406
|
+
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
92407
|
+
}
|
|
92338
92408
|
var MAX_BACKOFF_MS = 3e4;
|
|
92339
92409
|
var CRASH_WINDOW_MS = 6e4;
|
|
92340
92410
|
var MAX_CRASHES_IN_WINDOW = 5;
|
|
@@ -92413,7 +92483,8 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
|
|
|
92413
92483
|
...parentUdsPath !== void 0 ? { CAMSTACK_PARENT_UDS_PATH: parentUdsPath } : {},
|
|
92414
92484
|
...env
|
|
92415
92485
|
};
|
|
92416
|
-
const
|
|
92486
|
+
const heapFlags = runnerHeapFlags(addons);
|
|
92487
|
+
const child = spawnFn(process.execPath, [...heapFlags, runnerPath], {
|
|
92417
92488
|
env: childEnv,
|
|
92418
92489
|
stdio: [
|
|
92419
92490
|
"ignore",
|