@camstack/system 1.2.3 → 1.2.4
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.js +5 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +5 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +283 -148
- package/dist/index.mjs +274 -142
- package/dist/kernel/deps/ensure-addon-natives.d.ts +41 -0
- package/dist/kernel/deps/ensure-native-prebuilds.d.ts +4 -0
- package/package.json +1 -1
|
@@ -1396,7 +1396,11 @@ async function applyLinkedDevicesPatch(pctx, deviceId, patch) {
|
|
|
1396
1396
|
//#region src/builtins/device-manager/device-store-sections.ts
|
|
1397
1397
|
/** The device-manager's device-store-backed sections. Add an entry to extend. */
|
|
1398
1398
|
var DEVICE_STORE_SECTIONS = [{
|
|
1399
|
-
keys: [
|
|
1399
|
+
keys: [
|
|
1400
|
+
LINKED_MODE_CONFIG_KEY,
|
|
1401
|
+
LINKED_IDS_CONFIG_KEY,
|
|
1402
|
+
LINKED_TRACKED_IDS_CONFIG_KEY
|
|
1403
|
+
],
|
|
1400
1404
|
apply: (pctx, deviceId, patch) => applyLinkedDevicesPatch(pctx, deviceId, patch)
|
|
1401
1405
|
}];
|
|
1402
1406
|
/**
|
|
@@ -1391,7 +1391,11 @@ async function applyLinkedDevicesPatch(pctx, deviceId, patch) {
|
|
|
1391
1391
|
//#region src/builtins/device-manager/device-store-sections.ts
|
|
1392
1392
|
/** The device-manager's device-store-backed sections. Add an entry to extend. */
|
|
1393
1393
|
var DEVICE_STORE_SECTIONS = [{
|
|
1394
|
-
keys: [
|
|
1394
|
+
keys: [
|
|
1395
|
+
LINKED_MODE_CONFIG_KEY,
|
|
1396
|
+
LINKED_IDS_CONFIG_KEY,
|
|
1397
|
+
LINKED_TRACKED_IDS_CONFIG_KEY
|
|
1398
|
+
],
|
|
1395
1399
|
apply: (pctx, deviceId, patch) => applyLinkedDevicesPatch(pctx, deviceId, patch)
|
|
1396
1400
|
}];
|
|
1397
1401
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -85,4 +85,5 @@ export type { DirectCallerOptions } from './addon/addon-api-factory.js';
|
|
|
85
85
|
export { IntegrationRegistry } from './builtins/sqlite-storage/integration-registry.js';
|
|
86
86
|
export * from './kernel/index.js';
|
|
87
87
|
export { runNpm, resolveNpmInvocation, type RunNpmOptions, type NpmInvocation, } from './kernel/deps/npm-command.js';
|
|
88
|
-
export { ensureNativePrebuilds, TRADITIONAL_NATIVE_PACKAGES, type EnsureNativePrebuildsOptions, type PrebuildTarget, type PrebuildFetchFn, } from './kernel/deps/ensure-native-prebuilds.js';
|
|
88
|
+
export { ensureNativePrebuilds, hasDotNode, NATIVE_SCAN_DEPTH, TRADITIONAL_NATIVE_PACKAGES, type EnsureNativePrebuildsOptions, type PrebuildTarget, type PrebuildFetchFn, } from './kernel/deps/ensure-native-prebuilds.js';
|
|
89
|
+
export { ensureAddonNativePrebuilds, type EnsureAddonNativePrebuildsOptions, type NativeEnsureOutcome, type NativeEnsureResult, } from './kernel/deps/ensure-addon-natives.js';
|
package/dist/index.js
CHANGED
|
@@ -3030,8 +3030,270 @@ var AddonManifest = class {
|
|
|
3030
3030
|
}
|
|
3031
3031
|
};
|
|
3032
3032
|
//#endregion
|
|
3033
|
-
//#region src/kernel/
|
|
3033
|
+
//#region src/kernel/deps/ensure-native-prebuilds.ts
|
|
3034
|
+
/**
|
|
3035
|
+
* `ensureNativePrebuilds` — the post-install native-prebuild ENSURE step the
|
|
3036
|
+
* shared `RootUpdateService` runs while staging a `@camstack/server` closure,
|
|
3037
|
+
* BEFORE its `findMissingNativePrebuilds` validator (in `@camstack/node-root`)
|
|
3038
|
+
* decides whether to arm the swap.
|
|
3039
|
+
*
|
|
3040
|
+
* Why this exists (Mac desktop incident, 2026-07-21): a runtime
|
|
3041
|
+
* `applyServerUpdate` on a packaged desktop stages the closure with
|
|
3042
|
+
* `npm install @camstack/server@X` under the app's BUNDLED bare Node. The one
|
|
3043
|
+
* TRADITIONAL (non-N-API) native, `better-sqlite3`, resolves its `.node` via
|
|
3044
|
+
* its `install` script `prebuild-install || node-gyp rebuild`. On a user's Mac
|
|
3045
|
+
* with no build toolchain (and where the auto `prebuild-install` does not
|
|
3046
|
+
* target the bundled Node's ABI/arch the way the DMG build's EXPLICIT
|
|
3047
|
+
* `prebuild-install --runtime node --target <bundled> --arch <arch>` call does)
|
|
3048
|
+
* that leaves the module SOURCE-ONLY → the closure has no `better-sqlite3`
|
|
3049
|
+
* `.node` → the validator refuses the swap ("staged closure missing native
|
|
3050
|
+
* prebuilds: better-sqlite3"). The docker image never hits this: its Node ABI
|
|
3051
|
+
* has a published prebuild the auto path fetches cleanly, and it carries a
|
|
3052
|
+
* build toolchain fallback.
|
|
3053
|
+
*
|
|
3054
|
+
* This mirrors the DMG build's explicit re-fetch (`scripts/stage-deps.mjs`)
|
|
3055
|
+
* into the runtime staging path: for each traditional native that is present
|
|
3056
|
+
* in the closure but missing its compiled `.node`, fetch the prebuild for the
|
|
3057
|
+
* RUNNING Node ABI/arch (the same Node that will load the swapped closure).
|
|
3058
|
+
*
|
|
3059
|
+
* N-API natives (`sharp`, `node-av`) are ABI-stable and ship their binaries as
|
|
3060
|
+
* platform SIBLING packages that npm fetches reliably with no compile step, so
|
|
3061
|
+
* they are not in scope here — a genuine miss there is a broken install the
|
|
3062
|
+
* validator should still reject, not something a prebuild re-fetch can heal.
|
|
3063
|
+
*
|
|
3064
|
+
* Injected into the update service from the construction site so the zero-dep
|
|
3065
|
+
* `@camstack/node-root` layer never depends on npm/@camstack/system, and the
|
|
3066
|
+
* fetch is unit-testable with a fake fetcher.
|
|
3067
|
+
*/
|
|
3034
3068
|
var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
|
|
3069
|
+
/** Bounded recursive `.node` scan depth inside a module dir. */
|
|
3070
|
+
var NATIVE_SCAN_DEPTH = 4;
|
|
3071
|
+
/** Timeout for a single `prebuild-install` fetch. */
|
|
3072
|
+
var PREBUILD_FETCH_TIMEOUT_MS = 12e4;
|
|
3073
|
+
/**
|
|
3074
|
+
* Traditional (compile-or-prebuild) natives an installed closure must carry a
|
|
3075
|
+
* `.node` for. Only these are ENSURE-able by a prebuild re-fetch; N-API natives
|
|
3076
|
+
* are excluded (see file header).
|
|
3077
|
+
*/
|
|
3078
|
+
var TRADITIONAL_NATIVE_PACKAGES = ["better-sqlite3"];
|
|
3079
|
+
/** Bounded recursive scan for ANY `*.node` file under `dir`. */
|
|
3080
|
+
function hasDotNode(dir, maxDepth) {
|
|
3081
|
+
let entries;
|
|
3082
|
+
try {
|
|
3083
|
+
entries = node_fs.readdirSync(dir, { withFileTypes: true });
|
|
3084
|
+
} catch {
|
|
3085
|
+
return false;
|
|
3086
|
+
}
|
|
3087
|
+
for (const entry of entries) if (entry.isFile() && entry.name.endsWith(".node")) return true;
|
|
3088
|
+
if (maxDepth <= 0) return false;
|
|
3089
|
+
for (const entry of entries) if (entry.isDirectory() && hasDotNode(node_path.join(dir, entry.name), maxDepth - 1)) return true;
|
|
3090
|
+
return false;
|
|
3091
|
+
}
|
|
3092
|
+
/** Locate the bundled `prebuild-install` bin.js inside the staged closure. */
|
|
3093
|
+
function resolvePrebuildInstallBin(closureDir, moduleDir) {
|
|
3094
|
+
return [node_path.join(moduleDir, "node_modules", "prebuild-install", "bin.js"), node_path.join(closureDir, "node_modules", "prebuild-install", "bin.js")].find((c) => node_fs.existsSync(c)) ?? null;
|
|
3095
|
+
}
|
|
3096
|
+
/**
|
|
3097
|
+
* Default fetcher: run the closure's own `prebuild-install` under the CURRENT
|
|
3098
|
+
* Node (`process.execPath`) — no reliance on `npx`/`npm` on PATH (a packaged
|
|
3099
|
+
* app ships a bare Node). Explicitly targets the running ABI/arch, mirroring
|
|
3100
|
+
* `scripts/stage-deps.mjs`.
|
|
3101
|
+
*/
|
|
3102
|
+
function makeDefaultFetch(closureDir, logger) {
|
|
3103
|
+
return async (moduleDir, target) => {
|
|
3104
|
+
const bin = resolvePrebuildInstallBin(closureDir, moduleDir);
|
|
3105
|
+
if (bin === null) throw new Error(`prebuild-install not found in the staged closure for ${moduleDir}`);
|
|
3106
|
+
logger.info("fetching native prebuild via prebuild-install", { meta: {
|
|
3107
|
+
moduleDir,
|
|
3108
|
+
bin,
|
|
3109
|
+
nodeVersion: target.nodeVersion,
|
|
3110
|
+
arch: target.arch
|
|
3111
|
+
} });
|
|
3112
|
+
await execFileAsync$1(process.execPath, [
|
|
3113
|
+
bin,
|
|
3114
|
+
"--runtime",
|
|
3115
|
+
"node",
|
|
3116
|
+
"--target",
|
|
3117
|
+
target.nodeVersion,
|
|
3118
|
+
"--arch",
|
|
3119
|
+
target.arch
|
|
3120
|
+
], {
|
|
3121
|
+
cwd: moduleDir,
|
|
3122
|
+
timeout: PREBUILD_FETCH_TIMEOUT_MS
|
|
3123
|
+
});
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
/**
|
|
3127
|
+
* Ensure every traditional native in `<closureDir>/node_modules` carries its
|
|
3128
|
+
* compiled `.node`, fetching the prebuild for the running ABI when missing.
|
|
3129
|
+
*
|
|
3130
|
+
* A NO-OP when nothing is missing (docker / tests) and when a native package is
|
|
3131
|
+
* absent from the closure. Never throws for a single package's fetch failure —
|
|
3132
|
+
* it logs and continues so the downstream `findMissingNativePrebuilds`
|
|
3133
|
+
* validator makes the final swap/abort decision. Returns the packages whose
|
|
3134
|
+
* prebuild was successfully fetched (for logging/tests).
|
|
3135
|
+
*/
|
|
3136
|
+
async function ensureNativePrebuilds(closureDir, options) {
|
|
3137
|
+
const nm = node_path.join(closureDir, "node_modules");
|
|
3138
|
+
const target = options.target ?? {
|
|
3139
|
+
nodeVersion: process.versions.node,
|
|
3140
|
+
arch: process.arch
|
|
3141
|
+
};
|
|
3142
|
+
const packages = options.packages ?? TRADITIONAL_NATIVE_PACKAGES;
|
|
3143
|
+
const fetchPrebuild = options.fetchPrebuild ?? makeDefaultFetch(closureDir, options.logger);
|
|
3144
|
+
const fetched = [];
|
|
3145
|
+
for (const pkg of packages) {
|
|
3146
|
+
const moduleDir = node_path.join(nm, pkg);
|
|
3147
|
+
if (!node_fs.existsSync(moduleDir)) continue;
|
|
3148
|
+
if (hasDotNode(moduleDir, 4)) continue;
|
|
3149
|
+
options.logger.warn("staged closure native prebuild missing — fetching for running ABI", { meta: {
|
|
3150
|
+
pkg,
|
|
3151
|
+
moduleDir,
|
|
3152
|
+
nodeVersion: target.nodeVersion,
|
|
3153
|
+
arch: target.arch
|
|
3154
|
+
} });
|
|
3155
|
+
try {
|
|
3156
|
+
await fetchPrebuild(moduleDir, target);
|
|
3157
|
+
} catch (err) {
|
|
3158
|
+
options.logger.error("native prebuild fetch failed (validator will decide)", { meta: {
|
|
3159
|
+
pkg,
|
|
3160
|
+
error: (0, _camstack_types_addon.errMsg)(err)
|
|
3161
|
+
} });
|
|
3162
|
+
continue;
|
|
3163
|
+
}
|
|
3164
|
+
if (hasDotNode(moduleDir, 4)) {
|
|
3165
|
+
fetched.push(pkg);
|
|
3166
|
+
options.logger.info("native prebuild fetched", { meta: { pkg } });
|
|
3167
|
+
} else options.logger.error("native prebuild still missing after fetch", { meta: { pkg } });
|
|
3168
|
+
}
|
|
3169
|
+
return fetched;
|
|
3170
|
+
}
|
|
3171
|
+
//#endregion
|
|
3172
|
+
//#region src/kernel/deps/ensure-addon-natives.ts
|
|
3173
|
+
/**
|
|
3174
|
+
* `ensureAddonNativePrebuilds` — the POST-install native-prebuild ENSURE pass an
|
|
3175
|
+
* addon install runs on the INSTALLED addon dir AFTER `npm install`.
|
|
3176
|
+
*
|
|
3177
|
+
* Why this exists (Mac desktop first-boot FATAL, 2026-07-21): `installCopy`
|
|
3178
|
+
* copies `dist/`, plants prebuilt natives from the bundled addon copy
|
|
3179
|
+
* (`copyBundledNativeModules`), then `npm install`s the addon's runtime deps.
|
|
3180
|
+
* On a TOOLCHAIN-LESS host that `npm install` RE-CREATES the traditional native
|
|
3181
|
+
* `better-sqlite3` SOURCE-ONLY — clobbering the good `.node` the pre-install
|
|
3182
|
+
* copy just planted — and the runner later dies at addon init with a cryptic
|
|
3183
|
+
* "Could not locate the bindings file". The copy-BEFORE-install fix was
|
|
3184
|
+
* empirically insufficient: npm's own install in the copied dir replaces the
|
|
3185
|
+
* satisfied dep without its binary.
|
|
3186
|
+
*
|
|
3187
|
+
* This pass runs LAST, so it always sees the final on-disk state npm left, and
|
|
3188
|
+
* per referenced traditional native still lacking its compiled `.node`:
|
|
3189
|
+
* 1. bundled-copy first — if the bundled addon copy carries the matching
|
|
3190
|
+
* `.node` (same machine → same platform/arch/ABI), re-plant it DIRECTLY.
|
|
3191
|
+
* Cheapest + offline-safe; runs BEFORE any network fetch.
|
|
3192
|
+
* 2. fetch fallback — otherwise fetch the prebuild for the running ABI via the
|
|
3193
|
+
* shared `ensureNativePrebuilds` pointed at the addon dir.
|
|
3194
|
+
* 3. FAIL LOUD — if the native is STILL source-only after both, log a clear
|
|
3195
|
+
* error naming module + addon and THROW so the install fails at install
|
|
3196
|
+
* time rather than at addon init.
|
|
3197
|
+
*
|
|
3198
|
+
* N-API natives (`sharp`, `node-av`) ship their binaries as ABI-stable platform
|
|
3199
|
+
* SIBLING packages npm fetches without a compile step, so they are excluded from
|
|
3200
|
+
* the default set — a miss there is a broken install, not something a prebuild
|
|
3201
|
+
* re-fetch heals (same rationale as `ensureNativePrebuilds`).
|
|
3202
|
+
*/
|
|
3203
|
+
/**
|
|
3204
|
+
* Re-plant `<srcModuleDir>` → `<dstModuleDir>` when the source carries a `.node`.
|
|
3205
|
+
* Returns whether the destination ends up with a compiled binary.
|
|
3206
|
+
*/
|
|
3207
|
+
function replantFromBundled(srcModuleDir, dstModuleDir, hasNative) {
|
|
3208
|
+
if (!hasNative(srcModuleDir)) return false;
|
|
3209
|
+
try {
|
|
3210
|
+
node_fs.rmSync(dstModuleDir, {
|
|
3211
|
+
recursive: true,
|
|
3212
|
+
force: true
|
|
3213
|
+
});
|
|
3214
|
+
node_fs.mkdirSync(node_path.dirname(dstModuleDir), { recursive: true });
|
|
3215
|
+
node_fs.cpSync(srcModuleDir, dstModuleDir, {
|
|
3216
|
+
recursive: true,
|
|
3217
|
+
dereference: true
|
|
3218
|
+
});
|
|
3219
|
+
} catch {
|
|
3220
|
+
return false;
|
|
3221
|
+
}
|
|
3222
|
+
return hasNative(dstModuleDir);
|
|
3223
|
+
}
|
|
3224
|
+
/**
|
|
3225
|
+
* Ensure every referenced traditional native in `<addonDir>/node_modules`
|
|
3226
|
+
* carries its compiled `.node` after the addon's `npm install`. Bundled-copy
|
|
3227
|
+
* first (offline), prebuild-fetch fallback, LOUD throw on a residual miss.
|
|
3228
|
+
*
|
|
3229
|
+
* SKIPS a native absent from the addon's own `node_modules` (it resolves via a
|
|
3230
|
+
* hoisted host copy — not this addon's concern). A NO-OP in docker / dev where
|
|
3231
|
+
* npm produced a working `.node` (already-present). Returns the per-native
|
|
3232
|
+
* outcomes for logging/tests.
|
|
3233
|
+
*
|
|
3234
|
+
* @throws if any required native is still source-only after copy + fetch.
|
|
3235
|
+
*/
|
|
3236
|
+
async function ensureAddonNativePrebuilds(addonDir, options) {
|
|
3237
|
+
const packages = options.packages ?? TRADITIONAL_NATIVE_PACKAGES;
|
|
3238
|
+
const hasNative = options.hasNative ?? ((dir) => hasDotNode(dir, 4));
|
|
3239
|
+
const nm = node_path.join(addonDir, "node_modules");
|
|
3240
|
+
const results = [];
|
|
3241
|
+
const missing = [];
|
|
3242
|
+
for (const pkg of packages) {
|
|
3243
|
+
const moduleDir = node_path.join(nm, pkg);
|
|
3244
|
+
if (!node_fs.existsSync(moduleDir)) continue;
|
|
3245
|
+
if (hasNative(moduleDir)) {
|
|
3246
|
+
results.push({
|
|
3247
|
+
pkg,
|
|
3248
|
+
outcome: "already-present"
|
|
3249
|
+
});
|
|
3250
|
+
continue;
|
|
3251
|
+
}
|
|
3252
|
+
if (options.bundledSourceDir) {
|
|
3253
|
+
const src = node_path.join(options.bundledSourceDir, "node_modules", pkg);
|
|
3254
|
+
if (replantFromBundled(src, moduleDir, hasNative)) {
|
|
3255
|
+
options.logger.info("addon native prebuild re-planted from bundled copy (offline)", { meta: {
|
|
3256
|
+
addon: options.addonName,
|
|
3257
|
+
pkg,
|
|
3258
|
+
src,
|
|
3259
|
+
moduleDir
|
|
3260
|
+
} });
|
|
3261
|
+
results.push({
|
|
3262
|
+
pkg,
|
|
3263
|
+
outcome: "copied"
|
|
3264
|
+
});
|
|
3265
|
+
continue;
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
if ((await ensureNativePrebuilds(addonDir, {
|
|
3269
|
+
logger: options.logger,
|
|
3270
|
+
packages: [pkg],
|
|
3271
|
+
...options.target ? { target: options.target } : {},
|
|
3272
|
+
...options.fetchPrebuild ? { fetchPrebuild: options.fetchPrebuild } : {}
|
|
3273
|
+
})).includes(pkg)) {
|
|
3274
|
+
results.push({
|
|
3275
|
+
pkg,
|
|
3276
|
+
outcome: "fetched"
|
|
3277
|
+
});
|
|
3278
|
+
continue;
|
|
3279
|
+
}
|
|
3280
|
+
options.logger.error("addon native prebuild MISSING after ensure — module has no compiled .node", { meta: {
|
|
3281
|
+
addon: options.addonName,
|
|
3282
|
+
pkg,
|
|
3283
|
+
moduleDir
|
|
3284
|
+
} });
|
|
3285
|
+
results.push({
|
|
3286
|
+
pkg,
|
|
3287
|
+
outcome: "missing"
|
|
3288
|
+
});
|
|
3289
|
+
missing.push(pkg);
|
|
3290
|
+
}
|
|
3291
|
+
if (missing.length > 0) throw new Error(`${options.addonName} — required native module(s) missing a compiled .node after install (bundled-copy + prebuild-fetch both failed): ${missing.join(", ")}`);
|
|
3292
|
+
return results;
|
|
3293
|
+
}
|
|
3294
|
+
//#endregion
|
|
3295
|
+
//#region src/kernel/addon-installer.ts
|
|
3296
|
+
var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
3035
3297
|
function parseInstallSource(value) {
|
|
3036
3298
|
switch (value) {
|
|
3037
3299
|
case "npm":
|
|
@@ -3340,6 +3602,11 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3340
3602
|
} catch (err) {
|
|
3341
3603
|
this.logger.warn(`${packageName} — native deps install failed (continuing)`, { meta: { error: require_dist.errMsg(err) } });
|
|
3342
3604
|
}
|
|
3605
|
+
await ensureAddonNativePrebuilds(targetDir, {
|
|
3606
|
+
addonName: packageName,
|
|
3607
|
+
logger: this.logger,
|
|
3608
|
+
bundledSourceDir: sourceDir
|
|
3609
|
+
});
|
|
3343
3610
|
this.logger.info(`${packageName} — copied from ${sourceDir}`);
|
|
3344
3611
|
return {
|
|
3345
3612
|
name: pkgData.name,
|
|
@@ -3489,7 +3756,7 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3489
3756
|
const stagingDir = node_path.join(stagingRoot, `install-${process.pid}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
|
|
3490
3757
|
ensureDir(stagingDir);
|
|
3491
3758
|
try {
|
|
3492
|
-
await execFileAsync
|
|
3759
|
+
await execFileAsync("tar", [
|
|
3493
3760
|
"-xzf",
|
|
3494
3761
|
tgzPath,
|
|
3495
3762
|
"-C",
|
|
@@ -3526,6 +3793,10 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3526
3793
|
} catch (nativeErr) {
|
|
3527
3794
|
throw new Error(`${pkgView.name} — native deps install failed: ${require_dist.errMsg(nativeErr)}`, { cause: nativeErr });
|
|
3528
3795
|
}
|
|
3796
|
+
await ensureAddonNativePrebuilds(pkgDir, {
|
|
3797
|
+
addonName: pkgView.name,
|
|
3798
|
+
logger: this.logger
|
|
3799
|
+
});
|
|
3529
3800
|
await this.evictInstallDir(targetDir);
|
|
3530
3801
|
ensureDir(node_path.dirname(targetDir));
|
|
3531
3802
|
await node_fs.promises.rename(pkgDir, targetDir);
|
|
@@ -80144,7 +80415,7 @@ var require_compression = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
|
|
|
80144
80415
|
var { defaultsDeep } = require_lodash();
|
|
80145
80416
|
var { parseByteString } = require_utils$3();
|
|
80146
80417
|
var zlib = require("zlib");
|
|
80147
|
-
var { promisify
|
|
80418
|
+
var { promisify } = require("util");
|
|
80148
80419
|
/**
|
|
80149
80420
|
* This is a transmission compression middleware. It supports
|
|
80150
80421
|
* the `deflate`, `deflateRaw` & `gzip` compression methods.
|
|
@@ -80162,16 +80433,16 @@ var require_compression = /* @__PURE__ */ require_chunk.__commonJSMin(((exports,
|
|
|
80162
80433
|
const threshold = parseByteString(opts.threshold);
|
|
80163
80434
|
switch (opts.method) {
|
|
80164
80435
|
case "deflate":
|
|
80165
|
-
compress = promisify
|
|
80166
|
-
decompress = promisify
|
|
80436
|
+
compress = promisify(zlib.deflate);
|
|
80437
|
+
decompress = promisify(zlib.inflate);
|
|
80167
80438
|
break;
|
|
80168
80439
|
case "deflateRaw":
|
|
80169
|
-
compress = promisify
|
|
80170
|
-
decompress = promisify
|
|
80440
|
+
compress = promisify(zlib.deflateRaw);
|
|
80441
|
+
decompress = promisify(zlib.inflateRaw);
|
|
80171
80442
|
break;
|
|
80172
80443
|
case "gzip":
|
|
80173
|
-
compress = promisify
|
|
80174
|
-
decompress = promisify
|
|
80444
|
+
compress = promisify(zlib.gzip);
|
|
80445
|
+
decompress = promisify(zlib.gunzip);
|
|
80175
80446
|
break;
|
|
80176
80447
|
default:
|
|
80177
80448
|
/* istanbul ignore next */
|
|
@@ -93398,145 +93669,6 @@ var LifecycleJobEngine = class {
|
|
|
93398
93669
|
}
|
|
93399
93670
|
};
|
|
93400
93671
|
//#endregion
|
|
93401
|
-
//#region src/kernel/deps/ensure-native-prebuilds.ts
|
|
93402
|
-
/**
|
|
93403
|
-
* `ensureNativePrebuilds` — the post-install native-prebuild ENSURE step the
|
|
93404
|
-
* shared `RootUpdateService` runs while staging a `@camstack/server` closure,
|
|
93405
|
-
* BEFORE its `findMissingNativePrebuilds` validator (in `@camstack/node-root`)
|
|
93406
|
-
* decides whether to arm the swap.
|
|
93407
|
-
*
|
|
93408
|
-
* Why this exists (Mac desktop incident, 2026-07-21): a runtime
|
|
93409
|
-
* `applyServerUpdate` on a packaged desktop stages the closure with
|
|
93410
|
-
* `npm install @camstack/server@X` under the app's BUNDLED bare Node. The one
|
|
93411
|
-
* TRADITIONAL (non-N-API) native, `better-sqlite3`, resolves its `.node` via
|
|
93412
|
-
* its `install` script `prebuild-install || node-gyp rebuild`. On a user's Mac
|
|
93413
|
-
* with no build toolchain (and where the auto `prebuild-install` does not
|
|
93414
|
-
* target the bundled Node's ABI/arch the way the DMG build's EXPLICIT
|
|
93415
|
-
* `prebuild-install --runtime node --target <bundled> --arch <arch>` call does)
|
|
93416
|
-
* that leaves the module SOURCE-ONLY → the closure has no `better-sqlite3`
|
|
93417
|
-
* `.node` → the validator refuses the swap ("staged closure missing native
|
|
93418
|
-
* prebuilds: better-sqlite3"). The docker image never hits this: its Node ABI
|
|
93419
|
-
* has a published prebuild the auto path fetches cleanly, and it carries a
|
|
93420
|
-
* build toolchain fallback.
|
|
93421
|
-
*
|
|
93422
|
-
* This mirrors the DMG build's explicit re-fetch (`scripts/stage-deps.mjs`)
|
|
93423
|
-
* into the runtime staging path: for each traditional native that is present
|
|
93424
|
-
* in the closure but missing its compiled `.node`, fetch the prebuild for the
|
|
93425
|
-
* RUNNING Node ABI/arch (the same Node that will load the swapped closure).
|
|
93426
|
-
*
|
|
93427
|
-
* N-API natives (`sharp`, `node-av`) are ABI-stable and ship their binaries as
|
|
93428
|
-
* platform SIBLING packages that npm fetches reliably with no compile step, so
|
|
93429
|
-
* they are not in scope here — a genuine miss there is a broken install the
|
|
93430
|
-
* validator should still reject, not something a prebuild re-fetch can heal.
|
|
93431
|
-
*
|
|
93432
|
-
* Injected into the update service from the construction site so the zero-dep
|
|
93433
|
-
* `@camstack/node-root` layer never depends on npm/@camstack/system, and the
|
|
93434
|
-
* fetch is unit-testable with a fake fetcher.
|
|
93435
|
-
*/
|
|
93436
|
-
var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
93437
|
-
/** Bounded recursive `.node` scan depth inside a module dir. */
|
|
93438
|
-
var NATIVE_SCAN_DEPTH = 4;
|
|
93439
|
-
/** Timeout for a single `prebuild-install` fetch. */
|
|
93440
|
-
var PREBUILD_FETCH_TIMEOUT_MS = 12e4;
|
|
93441
|
-
/**
|
|
93442
|
-
* Traditional (compile-or-prebuild) natives an installed closure must carry a
|
|
93443
|
-
* `.node` for. Only these are ENSURE-able by a prebuild re-fetch; N-API natives
|
|
93444
|
-
* are excluded (see file header).
|
|
93445
|
-
*/
|
|
93446
|
-
var TRADITIONAL_NATIVE_PACKAGES = ["better-sqlite3"];
|
|
93447
|
-
/** Bounded recursive scan for ANY `*.node` file under `dir`. */
|
|
93448
|
-
function hasDotNode(dir, maxDepth) {
|
|
93449
|
-
let entries;
|
|
93450
|
-
try {
|
|
93451
|
-
entries = node_fs.readdirSync(dir, { withFileTypes: true });
|
|
93452
|
-
} catch {
|
|
93453
|
-
return false;
|
|
93454
|
-
}
|
|
93455
|
-
for (const entry of entries) if (entry.isFile() && entry.name.endsWith(".node")) return true;
|
|
93456
|
-
if (maxDepth <= 0) return false;
|
|
93457
|
-
for (const entry of entries) if (entry.isDirectory() && hasDotNode(node_path.join(dir, entry.name), maxDepth - 1)) return true;
|
|
93458
|
-
return false;
|
|
93459
|
-
}
|
|
93460
|
-
/** Locate the bundled `prebuild-install` bin.js inside the staged closure. */
|
|
93461
|
-
function resolvePrebuildInstallBin(closureDir, moduleDir) {
|
|
93462
|
-
return [node_path.join(moduleDir, "node_modules", "prebuild-install", "bin.js"), node_path.join(closureDir, "node_modules", "prebuild-install", "bin.js")].find((c) => node_fs.existsSync(c)) ?? null;
|
|
93463
|
-
}
|
|
93464
|
-
/**
|
|
93465
|
-
* Default fetcher: run the closure's own `prebuild-install` under the CURRENT
|
|
93466
|
-
* Node (`process.execPath`) — no reliance on `npx`/`npm` on PATH (a packaged
|
|
93467
|
-
* app ships a bare Node). Explicitly targets the running ABI/arch, mirroring
|
|
93468
|
-
* `scripts/stage-deps.mjs`.
|
|
93469
|
-
*/
|
|
93470
|
-
function makeDefaultFetch(closureDir, logger) {
|
|
93471
|
-
return async (moduleDir, target) => {
|
|
93472
|
-
const bin = resolvePrebuildInstallBin(closureDir, moduleDir);
|
|
93473
|
-
if (bin === null) throw new Error(`prebuild-install not found in the staged closure for ${moduleDir}`);
|
|
93474
|
-
logger.info("fetching native prebuild via prebuild-install", { meta: {
|
|
93475
|
-
moduleDir,
|
|
93476
|
-
bin,
|
|
93477
|
-
nodeVersion: target.nodeVersion,
|
|
93478
|
-
arch: target.arch
|
|
93479
|
-
} });
|
|
93480
|
-
await execFileAsync(process.execPath, [
|
|
93481
|
-
bin,
|
|
93482
|
-
"--runtime",
|
|
93483
|
-
"node",
|
|
93484
|
-
"--target",
|
|
93485
|
-
target.nodeVersion,
|
|
93486
|
-
"--arch",
|
|
93487
|
-
target.arch
|
|
93488
|
-
], {
|
|
93489
|
-
cwd: moduleDir,
|
|
93490
|
-
timeout: PREBUILD_FETCH_TIMEOUT_MS
|
|
93491
|
-
});
|
|
93492
|
-
};
|
|
93493
|
-
}
|
|
93494
|
-
/**
|
|
93495
|
-
* Ensure every traditional native in `<closureDir>/node_modules` carries its
|
|
93496
|
-
* compiled `.node`, fetching the prebuild for the running ABI when missing.
|
|
93497
|
-
*
|
|
93498
|
-
* A NO-OP when nothing is missing (docker / tests) and when a native package is
|
|
93499
|
-
* absent from the closure. Never throws for a single package's fetch failure —
|
|
93500
|
-
* it logs and continues so the downstream `findMissingNativePrebuilds`
|
|
93501
|
-
* validator makes the final swap/abort decision. Returns the packages whose
|
|
93502
|
-
* prebuild was successfully fetched (for logging/tests).
|
|
93503
|
-
*/
|
|
93504
|
-
async function ensureNativePrebuilds(closureDir, options) {
|
|
93505
|
-
const nm = node_path.join(closureDir, "node_modules");
|
|
93506
|
-
const target = options.target ?? {
|
|
93507
|
-
nodeVersion: process.versions.node,
|
|
93508
|
-
arch: process.arch
|
|
93509
|
-
};
|
|
93510
|
-
const packages = options.packages ?? TRADITIONAL_NATIVE_PACKAGES;
|
|
93511
|
-
const fetchPrebuild = options.fetchPrebuild ?? makeDefaultFetch(closureDir, options.logger);
|
|
93512
|
-
const fetched = [];
|
|
93513
|
-
for (const pkg of packages) {
|
|
93514
|
-
const moduleDir = node_path.join(nm, pkg);
|
|
93515
|
-
if (!node_fs.existsSync(moduleDir)) continue;
|
|
93516
|
-
if (hasDotNode(moduleDir, NATIVE_SCAN_DEPTH)) continue;
|
|
93517
|
-
options.logger.warn("staged closure native prebuild missing — fetching for running ABI", { meta: {
|
|
93518
|
-
pkg,
|
|
93519
|
-
moduleDir,
|
|
93520
|
-
nodeVersion: target.nodeVersion,
|
|
93521
|
-
arch: target.arch
|
|
93522
|
-
} });
|
|
93523
|
-
try {
|
|
93524
|
-
await fetchPrebuild(moduleDir, target);
|
|
93525
|
-
} catch (err) {
|
|
93526
|
-
options.logger.error("native prebuild fetch failed (validator will decide)", { meta: {
|
|
93527
|
-
pkg,
|
|
93528
|
-
error: (0, _camstack_types_addon.errMsg)(err)
|
|
93529
|
-
} });
|
|
93530
|
-
continue;
|
|
93531
|
-
}
|
|
93532
|
-
if (hasDotNode(moduleDir, NATIVE_SCAN_DEPTH)) {
|
|
93533
|
-
fetched.push(pkg);
|
|
93534
|
-
options.logger.info("native prebuild fetched", { meta: { pkg } });
|
|
93535
|
-
} else options.logger.error("native prebuild still missing after fetch", { meta: { pkg } });
|
|
93536
|
-
}
|
|
93537
|
-
return fetched;
|
|
93538
|
-
}
|
|
93539
|
-
//#endregion
|
|
93540
93672
|
exports.AGENT_CAP_FWD_ACTION = require_manifest_python_deps.AGENT_CAP_FWD_ACTION;
|
|
93541
93673
|
exports.AGENT_CAP_FWD_SERVICE = require_manifest_python_deps.AGENT_CAP_FWD_SERVICE;
|
|
93542
93674
|
exports.AGENT_READINESS_SERVICE_NAME = AGENT_READINESS_SERVICE_NAME;
|
|
@@ -93603,6 +93735,7 @@ exports.LogRingBuffer = LogRingBuffer;
|
|
|
93603
93735
|
exports.METHOD_ACCESS_MAP = require_dist.METHOD_ACCESS_MAP;
|
|
93604
93736
|
exports.ModelDownloadService = require_model_download_service.ModelDownloadService;
|
|
93605
93737
|
exports.NATIVE_PROVIDER_SERVICE_INFIX = require_manifest_python_deps.NATIVE_PROVIDER_SERVICE_INFIX;
|
|
93738
|
+
exports.NATIVE_SCAN_DEPTH = NATIVE_SCAN_DEPTH;
|
|
93606
93739
|
exports.NativeMetricsAddon = require_builtins_native_metrics_native_metrics_addon.default;
|
|
93607
93740
|
exports.NativeMetricsProvider = require_builtins_native_metrics_native_metrics_addon.NativeMetricsProvider;
|
|
93608
93741
|
exports.NetworkQualityTracker = NetworkQualityTracker;
|
|
@@ -93712,6 +93845,7 @@ exports.downloadFile = require_model_download_service.downloadFile;
|
|
|
93712
93845
|
exports.downloadModel = require_model_download_service.downloadModel;
|
|
93713
93846
|
exports.emitDownForOwnedCaps = require_dist.emitDownForOwnedCaps;
|
|
93714
93847
|
exports.encodeFrame = require_manifest_python_deps.encodeFrame;
|
|
93848
|
+
exports.ensureAddonNativePrebuilds = ensureAddonNativePrebuilds;
|
|
93715
93849
|
Object.defineProperty(exports, "ensureBinary", {
|
|
93716
93850
|
enumerable: true,
|
|
93717
93851
|
get: function() {
|
|
@@ -93772,6 +93906,7 @@ Object.defineProperty(exports, "getPythonDownloadUrl", {
|
|
|
93772
93906
|
exports.getRestartMarkerPath = getRestartMarkerPath;
|
|
93773
93907
|
exports.getSinglePidStats = require_resource_monitor.getSinglePidStats;
|
|
93774
93908
|
exports.getWorkerDeviceRegistry = require_manifest_python_deps.getWorkerDeviceRegistry;
|
|
93909
|
+
exports.hasDotNode = hasDotNode;
|
|
93775
93910
|
exports.hashClusterSecret = hashClusterSecret;
|
|
93776
93911
|
exports.installManifestNativeDeps = require_manifest_python_deps.installManifestNativeDeps;
|
|
93777
93912
|
exports.installManifestPythonDeps = require_manifest_python_deps.installManifestPythonDeps;
|
package/dist/index.mjs
CHANGED
|
@@ -3022,8 +3022,270 @@ var AddonManifest = class {
|
|
|
3022
3022
|
}
|
|
3023
3023
|
};
|
|
3024
3024
|
//#endregion
|
|
3025
|
-
//#region src/kernel/
|
|
3025
|
+
//#region src/kernel/deps/ensure-native-prebuilds.ts
|
|
3026
|
+
/**
|
|
3027
|
+
* `ensureNativePrebuilds` — the post-install native-prebuild ENSURE step the
|
|
3028
|
+
* shared `RootUpdateService` runs while staging a `@camstack/server` closure,
|
|
3029
|
+
* BEFORE its `findMissingNativePrebuilds` validator (in `@camstack/node-root`)
|
|
3030
|
+
* decides whether to arm the swap.
|
|
3031
|
+
*
|
|
3032
|
+
* Why this exists (Mac desktop incident, 2026-07-21): a runtime
|
|
3033
|
+
* `applyServerUpdate` on a packaged desktop stages the closure with
|
|
3034
|
+
* `npm install @camstack/server@X` under the app's BUNDLED bare Node. The one
|
|
3035
|
+
* TRADITIONAL (non-N-API) native, `better-sqlite3`, resolves its `.node` via
|
|
3036
|
+
* its `install` script `prebuild-install || node-gyp rebuild`. On a user's Mac
|
|
3037
|
+
* with no build toolchain (and where the auto `prebuild-install` does not
|
|
3038
|
+
* target the bundled Node's ABI/arch the way the DMG build's EXPLICIT
|
|
3039
|
+
* `prebuild-install --runtime node --target <bundled> --arch <arch>` call does)
|
|
3040
|
+
* that leaves the module SOURCE-ONLY → the closure has no `better-sqlite3`
|
|
3041
|
+
* `.node` → the validator refuses the swap ("staged closure missing native
|
|
3042
|
+
* prebuilds: better-sqlite3"). The docker image never hits this: its Node ABI
|
|
3043
|
+
* has a published prebuild the auto path fetches cleanly, and it carries a
|
|
3044
|
+
* build toolchain fallback.
|
|
3045
|
+
*
|
|
3046
|
+
* This mirrors the DMG build's explicit re-fetch (`scripts/stage-deps.mjs`)
|
|
3047
|
+
* into the runtime staging path: for each traditional native that is present
|
|
3048
|
+
* in the closure but missing its compiled `.node`, fetch the prebuild for the
|
|
3049
|
+
* RUNNING Node ABI/arch (the same Node that will load the swapped closure).
|
|
3050
|
+
*
|
|
3051
|
+
* N-API natives (`sharp`, `node-av`) are ABI-stable and ship their binaries as
|
|
3052
|
+
* platform SIBLING packages that npm fetches reliably with no compile step, so
|
|
3053
|
+
* they are not in scope here — a genuine miss there is a broken install the
|
|
3054
|
+
* validator should still reject, not something a prebuild re-fetch can heal.
|
|
3055
|
+
*
|
|
3056
|
+
* Injected into the update service from the construction site so the zero-dep
|
|
3057
|
+
* `@camstack/node-root` layer never depends on npm/@camstack/system, and the
|
|
3058
|
+
* fetch is unit-testable with a fake fetcher.
|
|
3059
|
+
*/
|
|
3026
3060
|
var execFileAsync$1 = promisify(execFile);
|
|
3061
|
+
/** Bounded recursive `.node` scan depth inside a module dir. */
|
|
3062
|
+
var NATIVE_SCAN_DEPTH = 4;
|
|
3063
|
+
/** Timeout for a single `prebuild-install` fetch. */
|
|
3064
|
+
var PREBUILD_FETCH_TIMEOUT_MS = 12e4;
|
|
3065
|
+
/**
|
|
3066
|
+
* Traditional (compile-or-prebuild) natives an installed closure must carry a
|
|
3067
|
+
* `.node` for. Only these are ENSURE-able by a prebuild re-fetch; N-API natives
|
|
3068
|
+
* are excluded (see file header).
|
|
3069
|
+
*/
|
|
3070
|
+
var TRADITIONAL_NATIVE_PACKAGES = ["better-sqlite3"];
|
|
3071
|
+
/** Bounded recursive scan for ANY `*.node` file under `dir`. */
|
|
3072
|
+
function hasDotNode(dir, maxDepth) {
|
|
3073
|
+
let entries;
|
|
3074
|
+
try {
|
|
3075
|
+
entries = fs$17.readdirSync(dir, { withFileTypes: true });
|
|
3076
|
+
} catch {
|
|
3077
|
+
return false;
|
|
3078
|
+
}
|
|
3079
|
+
for (const entry of entries) if (entry.isFile() && entry.name.endsWith(".node")) return true;
|
|
3080
|
+
if (maxDepth <= 0) return false;
|
|
3081
|
+
for (const entry of entries) if (entry.isDirectory() && hasDotNode(path$39.join(dir, entry.name), maxDepth - 1)) return true;
|
|
3082
|
+
return false;
|
|
3083
|
+
}
|
|
3084
|
+
/** Locate the bundled `prebuild-install` bin.js inside the staged closure. */
|
|
3085
|
+
function resolvePrebuildInstallBin(closureDir, moduleDir) {
|
|
3086
|
+
return [path$39.join(moduleDir, "node_modules", "prebuild-install", "bin.js"), path$39.join(closureDir, "node_modules", "prebuild-install", "bin.js")].find((c) => fs$17.existsSync(c)) ?? null;
|
|
3087
|
+
}
|
|
3088
|
+
/**
|
|
3089
|
+
* Default fetcher: run the closure's own `prebuild-install` under the CURRENT
|
|
3090
|
+
* Node (`process.execPath`) — no reliance on `npx`/`npm` on PATH (a packaged
|
|
3091
|
+
* app ships a bare Node). Explicitly targets the running ABI/arch, mirroring
|
|
3092
|
+
* `scripts/stage-deps.mjs`.
|
|
3093
|
+
*/
|
|
3094
|
+
function makeDefaultFetch(closureDir, logger) {
|
|
3095
|
+
return async (moduleDir, target) => {
|
|
3096
|
+
const bin = resolvePrebuildInstallBin(closureDir, moduleDir);
|
|
3097
|
+
if (bin === null) throw new Error(`prebuild-install not found in the staged closure for ${moduleDir}`);
|
|
3098
|
+
logger.info("fetching native prebuild via prebuild-install", { meta: {
|
|
3099
|
+
moduleDir,
|
|
3100
|
+
bin,
|
|
3101
|
+
nodeVersion: target.nodeVersion,
|
|
3102
|
+
arch: target.arch
|
|
3103
|
+
} });
|
|
3104
|
+
await execFileAsync$1(process.execPath, [
|
|
3105
|
+
bin,
|
|
3106
|
+
"--runtime",
|
|
3107
|
+
"node",
|
|
3108
|
+
"--target",
|
|
3109
|
+
target.nodeVersion,
|
|
3110
|
+
"--arch",
|
|
3111
|
+
target.arch
|
|
3112
|
+
], {
|
|
3113
|
+
cwd: moduleDir,
|
|
3114
|
+
timeout: PREBUILD_FETCH_TIMEOUT_MS
|
|
3115
|
+
});
|
|
3116
|
+
};
|
|
3117
|
+
}
|
|
3118
|
+
/**
|
|
3119
|
+
* Ensure every traditional native in `<closureDir>/node_modules` carries its
|
|
3120
|
+
* compiled `.node`, fetching the prebuild for the running ABI when missing.
|
|
3121
|
+
*
|
|
3122
|
+
* A NO-OP when nothing is missing (docker / tests) and when a native package is
|
|
3123
|
+
* absent from the closure. Never throws for a single package's fetch failure —
|
|
3124
|
+
* it logs and continues so the downstream `findMissingNativePrebuilds`
|
|
3125
|
+
* validator makes the final swap/abort decision. Returns the packages whose
|
|
3126
|
+
* prebuild was successfully fetched (for logging/tests).
|
|
3127
|
+
*/
|
|
3128
|
+
async function ensureNativePrebuilds(closureDir, options) {
|
|
3129
|
+
const nm = path$39.join(closureDir, "node_modules");
|
|
3130
|
+
const target = options.target ?? {
|
|
3131
|
+
nodeVersion: process.versions.node,
|
|
3132
|
+
arch: process.arch
|
|
3133
|
+
};
|
|
3134
|
+
const packages = options.packages ?? TRADITIONAL_NATIVE_PACKAGES;
|
|
3135
|
+
const fetchPrebuild = options.fetchPrebuild ?? makeDefaultFetch(closureDir, options.logger);
|
|
3136
|
+
const fetched = [];
|
|
3137
|
+
for (const pkg of packages) {
|
|
3138
|
+
const moduleDir = path$39.join(nm, pkg);
|
|
3139
|
+
if (!fs$17.existsSync(moduleDir)) continue;
|
|
3140
|
+
if (hasDotNode(moduleDir, 4)) continue;
|
|
3141
|
+
options.logger.warn("staged closure native prebuild missing — fetching for running ABI", { meta: {
|
|
3142
|
+
pkg,
|
|
3143
|
+
moduleDir,
|
|
3144
|
+
nodeVersion: target.nodeVersion,
|
|
3145
|
+
arch: target.arch
|
|
3146
|
+
} });
|
|
3147
|
+
try {
|
|
3148
|
+
await fetchPrebuild(moduleDir, target);
|
|
3149
|
+
} catch (err) {
|
|
3150
|
+
options.logger.error("native prebuild fetch failed (validator will decide)", { meta: {
|
|
3151
|
+
pkg,
|
|
3152
|
+
error: errMsg(err)
|
|
3153
|
+
} });
|
|
3154
|
+
continue;
|
|
3155
|
+
}
|
|
3156
|
+
if (hasDotNode(moduleDir, 4)) {
|
|
3157
|
+
fetched.push(pkg);
|
|
3158
|
+
options.logger.info("native prebuild fetched", { meta: { pkg } });
|
|
3159
|
+
} else options.logger.error("native prebuild still missing after fetch", { meta: { pkg } });
|
|
3160
|
+
}
|
|
3161
|
+
return fetched;
|
|
3162
|
+
}
|
|
3163
|
+
//#endregion
|
|
3164
|
+
//#region src/kernel/deps/ensure-addon-natives.ts
|
|
3165
|
+
/**
|
|
3166
|
+
* `ensureAddonNativePrebuilds` — the POST-install native-prebuild ENSURE pass an
|
|
3167
|
+
* addon install runs on the INSTALLED addon dir AFTER `npm install`.
|
|
3168
|
+
*
|
|
3169
|
+
* Why this exists (Mac desktop first-boot FATAL, 2026-07-21): `installCopy`
|
|
3170
|
+
* copies `dist/`, plants prebuilt natives from the bundled addon copy
|
|
3171
|
+
* (`copyBundledNativeModules`), then `npm install`s the addon's runtime deps.
|
|
3172
|
+
* On a TOOLCHAIN-LESS host that `npm install` RE-CREATES the traditional native
|
|
3173
|
+
* `better-sqlite3` SOURCE-ONLY — clobbering the good `.node` the pre-install
|
|
3174
|
+
* copy just planted — and the runner later dies at addon init with a cryptic
|
|
3175
|
+
* "Could not locate the bindings file". The copy-BEFORE-install fix was
|
|
3176
|
+
* empirically insufficient: npm's own install in the copied dir replaces the
|
|
3177
|
+
* satisfied dep without its binary.
|
|
3178
|
+
*
|
|
3179
|
+
* This pass runs LAST, so it always sees the final on-disk state npm left, and
|
|
3180
|
+
* per referenced traditional native still lacking its compiled `.node`:
|
|
3181
|
+
* 1. bundled-copy first — if the bundled addon copy carries the matching
|
|
3182
|
+
* `.node` (same machine → same platform/arch/ABI), re-plant it DIRECTLY.
|
|
3183
|
+
* Cheapest + offline-safe; runs BEFORE any network fetch.
|
|
3184
|
+
* 2. fetch fallback — otherwise fetch the prebuild for the running ABI via the
|
|
3185
|
+
* shared `ensureNativePrebuilds` pointed at the addon dir.
|
|
3186
|
+
* 3. FAIL LOUD — if the native is STILL source-only after both, log a clear
|
|
3187
|
+
* error naming module + addon and THROW so the install fails at install
|
|
3188
|
+
* time rather than at addon init.
|
|
3189
|
+
*
|
|
3190
|
+
* N-API natives (`sharp`, `node-av`) ship their binaries as ABI-stable platform
|
|
3191
|
+
* SIBLING packages npm fetches without a compile step, so they are excluded from
|
|
3192
|
+
* the default set — a miss there is a broken install, not something a prebuild
|
|
3193
|
+
* re-fetch heals (same rationale as `ensureNativePrebuilds`).
|
|
3194
|
+
*/
|
|
3195
|
+
/**
|
|
3196
|
+
* Re-plant `<srcModuleDir>` → `<dstModuleDir>` when the source carries a `.node`.
|
|
3197
|
+
* Returns whether the destination ends up with a compiled binary.
|
|
3198
|
+
*/
|
|
3199
|
+
function replantFromBundled(srcModuleDir, dstModuleDir, hasNative) {
|
|
3200
|
+
if (!hasNative(srcModuleDir)) return false;
|
|
3201
|
+
try {
|
|
3202
|
+
fs$17.rmSync(dstModuleDir, {
|
|
3203
|
+
recursive: true,
|
|
3204
|
+
force: true
|
|
3205
|
+
});
|
|
3206
|
+
fs$17.mkdirSync(path$39.dirname(dstModuleDir), { recursive: true });
|
|
3207
|
+
fs$17.cpSync(srcModuleDir, dstModuleDir, {
|
|
3208
|
+
recursive: true,
|
|
3209
|
+
dereference: true
|
|
3210
|
+
});
|
|
3211
|
+
} catch {
|
|
3212
|
+
return false;
|
|
3213
|
+
}
|
|
3214
|
+
return hasNative(dstModuleDir);
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* Ensure every referenced traditional native in `<addonDir>/node_modules`
|
|
3218
|
+
* carries its compiled `.node` after the addon's `npm install`. Bundled-copy
|
|
3219
|
+
* first (offline), prebuild-fetch fallback, LOUD throw on a residual miss.
|
|
3220
|
+
*
|
|
3221
|
+
* SKIPS a native absent from the addon's own `node_modules` (it resolves via a
|
|
3222
|
+
* hoisted host copy — not this addon's concern). A NO-OP in docker / dev where
|
|
3223
|
+
* npm produced a working `.node` (already-present). Returns the per-native
|
|
3224
|
+
* outcomes for logging/tests.
|
|
3225
|
+
*
|
|
3226
|
+
* @throws if any required native is still source-only after copy + fetch.
|
|
3227
|
+
*/
|
|
3228
|
+
async function ensureAddonNativePrebuilds(addonDir, options) {
|
|
3229
|
+
const packages = options.packages ?? TRADITIONAL_NATIVE_PACKAGES;
|
|
3230
|
+
const hasNative = options.hasNative ?? ((dir) => hasDotNode(dir, 4));
|
|
3231
|
+
const nm = path$39.join(addonDir, "node_modules");
|
|
3232
|
+
const results = [];
|
|
3233
|
+
const missing = [];
|
|
3234
|
+
for (const pkg of packages) {
|
|
3235
|
+
const moduleDir = path$39.join(nm, pkg);
|
|
3236
|
+
if (!fs$17.existsSync(moduleDir)) continue;
|
|
3237
|
+
if (hasNative(moduleDir)) {
|
|
3238
|
+
results.push({
|
|
3239
|
+
pkg,
|
|
3240
|
+
outcome: "already-present"
|
|
3241
|
+
});
|
|
3242
|
+
continue;
|
|
3243
|
+
}
|
|
3244
|
+
if (options.bundledSourceDir) {
|
|
3245
|
+
const src = path$39.join(options.bundledSourceDir, "node_modules", pkg);
|
|
3246
|
+
if (replantFromBundled(src, moduleDir, hasNative)) {
|
|
3247
|
+
options.logger.info("addon native prebuild re-planted from bundled copy (offline)", { meta: {
|
|
3248
|
+
addon: options.addonName,
|
|
3249
|
+
pkg,
|
|
3250
|
+
src,
|
|
3251
|
+
moduleDir
|
|
3252
|
+
} });
|
|
3253
|
+
results.push({
|
|
3254
|
+
pkg,
|
|
3255
|
+
outcome: "copied"
|
|
3256
|
+
});
|
|
3257
|
+
continue;
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
if ((await ensureNativePrebuilds(addonDir, {
|
|
3261
|
+
logger: options.logger,
|
|
3262
|
+
packages: [pkg],
|
|
3263
|
+
...options.target ? { target: options.target } : {},
|
|
3264
|
+
...options.fetchPrebuild ? { fetchPrebuild: options.fetchPrebuild } : {}
|
|
3265
|
+
})).includes(pkg)) {
|
|
3266
|
+
results.push({
|
|
3267
|
+
pkg,
|
|
3268
|
+
outcome: "fetched"
|
|
3269
|
+
});
|
|
3270
|
+
continue;
|
|
3271
|
+
}
|
|
3272
|
+
options.logger.error("addon native prebuild MISSING after ensure — module has no compiled .node", { meta: {
|
|
3273
|
+
addon: options.addonName,
|
|
3274
|
+
pkg,
|
|
3275
|
+
moduleDir
|
|
3276
|
+
} });
|
|
3277
|
+
results.push({
|
|
3278
|
+
pkg,
|
|
3279
|
+
outcome: "missing"
|
|
3280
|
+
});
|
|
3281
|
+
missing.push(pkg);
|
|
3282
|
+
}
|
|
3283
|
+
if (missing.length > 0) throw new Error(`${options.addonName} — required native module(s) missing a compiled .node after install (bundled-copy + prebuild-fetch both failed): ${missing.join(", ")}`);
|
|
3284
|
+
return results;
|
|
3285
|
+
}
|
|
3286
|
+
//#endregion
|
|
3287
|
+
//#region src/kernel/addon-installer.ts
|
|
3288
|
+
var execFileAsync = promisify(execFile);
|
|
3027
3289
|
function parseInstallSource(value) {
|
|
3028
3290
|
switch (value) {
|
|
3029
3291
|
case "npm":
|
|
@@ -3332,6 +3594,11 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3332
3594
|
} catch (err) {
|
|
3333
3595
|
this.logger.warn(`${packageName} — native deps install failed (continuing)`, { meta: { error: errMsg$1(err) } });
|
|
3334
3596
|
}
|
|
3597
|
+
await ensureAddonNativePrebuilds(targetDir, {
|
|
3598
|
+
addonName: packageName,
|
|
3599
|
+
logger: this.logger,
|
|
3600
|
+
bundledSourceDir: sourceDir
|
|
3601
|
+
});
|
|
3335
3602
|
this.logger.info(`${packageName} — copied from ${sourceDir}`);
|
|
3336
3603
|
return {
|
|
3337
3604
|
name: pkgData.name,
|
|
@@ -3481,7 +3748,7 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3481
3748
|
const stagingDir = path$39.join(stagingRoot, `install-${process.pid}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
|
|
3482
3749
|
ensureDir(stagingDir);
|
|
3483
3750
|
try {
|
|
3484
|
-
await execFileAsync
|
|
3751
|
+
await execFileAsync("tar", [
|
|
3485
3752
|
"-xzf",
|
|
3486
3753
|
tgzPath,
|
|
3487
3754
|
"-C",
|
|
@@ -3518,6 +3785,10 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3518
3785
|
} catch (nativeErr) {
|
|
3519
3786
|
throw new Error(`${pkgView.name} — native deps install failed: ${errMsg$1(nativeErr)}`, { cause: nativeErr });
|
|
3520
3787
|
}
|
|
3788
|
+
await ensureAddonNativePrebuilds(pkgDir, {
|
|
3789
|
+
addonName: pkgView.name,
|
|
3790
|
+
logger: this.logger
|
|
3791
|
+
});
|
|
3521
3792
|
await this.evictInstallDir(targetDir);
|
|
3522
3793
|
ensureDir(path$39.dirname(targetDir));
|
|
3523
3794
|
await fs$17.promises.rename(pkgDir, targetDir);
|
|
@@ -93390,143 +93661,4 @@ var LifecycleJobEngine = class {
|
|
|
93390
93661
|
}
|
|
93391
93662
|
};
|
|
93392
93663
|
//#endregion
|
|
93393
|
-
|
|
93394
|
-
/**
|
|
93395
|
-
* `ensureNativePrebuilds` — the post-install native-prebuild ENSURE step the
|
|
93396
|
-
* shared `RootUpdateService` runs while staging a `@camstack/server` closure,
|
|
93397
|
-
* BEFORE its `findMissingNativePrebuilds` validator (in `@camstack/node-root`)
|
|
93398
|
-
* decides whether to arm the swap.
|
|
93399
|
-
*
|
|
93400
|
-
* Why this exists (Mac desktop incident, 2026-07-21): a runtime
|
|
93401
|
-
* `applyServerUpdate` on a packaged desktop stages the closure with
|
|
93402
|
-
* `npm install @camstack/server@X` under the app's BUNDLED bare Node. The one
|
|
93403
|
-
* TRADITIONAL (non-N-API) native, `better-sqlite3`, resolves its `.node` via
|
|
93404
|
-
* its `install` script `prebuild-install || node-gyp rebuild`. On a user's Mac
|
|
93405
|
-
* with no build toolchain (and where the auto `prebuild-install` does not
|
|
93406
|
-
* target the bundled Node's ABI/arch the way the DMG build's EXPLICIT
|
|
93407
|
-
* `prebuild-install --runtime node --target <bundled> --arch <arch>` call does)
|
|
93408
|
-
* that leaves the module SOURCE-ONLY → the closure has no `better-sqlite3`
|
|
93409
|
-
* `.node` → the validator refuses the swap ("staged closure missing native
|
|
93410
|
-
* prebuilds: better-sqlite3"). The docker image never hits this: its Node ABI
|
|
93411
|
-
* has a published prebuild the auto path fetches cleanly, and it carries a
|
|
93412
|
-
* build toolchain fallback.
|
|
93413
|
-
*
|
|
93414
|
-
* This mirrors the DMG build's explicit re-fetch (`scripts/stage-deps.mjs`)
|
|
93415
|
-
* into the runtime staging path: for each traditional native that is present
|
|
93416
|
-
* in the closure but missing its compiled `.node`, fetch the prebuild for the
|
|
93417
|
-
* RUNNING Node ABI/arch (the same Node that will load the swapped closure).
|
|
93418
|
-
*
|
|
93419
|
-
* N-API natives (`sharp`, `node-av`) are ABI-stable and ship their binaries as
|
|
93420
|
-
* platform SIBLING packages that npm fetches reliably with no compile step, so
|
|
93421
|
-
* they are not in scope here — a genuine miss there is a broken install the
|
|
93422
|
-
* validator should still reject, not something a prebuild re-fetch can heal.
|
|
93423
|
-
*
|
|
93424
|
-
* Injected into the update service from the construction site so the zero-dep
|
|
93425
|
-
* `@camstack/node-root` layer never depends on npm/@camstack/system, and the
|
|
93426
|
-
* fetch is unit-testable with a fake fetcher.
|
|
93427
|
-
*/
|
|
93428
|
-
var execFileAsync = promisify(execFile);
|
|
93429
|
-
/** Bounded recursive `.node` scan depth inside a module dir. */
|
|
93430
|
-
var NATIVE_SCAN_DEPTH = 4;
|
|
93431
|
-
/** Timeout for a single `prebuild-install` fetch. */
|
|
93432
|
-
var PREBUILD_FETCH_TIMEOUT_MS = 12e4;
|
|
93433
|
-
/**
|
|
93434
|
-
* Traditional (compile-or-prebuild) natives an installed closure must carry a
|
|
93435
|
-
* `.node` for. Only these are ENSURE-able by a prebuild re-fetch; N-API natives
|
|
93436
|
-
* are excluded (see file header).
|
|
93437
|
-
*/
|
|
93438
|
-
var TRADITIONAL_NATIVE_PACKAGES = ["better-sqlite3"];
|
|
93439
|
-
/** Bounded recursive scan for ANY `*.node` file under `dir`. */
|
|
93440
|
-
function hasDotNode(dir, maxDepth) {
|
|
93441
|
-
let entries;
|
|
93442
|
-
try {
|
|
93443
|
-
entries = fs$17.readdirSync(dir, { withFileTypes: true });
|
|
93444
|
-
} catch {
|
|
93445
|
-
return false;
|
|
93446
|
-
}
|
|
93447
|
-
for (const entry of entries) if (entry.isFile() && entry.name.endsWith(".node")) return true;
|
|
93448
|
-
if (maxDepth <= 0) return false;
|
|
93449
|
-
for (const entry of entries) if (entry.isDirectory() && hasDotNode(path$39.join(dir, entry.name), maxDepth - 1)) return true;
|
|
93450
|
-
return false;
|
|
93451
|
-
}
|
|
93452
|
-
/** Locate the bundled `prebuild-install` bin.js inside the staged closure. */
|
|
93453
|
-
function resolvePrebuildInstallBin(closureDir, moduleDir) {
|
|
93454
|
-
return [path$39.join(moduleDir, "node_modules", "prebuild-install", "bin.js"), path$39.join(closureDir, "node_modules", "prebuild-install", "bin.js")].find((c) => fs$17.existsSync(c)) ?? null;
|
|
93455
|
-
}
|
|
93456
|
-
/**
|
|
93457
|
-
* Default fetcher: run the closure's own `prebuild-install` under the CURRENT
|
|
93458
|
-
* Node (`process.execPath`) — no reliance on `npx`/`npm` on PATH (a packaged
|
|
93459
|
-
* app ships a bare Node). Explicitly targets the running ABI/arch, mirroring
|
|
93460
|
-
* `scripts/stage-deps.mjs`.
|
|
93461
|
-
*/
|
|
93462
|
-
function makeDefaultFetch(closureDir, logger) {
|
|
93463
|
-
return async (moduleDir, target) => {
|
|
93464
|
-
const bin = resolvePrebuildInstallBin(closureDir, moduleDir);
|
|
93465
|
-
if (bin === null) throw new Error(`prebuild-install not found in the staged closure for ${moduleDir}`);
|
|
93466
|
-
logger.info("fetching native prebuild via prebuild-install", { meta: {
|
|
93467
|
-
moduleDir,
|
|
93468
|
-
bin,
|
|
93469
|
-
nodeVersion: target.nodeVersion,
|
|
93470
|
-
arch: target.arch
|
|
93471
|
-
} });
|
|
93472
|
-
await execFileAsync(process.execPath, [
|
|
93473
|
-
bin,
|
|
93474
|
-
"--runtime",
|
|
93475
|
-
"node",
|
|
93476
|
-
"--target",
|
|
93477
|
-
target.nodeVersion,
|
|
93478
|
-
"--arch",
|
|
93479
|
-
target.arch
|
|
93480
|
-
], {
|
|
93481
|
-
cwd: moduleDir,
|
|
93482
|
-
timeout: PREBUILD_FETCH_TIMEOUT_MS
|
|
93483
|
-
});
|
|
93484
|
-
};
|
|
93485
|
-
}
|
|
93486
|
-
/**
|
|
93487
|
-
* Ensure every traditional native in `<closureDir>/node_modules` carries its
|
|
93488
|
-
* compiled `.node`, fetching the prebuild for the running ABI when missing.
|
|
93489
|
-
*
|
|
93490
|
-
* A NO-OP when nothing is missing (docker / tests) and when a native package is
|
|
93491
|
-
* absent from the closure. Never throws for a single package's fetch failure —
|
|
93492
|
-
* it logs and continues so the downstream `findMissingNativePrebuilds`
|
|
93493
|
-
* validator makes the final swap/abort decision. Returns the packages whose
|
|
93494
|
-
* prebuild was successfully fetched (for logging/tests).
|
|
93495
|
-
*/
|
|
93496
|
-
async function ensureNativePrebuilds(closureDir, options) {
|
|
93497
|
-
const nm = path$39.join(closureDir, "node_modules");
|
|
93498
|
-
const target = options.target ?? {
|
|
93499
|
-
nodeVersion: process.versions.node,
|
|
93500
|
-
arch: process.arch
|
|
93501
|
-
};
|
|
93502
|
-
const packages = options.packages ?? TRADITIONAL_NATIVE_PACKAGES;
|
|
93503
|
-
const fetchPrebuild = options.fetchPrebuild ?? makeDefaultFetch(closureDir, options.logger);
|
|
93504
|
-
const fetched = [];
|
|
93505
|
-
for (const pkg of packages) {
|
|
93506
|
-
const moduleDir = path$39.join(nm, pkg);
|
|
93507
|
-
if (!fs$17.existsSync(moduleDir)) continue;
|
|
93508
|
-
if (hasDotNode(moduleDir, NATIVE_SCAN_DEPTH)) continue;
|
|
93509
|
-
options.logger.warn("staged closure native prebuild missing — fetching for running ABI", { meta: {
|
|
93510
|
-
pkg,
|
|
93511
|
-
moduleDir,
|
|
93512
|
-
nodeVersion: target.nodeVersion,
|
|
93513
|
-
arch: target.arch
|
|
93514
|
-
} });
|
|
93515
|
-
try {
|
|
93516
|
-
await fetchPrebuild(moduleDir, target);
|
|
93517
|
-
} catch (err) {
|
|
93518
|
-
options.logger.error("native prebuild fetch failed (validator will decide)", { meta: {
|
|
93519
|
-
pkg,
|
|
93520
|
-
error: errMsg(err)
|
|
93521
|
-
} });
|
|
93522
|
-
continue;
|
|
93523
|
-
}
|
|
93524
|
-
if (hasDotNode(moduleDir, NATIVE_SCAN_DEPTH)) {
|
|
93525
|
-
fetched.push(pkg);
|
|
93526
|
-
options.logger.info("native prebuild fetched", { meta: { pkg } });
|
|
93527
|
-
} else options.logger.error("native prebuild still missing after fetch", { meta: { pkg } });
|
|
93528
|
-
}
|
|
93529
|
-
return fetched;
|
|
93530
|
-
}
|
|
93531
|
-
//#endregion
|
|
93532
|
-
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, RESTART_MARKER_FILE, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, addonSettingsCapability, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyCapRoute, clearPendingRestart, clusterEventTopic, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, expandCapMethods, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runNpm, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, serializeTypedArrays, setHubConnected, setNodeEventInterest, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
|
93664
|
+
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, RESTART_MARKER_FILE, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, addonSettingsCapability, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyCapRoute, clearPendingRestart, clusterEventTopic, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, expandCapMethods, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runNpm, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, serializeTypedArrays, setHubConnected, setNodeEventInterest, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { IScopedLogger } from '@camstack/types';
|
|
2
|
+
import { PrebuildFetchFn, PrebuildTarget } from './ensure-native-prebuilds.js';
|
|
3
|
+
/** Per-native outcome of the ensure pass. */
|
|
4
|
+
export type NativeEnsureOutcome = 'already-present' | 'copied' | 'fetched' | 'missing';
|
|
5
|
+
export interface NativeEnsureResult {
|
|
6
|
+
readonly pkg: string;
|
|
7
|
+
readonly outcome: NativeEnsureOutcome;
|
|
8
|
+
}
|
|
9
|
+
export interface EnsureAddonNativePrebuildsOptions {
|
|
10
|
+
/** Addon package name — for loud error messages naming module + addon. */
|
|
11
|
+
readonly addonName: string;
|
|
12
|
+
readonly logger: IScopedLogger;
|
|
13
|
+
/**
|
|
14
|
+
* Bundled addon copy dir (the `installCopy` `sourceDir` / a
|
|
15
|
+
* `CAMSTACK_BUNDLED_ADDONS_DIR/<addon>` copy) whose own
|
|
16
|
+
* `node_modules/<pkg>` may carry a prebuilt `.node` for THIS machine.
|
|
17
|
+
* Copied in directly before any network fetch. Absent on the npm/tgz path.
|
|
18
|
+
*/
|
|
19
|
+
readonly bundledSourceDir?: string;
|
|
20
|
+
/** Running ABI/arch the fetched prebuild must match. Defaults to the process. */
|
|
21
|
+
readonly target?: PrebuildTarget;
|
|
22
|
+
/** Native package set. Defaults to `TRADITIONAL_NATIVE_PACKAGES`. */
|
|
23
|
+
readonly packages?: readonly string[];
|
|
24
|
+
/** Override the fetcher (tests). Production uses the bundled `prebuild-install`. */
|
|
25
|
+
readonly fetchPrebuild?: PrebuildFetchFn;
|
|
26
|
+
/** Override the `.node` presence scan (tests). */
|
|
27
|
+
readonly hasNative?: (moduleDir: string) => boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Ensure every referenced traditional native in `<addonDir>/node_modules`
|
|
31
|
+
* carries its compiled `.node` after the addon's `npm install`. Bundled-copy
|
|
32
|
+
* first (offline), prebuild-fetch fallback, LOUD throw on a residual miss.
|
|
33
|
+
*
|
|
34
|
+
* SKIPS a native absent from the addon's own `node_modules` (it resolves via a
|
|
35
|
+
* hoisted host copy — not this addon's concern). A NO-OP in docker / dev where
|
|
36
|
+
* npm produced a working `.node` (already-present). Returns the per-native
|
|
37
|
+
* outcomes for logging/tests.
|
|
38
|
+
*
|
|
39
|
+
* @throws if any required native is still source-only after copy + fetch.
|
|
40
|
+
*/
|
|
41
|
+
export declare function ensureAddonNativePrebuilds(addonDir: string, options: EnsureAddonNativePrebuildsOptions): Promise<readonly NativeEnsureResult[]>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { IScopedLogger } from '@camstack/types';
|
|
2
|
+
/** Bounded recursive `.node` scan depth inside a module dir. */
|
|
3
|
+
export declare const NATIVE_SCAN_DEPTH = 4;
|
|
2
4
|
/**
|
|
3
5
|
* Traditional (compile-or-prebuild) natives an installed closure must carry a
|
|
4
6
|
* `.node` for. Only these are ENSURE-able by a prebuild re-fetch; N-API natives
|
|
@@ -23,6 +25,8 @@ export interface EnsureNativePrebuildsOptions {
|
|
|
23
25
|
/** Override the fetcher (tests). Production uses the bundled `prebuild-install`. */
|
|
24
26
|
readonly fetchPrebuild?: PrebuildFetchFn;
|
|
25
27
|
}
|
|
28
|
+
/** Bounded recursive scan for ANY `*.node` file under `dir`. */
|
|
29
|
+
export declare function hasDotNode(dir: string, maxDepth: number): boolean;
|
|
26
30
|
/**
|
|
27
31
|
* Ensure every traditional native in `<closureDir>/node_modules` carries its
|
|
28
32
|
* compiled `.node`, fetching the prebuild for the running ABI when missing.
|