@camstack/system 1.2.2 → 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/addon-runner.js +2 -2
- package/dist/addon-runner.mjs +2 -2
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +6 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +6 -2
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-DTTTjPCL.mjs → dist-72yufD8v.mjs} +2 -1
- package/dist/{dist-CkdNC8US.js → dist-CQZxGJ8u.js} +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +289 -12
- package/dist/index.mjs +286 -14
- package/dist/kernel/deps/ensure-addon-natives.d.ts +41 -0
- package/dist/kernel/deps/ensure-native-prebuilds.d.ts +40 -0
- package/dist/kernel/deps/manifest-native-deps.d.ts +17 -0
- package/dist/{manifest-python-deps-s_-uULmp.mjs → manifest-python-deps-6__QF75g.mjs} +88 -2
- package/dist/{manifest-python-deps-DRq5MreI.js → manifest-python-deps-Cd7ocUBi.js} +93 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { At as EventCategory$1, Dt as resolveCapMount, Et as readinessKey, F as kebabToCamel, I as lifecycleJobSchema, K as procedureAuthKey, M as isCollectionArrayMethod, N as isObjectInput, O as extractNestedAddonId, Ot as scopeKey, P as isVoidInput, Tt as parseJsonUnknown$1, U as objectInputDeclaresAddonId, _t as asString$1, ct as DEVICE_STATUS_METHOD, ft as ReadinessRegistry, gt as asNumber, ht as asJsonObject$1, it as errMsg$1, j as isArrayOutputSchema, o as METHOD_ACCESS_MAP, ot as DATAPLANE_SECRET_HEADER$1, p as addonSettingsCapability, pt as ReadinessTimeoutError, s as RUNTIME_DEFAULTS, st as DEVICE_SETTINGS_CONTRIBUTION_METHODS, t as ALL_CAPABILITY_DEFINITIONS, vt as createEvent, wt as parseJsonObject, xt as expandCapMethods, yt as emitDownForOwnedCaps, z as looseSchema } from "./dist-
|
|
1
|
+
import { At as EventCategory$1, Dt as resolveCapMount, Et as readinessKey, F as kebabToCamel, I as lifecycleJobSchema, K as procedureAuthKey, M as isCollectionArrayMethod, N as isObjectInput, O as extractNestedAddonId, Ot as scopeKey, P as isVoidInput, Tt as parseJsonUnknown$1, U as objectInputDeclaresAddonId, _t as asString$1, ct as DEVICE_STATUS_METHOD, ft as ReadinessRegistry, gt as asNumber, ht as asJsonObject$1, it as errMsg$1, j as isArrayOutputSchema, o as METHOD_ACCESS_MAP, ot as DATAPLANE_SECRET_HEADER$1, p as addonSettingsCapability, pt as ReadinessTimeoutError, s as RUNTIME_DEFAULTS, st as DEVICE_SETTINGS_CONTRIBUTION_METHODS, t as ALL_CAPABILITY_DEFINITIONS, vt as createEvent, wt as parseJsonObject, xt as expandCapMethods, yt as emitDownForOwnedCaps, z as looseSchema } from "./dist-72yufD8v.mjs";
|
|
2
2
|
import { a as downloadModel, c as getModelFilePath, d as contentTypeFor, f as createAuthenticatedFileServer, h as resolveFilePath, i as downloadFile, l as isModelDownloaded, m as parseTokenizedUrl, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, s as fetchJson, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-Cp9f4dk6.mjs";
|
|
3
|
-
import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as
|
|
3
|
+
import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-6__QF75g.mjs";
|
|
4
4
|
import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-Le3l0qS2.mjs";
|
|
5
5
|
import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
|
|
6
6
|
import { SqliteSettingsAddon, t as SqliteSettingsBackend } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
|
|
@@ -29,7 +29,7 @@ import { accessSync, constants, existsSync, mkdirSync, readFileSync } from "node
|
|
|
29
29
|
import * as path$39 from "node:path";
|
|
30
30
|
import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
31
31
|
import { X509Certificate, createHash, randomUUID, timingSafeEqual } from "node:crypto";
|
|
32
|
-
import { asJsonObject, parseJsonUnknown } from "@camstack/types/addon";
|
|
32
|
+
import { asJsonObject, errMsg, parseJsonUnknown } from "@camstack/types/addon";
|
|
33
33
|
import { execFile, spawn } from "node:child_process";
|
|
34
34
|
import * as util$10 from "node:util";
|
|
35
35
|
import { promisify } from "node:util";
|
|
@@ -176,7 +176,7 @@ function proxyToUpstream(opts) {
|
|
|
176
176
|
}
|
|
177
177
|
//#endregion
|
|
178
178
|
//#region src/python/python-env-manager.ts
|
|
179
|
-
var execFileAsync$
|
|
179
|
+
var execFileAsync$4 = promisify(execFile);
|
|
180
180
|
var PythonEnvManager = class {
|
|
181
181
|
venvPath;
|
|
182
182
|
cachedProbe = null;
|
|
@@ -186,12 +186,12 @@ var PythonEnvManager = class {
|
|
|
186
186
|
async probe() {
|
|
187
187
|
if (this.cachedProbe) return this.cachedProbe;
|
|
188
188
|
for (const cmd of ["python3", "python"]) try {
|
|
189
|
-
const { stdout } = await execFileAsync$
|
|
189
|
+
const { stdout } = await execFileAsync$4(cmd, ["--version"]);
|
|
190
190
|
const version = stdout.trim().replace("Python ", "");
|
|
191
191
|
const major = parseInt(version.split(".")[0] ?? "0", 10);
|
|
192
192
|
const minor = parseInt(version.split(".")[1] ?? "0", 10);
|
|
193
193
|
if (major < 3 || major === 3 && minor < 10) continue;
|
|
194
|
-
const { stdout: pathOut } = await execFileAsync$
|
|
194
|
+
const { stdout: pathOut } = await execFileAsync$4(cmd, ["-c", "import sys; print(sys.executable)"]);
|
|
195
195
|
this.cachedProbe = {
|
|
196
196
|
available: true,
|
|
197
197
|
version,
|
|
@@ -207,13 +207,13 @@ var PythonEnvManager = class {
|
|
|
207
207
|
async ensure(options) {
|
|
208
208
|
const probe = await this.probe();
|
|
209
209
|
if (!probe.available || !probe.path) throw new Error("Python 3.10+ is required but not found on this system");
|
|
210
|
-
if (!fs$17.existsSync(path$39.join(this.venvPath, "bin", "python"))) await execFileAsync$
|
|
210
|
+
if (!fs$17.existsSync(path$39.join(this.venvPath, "bin", "python"))) await execFileAsync$4(probe.path, [
|
|
211
211
|
"-m",
|
|
212
212
|
"venv",
|
|
213
213
|
this.venvPath
|
|
214
214
|
]);
|
|
215
215
|
const venvPython = path$39.join(this.venvPath, "bin", "python");
|
|
216
|
-
if (options.packages.length > 0) await execFileAsync$
|
|
216
|
+
if (options.packages.length > 0) await execFileAsync$4(venvPython, [
|
|
217
217
|
"-m",
|
|
218
218
|
"pip",
|
|
219
219
|
"install",
|
|
@@ -1684,7 +1684,7 @@ function matchPath(pattern, path) {
|
|
|
1684
1684
|
}
|
|
1685
1685
|
//#endregion
|
|
1686
1686
|
//#region src/tls/cert-manager.ts
|
|
1687
|
-
var execFileAsync$
|
|
1687
|
+
var execFileAsync$3 = promisify(execFile);
|
|
1688
1688
|
/**
|
|
1689
1689
|
* Ensure a self-signed TLS certificate exists in the given directory.
|
|
1690
1690
|
* Generates one if missing. Returns paths to cert and key files.
|
|
@@ -1721,7 +1721,7 @@ async function ensureTlsCert(dataDir, options) {
|
|
|
1721
1721
|
for (const dns of sanDns) sanParts.push(`DNS:${dns}`);
|
|
1722
1722
|
for (const ip of sanIps) sanParts.push(`IP:${ip}`);
|
|
1723
1723
|
const sanString = sanParts.join(",");
|
|
1724
|
-
await execFileAsync$
|
|
1724
|
+
await execFileAsync$3("openssl", [
|
|
1725
1725
|
"req",
|
|
1726
1726
|
"-x509",
|
|
1727
1727
|
"-newkey",
|
|
@@ -2589,7 +2589,7 @@ var AddonEngineManager = class {
|
|
|
2589
2589
|
};
|
|
2590
2590
|
//#endregion
|
|
2591
2591
|
//#region src/kernel/fs-utils.ts
|
|
2592
|
-
var execFileAsync$
|
|
2592
|
+
var execFileAsync$2 = promisify(execFile);
|
|
2593
2593
|
/**
|
|
2594
2594
|
* Ensure a directory exists (recursive).
|
|
2595
2595
|
* Single source of truth — replaces scattered mkdirSync calls.
|
|
@@ -2773,7 +2773,7 @@ function ensureLibraryBuilt(packageName, packagesDir) {
|
|
|
2773
2773
|
async function installPackageFromNpm(packageName, targetDir) {
|
|
2774
2774
|
const tmpDir = await fs$17.promises.mkdtemp(path$39.join(os$18.tmpdir(), "camstack-install-"));
|
|
2775
2775
|
try {
|
|
2776
|
-
const { stdout } = await execFileAsync$
|
|
2776
|
+
const { stdout } = await execFileAsync$2("npm", [
|
|
2777
2777
|
"pack",
|
|
2778
2778
|
packageName,
|
|
2779
2779
|
"--pack-destination",
|
|
@@ -2787,7 +2787,7 @@ async function installPackageFromNpm(packageName, targetDir) {
|
|
|
2787
2787
|
const tgzPath = path$39.join(tmpDir, tgzFilename);
|
|
2788
2788
|
const extractDir = path$39.join(tmpDir, "extracted");
|
|
2789
2789
|
ensureDir(extractDir);
|
|
2790
|
-
await execFileAsync$
|
|
2790
|
+
await execFileAsync$2("tar", [
|
|
2791
2791
|
"-xzf",
|
|
2792
2792
|
tgzPath,
|
|
2793
2793
|
"-C",
|
|
@@ -3022,6 +3022,268 @@ var AddonManifest = class {
|
|
|
3022
3022
|
}
|
|
3023
3023
|
};
|
|
3024
3024
|
//#endregion
|
|
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
|
+
*/
|
|
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
|
|
3025
3287
|
//#region src/kernel/addon-installer.ts
|
|
3026
3288
|
var execFileAsync = promisify(execFile);
|
|
3027
3289
|
function parseInstallSource(value) {
|
|
@@ -3310,6 +3572,7 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3310
3572
|
await fs$17.promises.writeFile(path$39.join(targetDir, "package.json"), JSON.stringify(stripBundledDeps(pkgData), null, 2));
|
|
3311
3573
|
await copyDirRecursive(distDir, path$39.join(targetDir, "dist"));
|
|
3312
3574
|
await copyExtraFileDirs(pkgData, sourceDir, targetDir);
|
|
3575
|
+
copyBundledNativeModules(sourceDir, targetDir, this.logger);
|
|
3313
3576
|
await fs$17.promises.writeFile(path$39.join(targetDir, ".install-source"), "local");
|
|
3314
3577
|
const localPkgVersion = asString$1(pkgData.version, "0.0.0");
|
|
3315
3578
|
this.manifest.upsert(packageName, {
|
|
@@ -3331,6 +3594,11 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3331
3594
|
} catch (err) {
|
|
3332
3595
|
this.logger.warn(`${packageName} — native deps install failed (continuing)`, { meta: { error: errMsg$1(err) } });
|
|
3333
3596
|
}
|
|
3597
|
+
await ensureAddonNativePrebuilds(targetDir, {
|
|
3598
|
+
addonName: packageName,
|
|
3599
|
+
logger: this.logger,
|
|
3600
|
+
bundledSourceDir: sourceDir
|
|
3601
|
+
});
|
|
3334
3602
|
this.logger.info(`${packageName} — copied from ${sourceDir}`);
|
|
3335
3603
|
return {
|
|
3336
3604
|
name: pkgData.name,
|
|
@@ -3517,6 +3785,10 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3517
3785
|
} catch (nativeErr) {
|
|
3518
3786
|
throw new Error(`${pkgView.name} — native deps install failed: ${errMsg$1(nativeErr)}`, { cause: nativeErr });
|
|
3519
3787
|
}
|
|
3788
|
+
await ensureAddonNativePrebuilds(pkgDir, {
|
|
3789
|
+
addonName: pkgView.name,
|
|
3790
|
+
logger: this.logger
|
|
3791
|
+
});
|
|
3520
3792
|
await this.evictInstallDir(targetDir);
|
|
3521
3793
|
ensureDir(path$39.dirname(targetDir));
|
|
3522
3794
|
await fs$17.promises.rename(pkgDir, targetDir);
|
|
@@ -93389,4 +93661,4 @@ var LifecycleJobEngine = class {
|
|
|
93389
93661
|
}
|
|
93390
93662
|
};
|
|
93391
93663
|
//#endregion
|
|
93392
|
-
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, 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, 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[]>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { IScopedLogger } from '@camstack/types';
|
|
2
|
+
/** Bounded recursive `.node` scan depth inside a module dir. */
|
|
3
|
+
export declare const NATIVE_SCAN_DEPTH = 4;
|
|
4
|
+
/**
|
|
5
|
+
* Traditional (compile-or-prebuild) natives an installed closure must carry a
|
|
6
|
+
* `.node` for. Only these are ENSURE-able by a prebuild re-fetch; N-API natives
|
|
7
|
+
* are excluded (see file header).
|
|
8
|
+
*/
|
|
9
|
+
export declare const TRADITIONAL_NATIVE_PACKAGES: readonly string[];
|
|
10
|
+
/** The running Node ABI/arch the fetched prebuild must match. */
|
|
11
|
+
export interface PrebuildTarget {
|
|
12
|
+
/** Node version, e.g. `process.versions.node` (`24.16.0`). */
|
|
13
|
+
readonly nodeVersion: string;
|
|
14
|
+
/** CPU arch, e.g. `process.arch` (`arm64` | `x64`). */
|
|
15
|
+
readonly arch: string;
|
|
16
|
+
}
|
|
17
|
+
/** Fetch a `<moduleDir>`'s prebuild for `target`. Test seam. */
|
|
18
|
+
export type PrebuildFetchFn = (moduleDir: string, target: PrebuildTarget) => Promise<void>;
|
|
19
|
+
export interface EnsureNativePrebuildsOptions {
|
|
20
|
+
readonly logger: IScopedLogger;
|
|
21
|
+
/** Defaults to the running process (`process.versions.node` / `process.arch`). */
|
|
22
|
+
readonly target?: PrebuildTarget;
|
|
23
|
+
/** Override the native package set (tests). */
|
|
24
|
+
readonly packages?: readonly string[];
|
|
25
|
+
/** Override the fetcher (tests). Production uses the bundled `prebuild-install`. */
|
|
26
|
+
readonly fetchPrebuild?: PrebuildFetchFn;
|
|
27
|
+
}
|
|
28
|
+
/** Bounded recursive scan for ANY `*.node` file under `dir`. */
|
|
29
|
+
export declare function hasDotNode(dir: string, maxDepth: number): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Ensure every traditional native in `<closureDir>/node_modules` carries its
|
|
32
|
+
* compiled `.node`, fetching the prebuild for the running ABI when missing.
|
|
33
|
+
*
|
|
34
|
+
* A NO-OP when nothing is missing (docker / tests) and when a native package is
|
|
35
|
+
* absent from the closure. Never throws for a single package's fetch failure —
|
|
36
|
+
* it logs and continues so the downstream `findMissingNativePrebuilds`
|
|
37
|
+
* validator makes the final swap/abort decision. Returns the packages whose
|
|
38
|
+
* prebuild was successfully fetched (for logging/tests).
|
|
39
|
+
*/
|
|
40
|
+
export declare function ensureNativePrebuilds(closureDir: string, options: EnsureNativePrebuildsOptions): Promise<readonly string[]>;
|
|
@@ -23,3 +23,20 @@ import { IScopedLogger } from '@camstack/types';
|
|
|
23
23
|
* otherwise).
|
|
24
24
|
*/
|
|
25
25
|
export declare function installManifestNativeDeps(addonDir: string, pkgRaw: Record<string, unknown>, logger: IScopedLogger, registry?: string, npmCacheDir?: string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Propagate PREBUILT native modules from a bundled addon copy's own
|
|
28
|
+
* `node_modules` (the packaged-desktop 'local' source at
|
|
29
|
+
* `<resources>/addons/<addon>`) into the freshly-copied target
|
|
30
|
+
* (`<data>/addons/@camstack/<addon>`), BEFORE the target's `npm install` runs.
|
|
31
|
+
*
|
|
32
|
+
* Why (Mac desktop first-boot FATAL, 2026-07-21): `installCopy` copies only
|
|
33
|
+
* `dist/` then `npm install`s the addon's runtime deps. On a toolchain-less Mac
|
|
34
|
+
* that install leaves the traditional native `better-sqlite3` SOURCE-ONLY in
|
|
35
|
+
* `<addon>/node_modules` — a broken copy that then SHADOWS the good hoisted
|
|
36
|
+
* `.node` NODE_PATH would otherwise resolve, crashing the runner with "Could
|
|
37
|
+
* not locate the bindings file". Seeding the prebuilt copy here makes the npm
|
|
38
|
+
* install treat the native as satisfied (no re-run of `prebuild-install`), so
|
|
39
|
+
* the good `.node` stays in place. A no-op in dev / docker where the source
|
|
40
|
+
* carries no bundled natives.
|
|
41
|
+
*/
|
|
42
|
+
export declare function copyBundledNativeModules(sourceDir: string, targetDir: string, logger: IScopedLogger): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { q as readNodePin } from "./dist-
|
|
1
|
+
import { q as readNodePin } from "./dist-72yufD8v.mjs";
|
|
2
2
|
import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import * as fs from "node:fs";
|
|
@@ -437,6 +437,92 @@ function copyPrebuiltNativeDep(name, addonDir, logger) {
|
|
|
437
437
|
}
|
|
438
438
|
return false;
|
|
439
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Native packages the packaged desktop ships PREBUILT inside each bundled addon
|
|
442
|
+
* copy (`scripts/stage-deps.mjs` plants them), keyed to the platform SIBLING
|
|
443
|
+
* scopes that hold the N-API binary. The traditional `better-sqlite3` carries
|
|
444
|
+
* its `.node` in its own dir; `sharp`/`node-av` carry it in a `@img`/`@seydx`
|
|
445
|
+
* sibling.
|
|
446
|
+
*/
|
|
447
|
+
var BUNDLED_NATIVE_SIBLING_SCOPES = {
|
|
448
|
+
"better-sqlite3": [],
|
|
449
|
+
sharp: ["@img"],
|
|
450
|
+
"node-av": ["@seydx"]
|
|
451
|
+
};
|
|
452
|
+
/** Bounded recursive scan for ANY `*.node` under `dir`. */
|
|
453
|
+
function hasAnyDotNode(dir, maxDepth) {
|
|
454
|
+
let entries;
|
|
455
|
+
try {
|
|
456
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
457
|
+
} catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
for (const entry of entries) if (entry.isFile() && entry.name.endsWith(".node")) return true;
|
|
461
|
+
if (maxDepth <= 0) return false;
|
|
462
|
+
for (const entry of entries) if (entry.isDirectory() && hasAnyDotNode(path$1.join(dir, entry.name), maxDepth - 1)) return true;
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
/** Copy `<src>` → `<dst>` when `<src>` carries a `.node`. Best-effort. */
|
|
466
|
+
function copyPrebuiltModuleDir(src, dst, label, logger) {
|
|
467
|
+
if (!hasAnyDotNode(src, 5)) return;
|
|
468
|
+
if (hasAnyDotNode(dst, 5)) return;
|
|
469
|
+
try {
|
|
470
|
+
fs.rmSync(dst, {
|
|
471
|
+
recursive: true,
|
|
472
|
+
force: true
|
|
473
|
+
});
|
|
474
|
+
fs.mkdirSync(path$1.dirname(dst), { recursive: true });
|
|
475
|
+
fs.cpSync(src, dst, {
|
|
476
|
+
recursive: true,
|
|
477
|
+
dereference: true
|
|
478
|
+
});
|
|
479
|
+
logger.info("Prebuilt native module propagated from bundled addon copy", { meta: {
|
|
480
|
+
module: label,
|
|
481
|
+
src,
|
|
482
|
+
dst
|
|
483
|
+
} });
|
|
484
|
+
} catch (err) {
|
|
485
|
+
logger.warn("Prebuilt native module propagation failed (npm install will retry)", { meta: {
|
|
486
|
+
module: label,
|
|
487
|
+
src,
|
|
488
|
+
error: errMsg(err)
|
|
489
|
+
} });
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Propagate PREBUILT native modules from a bundled addon copy's own
|
|
494
|
+
* `node_modules` (the packaged-desktop 'local' source at
|
|
495
|
+
* `<resources>/addons/<addon>`) into the freshly-copied target
|
|
496
|
+
* (`<data>/addons/@camstack/<addon>`), BEFORE the target's `npm install` runs.
|
|
497
|
+
*
|
|
498
|
+
* Why (Mac desktop first-boot FATAL, 2026-07-21): `installCopy` copies only
|
|
499
|
+
* `dist/` then `npm install`s the addon's runtime deps. On a toolchain-less Mac
|
|
500
|
+
* that install leaves the traditional native `better-sqlite3` SOURCE-ONLY in
|
|
501
|
+
* `<addon>/node_modules` — a broken copy that then SHADOWS the good hoisted
|
|
502
|
+
* `.node` NODE_PATH would otherwise resolve, crashing the runner with "Could
|
|
503
|
+
* not locate the bindings file". Seeding the prebuilt copy here makes the npm
|
|
504
|
+
* install treat the native as satisfied (no re-run of `prebuild-install`), so
|
|
505
|
+
* the good `.node` stays in place. A no-op in dev / docker where the source
|
|
506
|
+
* carries no bundled natives.
|
|
507
|
+
*/
|
|
508
|
+
function copyBundledNativeModules(sourceDir, targetDir, logger) {
|
|
509
|
+
const srcNm = path$1.join(sourceDir, "node_modules");
|
|
510
|
+
if (!fs.existsSync(srcNm)) return;
|
|
511
|
+
const dstNm = path$1.join(targetDir, "node_modules");
|
|
512
|
+
for (const [name, scopes] of Object.entries(BUNDLED_NATIVE_SIBLING_SCOPES)) {
|
|
513
|
+
copyPrebuiltModuleDir(path$1.join(srcNm, name), path$1.join(dstNm, name), name, logger);
|
|
514
|
+
for (const scope of scopes) {
|
|
515
|
+
const scopeSrc = path$1.join(srcNm, scope);
|
|
516
|
+
let siblings;
|
|
517
|
+
try {
|
|
518
|
+
siblings = fs.readdirSync(scopeSrc);
|
|
519
|
+
} catch {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
for (const sibling of siblings) copyPrebuiltModuleDir(path$1.join(scopeSrc, sibling), path$1.join(dstNm, scope, sibling), `${scope}/${sibling}`, logger);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
440
526
|
/** A package dir is "built" when it carries a compiled `.node` or a prebuilds tree. */
|
|
441
527
|
function hasBuiltBinary(pkgDir) {
|
|
442
528
|
if (!fs.existsSync(path$1.join(pkgDir, "package.json"))) return false;
|
|
@@ -7320,4 +7406,4 @@ async function installManifestPythonDeps(declaration, addonDir, deps, logger) {
|
|
|
7320
7406
|
await deps.installPythonRequirements(reqAbs);
|
|
7321
7407
|
}
|
|
7322
7408
|
//#endregion
|
|
7323
|
-
export { buildNativeCapProxy as $, createHubCapForwardService as A, AGENT_CAP_FWD_ACTION as B, brokerTransportLink as C,
|
|
7409
|
+
export { buildNativeCapProxy as $, createHubCapForwardService as A, AGENT_CAP_FWD_ACTION as B, brokerTransportLink as C, resolveAddonClass as Ct, localProviderLink as D, ipcParentLink as E, createUdsLogger as F, callWithServiceDiscovery as G, CapRouteResolver as H, createUdsLoggerWithControl as I, UdsLocalTransportServer as J, createLocalTransport as K, LocalChildClient as L, createUdsEventBridge as M, createUdsEventBus as N, HUB_CAP_FWD_ACTION as O, udsChildLogToWorkerEntry as P, encodeFrame as Q, LocalChildRegistry as R, brokerCallForCap as S, runNpm as St, ipcChildLink as T, CapRouteError as U, AGENT_CAP_FWD_SERVICE as V, classifyCapRoute as W, localEndpointPath as X, SocketChannel as Y, FrameDecoder as Z, createKernelHwAccel as _, CapabilityHandle as _t, getWorkerDeviceRegistry as a, setWorkerNativeCapsChangeListener as at, __resetCapUsageRegistryForTests as b, installManifestNativeDeps as bt, setHubConnected as c, NATIVE_PROVIDER_SERVICE_INFIX as ct, getBrokerEventBus as d, capBareAction as dt, buildUdsNativeCapProxy as et, getMoleculerEventStats as f, capServiceName as ft, AddonDepsManager as g, DeviceRegistry as gt, subscribePassthrough as h, serializeTypedArrays as ht, createUdsAddonContext as i, mountNativeCapService as it, createParentUnownedCallHandler as j, HUB_CAP_FWD_SERVICE as k, EVENT_TOPIC_PREFIX as l, capActionName as lt, setNodeEventInterest as m, deserializeTypedArrays as mt, adaptBrokerToCluster as n, getWorkerNativeCapProvider as nt, getOrInitReadinessRegistry as o, createAddonService as ot, registerEventBusService as p, parseCapAction as pt, UdsLocalTransportClient as q, createAddonContext as r, getWorkerNativeCapSnapshot as rt, getOrInitReadinessRegistryForClient as s, validateProviderRegistrations as st, installManifestPythonDeps as t, createBrokerDeviceManagerApi as tt, clusterEventTopic as u, capActionSuffix as ut, resolveHwAccel as v, CapabilityUnavailableError as vt, buildLinkChain as w, createAddonDataPlaneFacility as wt, getCapUsageRegistry as x, resolveNpmInvocation as xt, CapUsageRegistry as y, copyBundledNativeModules as yt, UDS_NO_ROUTE_PREFIX as z };
|