@camstack/system 1.1.50 → 1.1.51
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 +1 -1
- package/dist/builtins/device-manager/device-manager.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 +162 -12
- package/dist/builtins/snapshot/index.mjs +162 -12
- package/dist/builtins/snapshot/snapshot-media-handler.d.ts +38 -0
- package/dist/builtins/snapshot/snapshot.addon.d.ts +26 -0
- 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-CC3E-_3h.mjs → dist-BWQX9yUj.mjs} +11 -3
- package/dist/{dist-CQYo6Zp8.js → dist-C3uHEBtP.js} +10 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +34 -22
- package/dist/index.mjs +34 -23
- package/dist/kernel/addon-installer.d.ts +8 -0
- package/dist/kernel/deps/manifest-native-deps.d.ts +1 -1
- package/dist/kernel/deps/npm-command.d.ts +38 -0
- package/dist/{manifest-python-deps-DqATCsK0.mjs → manifest-python-deps-BA6KA9If.mjs} +268 -124
- package/dist/{manifest-python-deps-Wy8dNJpr.js → manifest-python-deps-CVcJ9hDX.js} +279 -123
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-C3uHEBtP.js");
|
|
3
3
|
let _camstack_types_node = require("@camstack/types/node");
|
|
4
4
|
let node_http = require("node:http");
|
|
5
5
|
let node_fs = require("node:fs");
|
|
@@ -8,14 +8,130 @@ let node_path = require("node:path");
|
|
|
8
8
|
node_path = require_chunk.__toESM(node_path);
|
|
9
9
|
let node_crypto = require("node:crypto");
|
|
10
10
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
11
|
+
let _camstack_types_addon = require("@camstack/types/addon");
|
|
11
12
|
let node_child_process = require("node:child_process");
|
|
12
13
|
let node_util = require("node:util");
|
|
13
14
|
let node_os = require("node:os");
|
|
14
15
|
node_os = require_chunk.__toESM(node_os);
|
|
15
16
|
let node_fs_promises = require("node:fs/promises");
|
|
16
|
-
let _camstack_types_addon = require("@camstack/types/addon");
|
|
17
17
|
let _trpc_client = require("@trpc/client");
|
|
18
18
|
let node_net = require("node:net");
|
|
19
|
+
//#region src/kernel/moleculer/addon-data-plane-facility.ts
|
|
20
|
+
/**
|
|
21
|
+
* Kernel side of the addon HTTP **data-plane** (see {@link AddonDataPlane}).
|
|
22
|
+
*
|
|
23
|
+
* Each addon context gets one facility. On the first `serve()`, it binds a
|
|
24
|
+
* `127.0.0.1` HTTP listener IN THE ADDON'S PROCESS and multiplexes registered
|
|
25
|
+
* prefixes → the addon's real `(req, res)` handlers (longest-prefix wins). Every
|
|
26
|
+
* request must carry the per-listener shared secret (injected by the hub's
|
|
27
|
+
* reverse-proxy) — anything else gets 403, so no other local process can reach
|
|
28
|
+
* the listener. The facility reports its live endpoints through a `sink` so the
|
|
29
|
+
* runner can hand them to the hub (which pulls them when it mounts the addon).
|
|
30
|
+
*
|
|
31
|
+
* The addon never sees the secret, the port, or any transport detail — it just
|
|
32
|
+
* streams to `res`. The hub authenticates and reverse-proxies in front.
|
|
33
|
+
*/
|
|
34
|
+
function trimSlashes(s) {
|
|
35
|
+
return s.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
36
|
+
}
|
|
37
|
+
function createAddonDataPlaneFacility(args) {
|
|
38
|
+
const bindHost = args.bindHost ?? "127.0.0.1";
|
|
39
|
+
const secret = (0, node_crypto.randomBytes)(24).toString("hex");
|
|
40
|
+
const served = /* @__PURE__ */ new Map();
|
|
41
|
+
let server = null;
|
|
42
|
+
let baseUrl = "";
|
|
43
|
+
const route = (req, res) => {
|
|
44
|
+
if (req.headers[_camstack_types_addon.DATAPLANE_SECRET_HEADER] !== secret) {
|
|
45
|
+
res.writeHead(403).end();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const normalized = `/${trimSlashes((req.url ?? "/").split("?")[0] ?? "/")}`;
|
|
49
|
+
let best = null;
|
|
50
|
+
for (const [prefix, entry] of served) {
|
|
51
|
+
const p = `/${prefix}`;
|
|
52
|
+
if (normalized === p || normalized.startsWith(`${p}/`)) {
|
|
53
|
+
if (best === null || p.length > `/${best.prefix}`.length) best = {
|
|
54
|
+
prefix,
|
|
55
|
+
entry,
|
|
56
|
+
rest: normalized.slice(p.length) || "/"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (best === null) {
|
|
61
|
+
res.writeHead(404).end();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const original = req.url ?? "";
|
|
65
|
+
const qIdx = original.indexOf("?");
|
|
66
|
+
req.url = best.rest + (qIdx >= 0 ? original.slice(qIdx) : "");
|
|
67
|
+
Promise.resolve(best.entry.handler(req, res)).catch((err) => {
|
|
68
|
+
args.logger.warn("data-plane handler threw", { meta: {
|
|
69
|
+
prefix: best.prefix,
|
|
70
|
+
error: err instanceof Error ? err.message : String(err)
|
|
71
|
+
} });
|
|
72
|
+
if (!res.headersSent) res.writeHead(500).end();
|
|
73
|
+
else res.end();
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const ensureServer = async () => {
|
|
77
|
+
if (server) return;
|
|
78
|
+
const s = (0, node_http.createServer)((req, res) => route(req, res));
|
|
79
|
+
await new Promise((resolve, reject) => {
|
|
80
|
+
s.once("error", reject);
|
|
81
|
+
s.listen(0, bindHost, () => resolve());
|
|
82
|
+
});
|
|
83
|
+
const addr = s.address();
|
|
84
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
85
|
+
server = s;
|
|
86
|
+
baseUrl = `http://${bindHost}:${port}`;
|
|
87
|
+
};
|
|
88
|
+
const publish = () => {
|
|
89
|
+
const endpoints = [...served.entries()].map(([prefix, entry]) => ({
|
|
90
|
+
prefix,
|
|
91
|
+
access: entry.access,
|
|
92
|
+
baseUrl,
|
|
93
|
+
secret
|
|
94
|
+
}));
|
|
95
|
+
args.sink?.set(args.addonId, endpoints);
|
|
96
|
+
};
|
|
97
|
+
const dataPlane = { serve: async (options) => {
|
|
98
|
+
const prefix = trimSlashes(options.prefix);
|
|
99
|
+
if (prefix.length === 0) throw new Error("dataPlane.serve: a non-empty prefix is required");
|
|
100
|
+
await ensureServer();
|
|
101
|
+
served.set(prefix, {
|
|
102
|
+
access: options.access,
|
|
103
|
+
handler: options.handler
|
|
104
|
+
});
|
|
105
|
+
publish();
|
|
106
|
+
args.logger.info("data-plane endpoint served", { meta: {
|
|
107
|
+
prefix,
|
|
108
|
+
access: options.access,
|
|
109
|
+
baseUrl
|
|
110
|
+
} });
|
|
111
|
+
return {
|
|
112
|
+
baseUrl,
|
|
113
|
+
prefix,
|
|
114
|
+
dispose: async () => {
|
|
115
|
+
served.delete(prefix);
|
|
116
|
+
publish();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
} };
|
|
120
|
+
const dispose = async () => {
|
|
121
|
+
served.clear();
|
|
122
|
+
args.sink?.set(args.addonId, []);
|
|
123
|
+
if (server) {
|
|
124
|
+
const s = server;
|
|
125
|
+
server = null;
|
|
126
|
+
await new Promise((resolve) => s.close(() => resolve()));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
130
|
+
dataPlane,
|
|
131
|
+
dispose
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
19
135
|
//#region src/kernel/addon-class-resolver.ts
|
|
20
136
|
function isRecord(value) {
|
|
21
137
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -56,9 +172,147 @@ function resolveAddonClass(mod) {
|
|
|
56
172
|
if (namedAddon) return namedAddon;
|
|
57
173
|
}
|
|
58
174
|
//#endregion
|
|
175
|
+
//#region src/kernel/deps/npm-command.ts
|
|
176
|
+
/**
|
|
177
|
+
* npm invocation resolver — makes "is npm available?" a solved problem for
|
|
178
|
+
* every installer code path that shells out to npm.
|
|
179
|
+
*
|
|
180
|
+
* Why this exists (Mac mini incident, silent for days until 2026-07-16): the
|
|
181
|
+
* packaged Electron agent ships a BARE node binary — `Resources/node/bin/node`
|
|
182
|
+
* with no npm — so every `execFileAsync('npm', ...)` died with ENOENT inside a
|
|
183
|
+
* "non-fatal" catch. Addons with runtime deps installed WITHOUT their
|
|
184
|
+
* node_modules, reported "running", and failed at first import
|
|
185
|
+
* (`Cannot find module 'sharp'`): the detection group ran 1/3 members and the
|
|
186
|
+
* node silently fell out of the balancer pool.
|
|
187
|
+
*
|
|
188
|
+
* Resolution order:
|
|
189
|
+
* 1. system `npm` on PATH (probed once per process) — hub image, dev
|
|
190
|
+
* machines, Linux agent containers;
|
|
191
|
+
* 2. a previously-bootstrapped copy under `cacheDir` — run its `npm-cli.js`
|
|
192
|
+
* with the CURRENT node binary (`process.execPath`), which always exists;
|
|
193
|
+
* 3. BOOTSTRAP: fetch `<registry>/npm/latest` (the npm package is fully
|
|
194
|
+
* self-contained — its tarball bundles every dependency), download +
|
|
195
|
+
* extract into `cacheDir`, then use it as in (2).
|
|
196
|
+
*
|
|
197
|
+
* Failures THROW with an actionable message — a caller must never proceed as
|
|
198
|
+
* if dependencies were installed when they were not.
|
|
199
|
+
*/
|
|
200
|
+
var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
|
|
201
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
202
|
+
/** Where the bootstrap lands inside `cacheDir` (npm tgz carries a `package/` prefix). */
|
|
203
|
+
function bootstrappedCliPath(cacheDir) {
|
|
204
|
+
return node_path.join(cacheDir, "package", "bin", "npm-cli.js");
|
|
205
|
+
}
|
|
206
|
+
async function defaultProbeSystemNpm() {
|
|
207
|
+
try {
|
|
208
|
+
await execFileAsync$1("npm", ["--version"], { timeout: 15e3 });
|
|
209
|
+
return true;
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function defaultFetchJson(url) {
|
|
215
|
+
const res = await fetch(url);
|
|
216
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
217
|
+
return res.json();
|
|
218
|
+
}
|
|
219
|
+
async function defaultDownloadTarball(url, destTgz) {
|
|
220
|
+
const res = await fetch(url);
|
|
221
|
+
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} downloading ${url}`);
|
|
222
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
223
|
+
await node_fs.promises.writeFile(destTgz, bytes);
|
|
224
|
+
}
|
|
225
|
+
async function defaultExtractTarball(tgzPath, destDir) {
|
|
226
|
+
await execFileAsync$1("tar", [
|
|
227
|
+
"-xzf",
|
|
228
|
+
tgzPath,
|
|
229
|
+
"-C",
|
|
230
|
+
destDir
|
|
231
|
+
], { timeout: 12e4 });
|
|
232
|
+
}
|
|
233
|
+
/** The `dist.tarball` URL out of a registry version-metadata document. */
|
|
234
|
+
function tarballUrlOf(metadata) {
|
|
235
|
+
if (typeof metadata !== "object" || metadata === null) return null;
|
|
236
|
+
const dist = metadata["dist"];
|
|
237
|
+
if (typeof dist !== "object" || dist === null) return null;
|
|
238
|
+
const tarball = dist["tarball"];
|
|
239
|
+
return typeof tarball === "string" && tarball.length > 0 ? tarball : null;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Process-wide memo: resolution is stable for the process lifetime (npm does
|
|
243
|
+
* not appear/disappear mid-run, and the bootstrap is idempotent on disk).
|
|
244
|
+
*/
|
|
245
|
+
var cachedInvocation = null;
|
|
246
|
+
async function resolveNpmInvocation(options) {
|
|
247
|
+
cachedInvocation ??= resolveUncached(options).catch((err) => {
|
|
248
|
+
cachedInvocation = null;
|
|
249
|
+
throw err;
|
|
250
|
+
});
|
|
251
|
+
return cachedInvocation;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Resolve a working npm (system → bootstrapped → bootstrap) and run it with
|
|
255
|
+
* `args`. The ONE entry point every installer code path uses instead of a raw
|
|
256
|
+
* `execFile('npm', ...)` — so "npm missing on this host" is either solved
|
|
257
|
+
* (bootstrap) or a LOUD, actionable error, never a swallowed ENOENT.
|
|
258
|
+
*/
|
|
259
|
+
async function runNpm(args, options) {
|
|
260
|
+
const invocation = await resolveNpmInvocation({
|
|
261
|
+
cacheDir: options.cacheDir,
|
|
262
|
+
registry: options.registry,
|
|
263
|
+
logger: options.logger
|
|
264
|
+
});
|
|
265
|
+
return execFileAsync$1(invocation.command, [...invocation.argsPrefix, ...args], {
|
|
266
|
+
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
267
|
+
timeout: options.timeout ?? 3e5
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async function resolveUncached(options) {
|
|
271
|
+
if (await (options.probeSystemNpm ?? defaultProbeSystemNpm)()) return {
|
|
272
|
+
command: "npm",
|
|
273
|
+
argsPrefix: []
|
|
274
|
+
};
|
|
275
|
+
const cliPath = bootstrappedCliPath(options.cacheDir);
|
|
276
|
+
if (node_fs.existsSync(cliPath)) {
|
|
277
|
+
options.logger.info("system npm unavailable — using bootstrapped npm", { meta: { cliPath } });
|
|
278
|
+
return {
|
|
279
|
+
command: process.execPath,
|
|
280
|
+
argsPrefix: [cliPath]
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
options.logger.warn("system npm unavailable — bootstrapping npm from the registry", { meta: { cacheDir: options.cacheDir } });
|
|
284
|
+
try {
|
|
285
|
+
const registry = (options.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
286
|
+
const tarballUrl = tarballUrlOf(await (options.fetchJson ?? defaultFetchJson)(`${registry}/npm/latest`));
|
|
287
|
+
if (!tarballUrl) throw new Error(`registry metadata for 'npm' carries no dist.tarball (${registry})`);
|
|
288
|
+
await node_fs.promises.mkdir(options.cacheDir, { recursive: true });
|
|
289
|
+
const tgzPath = node_path.join(options.cacheDir, "npm-bootstrap.tgz");
|
|
290
|
+
await (options.downloadTarball ?? defaultDownloadTarball)(tarballUrl, tgzPath);
|
|
291
|
+
await (options.extractTarball ?? defaultExtractTarball)(tgzPath, options.cacheDir);
|
|
292
|
+
await node_fs.promises.rm(tgzPath, { force: true });
|
|
293
|
+
if (!node_fs.existsSync(cliPath)) throw new Error(`extracted npm tarball is missing ${cliPath}`);
|
|
294
|
+
options.logger.info("npm bootstrapped", { meta: { cliPath } });
|
|
295
|
+
return {
|
|
296
|
+
command: process.execPath,
|
|
297
|
+
argsPrefix: [cliPath]
|
|
298
|
+
};
|
|
299
|
+
} catch (err) {
|
|
300
|
+
throw new Error(`no system npm on PATH and the npm bootstrap failed — cannot install addon dependencies on this node: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
59
304
|
//#region src/kernel/deps/manifest-native-deps.ts
|
|
60
305
|
var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
61
306
|
/**
|
|
307
|
+
* Fallback bootstrap-cache location when the caller doesn't thread one
|
|
308
|
+
* through (the forked addon-runner's spawn-time install). tmpdir survives the
|
|
309
|
+
* process; a reboot just re-bootstraps. Callers with a persistent root (the
|
|
310
|
+
* installer) pass their own.
|
|
311
|
+
*/
|
|
312
|
+
function defaultNpmCacheDir() {
|
|
313
|
+
return node_path.join(node_os.tmpdir(), "camstack-npm-bootstrap");
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
62
316
|
* Native node modules an addon needs at runtime but cannot be bundled
|
|
63
317
|
* (`.node` binary files require ABI-matched compilation). Mirror of the
|
|
64
318
|
* Python `requirements.txt` pattern in `manifest-python-deps.ts` —
|
|
@@ -81,7 +335,7 @@ var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
|
81
335
|
* ABI; surface a clear error at first `import` of the native module
|
|
82
336
|
* otherwise).
|
|
83
337
|
*/
|
|
84
|
-
async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
|
|
338
|
+
async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry, npmCacheDir) {
|
|
85
339
|
const native = readNativeDeps(pkgRaw);
|
|
86
340
|
if (native == null || Object.keys(native).length === 0) return;
|
|
87
341
|
const markerFile = node_path.join(addonDir, ".camstack-native-deps-installed");
|
|
@@ -126,14 +380,17 @@ async function installManifestNativeDeps(addonDir, pkgRaw, logger, registry) {
|
|
|
126
380
|
...specs
|
|
127
381
|
];
|
|
128
382
|
try {
|
|
129
|
-
await
|
|
383
|
+
await runNpm(args, {
|
|
384
|
+
cacheDir: npmCacheDir ?? defaultNpmCacheDir(),
|
|
385
|
+
registry,
|
|
386
|
+
logger,
|
|
130
387
|
cwd: addonDir,
|
|
131
388
|
timeout: 3e5
|
|
132
389
|
});
|
|
133
390
|
} catch (err) {
|
|
134
391
|
throw new Error(`npm install of native deps failed for ${addonDir}: ${(0, _camstack_types_addon.errMsg)(err)}`, { cause: err });
|
|
135
392
|
}
|
|
136
|
-
await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger);
|
|
393
|
+
await rebuildNativeDeps(addonDir, pending.map(([name]) => name), logger, npmCacheDir ?? defaultNpmCacheDir(), registry);
|
|
137
394
|
try {
|
|
138
395
|
node_fs.writeFileSync(markerFile, markerHash);
|
|
139
396
|
} catch (err) {
|
|
@@ -249,7 +506,7 @@ function markerMatches(markerFile, expected) {
|
|
|
249
506
|
* The first `import` of an actually-mismatched binary throws a clear
|
|
250
507
|
* error that points the operator at this rebuild step.
|
|
251
508
|
*/
|
|
252
|
-
async function rebuildNativeDeps(addonDir, packageNames, logger) {
|
|
509
|
+
async function rebuildNativeDeps(addonDir, packageNames, logger, npmCacheDir, registry) {
|
|
253
510
|
if (typeof process.versions.electron === "string") {
|
|
254
511
|
const electronVersion = process.versions.electron;
|
|
255
512
|
logger.info("Rebuilding native deps for Electron", { meta: {
|
|
@@ -293,7 +550,10 @@ async function rebuildNativeDeps(addonDir, packageNames, logger) {
|
|
|
293
550
|
packages: packageNames
|
|
294
551
|
} });
|
|
295
552
|
try {
|
|
296
|
-
await
|
|
553
|
+
await runNpm(["rebuild", ...packageNames], {
|
|
554
|
+
cacheDir: npmCacheDir,
|
|
555
|
+
registry,
|
|
556
|
+
logger,
|
|
297
557
|
cwd: addonDir,
|
|
298
558
|
timeout: 6e5
|
|
299
559
|
});
|
|
@@ -5749,122 +6009,6 @@ function createKernelInferenceEngine() {
|
|
|
5749
6009
|
return { resolveHardware: async () => mapSignalsToInferenceHardware(probePlatform()) };
|
|
5750
6010
|
}
|
|
5751
6011
|
//#endregion
|
|
5752
|
-
//#region src/kernel/moleculer/addon-data-plane-facility.ts
|
|
5753
|
-
/**
|
|
5754
|
-
* Kernel side of the addon HTTP **data-plane** (see {@link AddonDataPlane}).
|
|
5755
|
-
*
|
|
5756
|
-
* Each addon context gets one facility. On the first `serve()`, it binds a
|
|
5757
|
-
* `127.0.0.1` HTTP listener IN THE ADDON'S PROCESS and multiplexes registered
|
|
5758
|
-
* prefixes → the addon's real `(req, res)` handlers (longest-prefix wins). Every
|
|
5759
|
-
* request must carry the per-listener shared secret (injected by the hub's
|
|
5760
|
-
* reverse-proxy) — anything else gets 403, so no other local process can reach
|
|
5761
|
-
* the listener. The facility reports its live endpoints through a `sink` so the
|
|
5762
|
-
* runner can hand them to the hub (which pulls them when it mounts the addon).
|
|
5763
|
-
*
|
|
5764
|
-
* The addon never sees the secret, the port, or any transport detail — it just
|
|
5765
|
-
* streams to `res`. The hub authenticates and reverse-proxies in front.
|
|
5766
|
-
*/
|
|
5767
|
-
function trimSlashes(s) {
|
|
5768
|
-
return s.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
5769
|
-
}
|
|
5770
|
-
function createAddonDataPlaneFacility(args) {
|
|
5771
|
-
const bindHost = args.bindHost ?? "127.0.0.1";
|
|
5772
|
-
const secret = (0, node_crypto.randomBytes)(24).toString("hex");
|
|
5773
|
-
const served = /* @__PURE__ */ new Map();
|
|
5774
|
-
let server = null;
|
|
5775
|
-
let baseUrl = "";
|
|
5776
|
-
const route = (req, res) => {
|
|
5777
|
-
if (req.headers[_camstack_types_addon.DATAPLANE_SECRET_HEADER] !== secret) {
|
|
5778
|
-
res.writeHead(403).end();
|
|
5779
|
-
return;
|
|
5780
|
-
}
|
|
5781
|
-
const normalized = `/${trimSlashes((req.url ?? "/").split("?")[0] ?? "/")}`;
|
|
5782
|
-
let best = null;
|
|
5783
|
-
for (const [prefix, entry] of served) {
|
|
5784
|
-
const p = `/${prefix}`;
|
|
5785
|
-
if (normalized === p || normalized.startsWith(`${p}/`)) {
|
|
5786
|
-
if (best === null || p.length > `/${best.prefix}`.length) best = {
|
|
5787
|
-
prefix,
|
|
5788
|
-
entry,
|
|
5789
|
-
rest: normalized.slice(p.length) || "/"
|
|
5790
|
-
};
|
|
5791
|
-
}
|
|
5792
|
-
}
|
|
5793
|
-
if (best === null) {
|
|
5794
|
-
res.writeHead(404).end();
|
|
5795
|
-
return;
|
|
5796
|
-
}
|
|
5797
|
-
const original = req.url ?? "";
|
|
5798
|
-
const qIdx = original.indexOf("?");
|
|
5799
|
-
req.url = best.rest + (qIdx >= 0 ? original.slice(qIdx) : "");
|
|
5800
|
-
Promise.resolve(best.entry.handler(req, res)).catch((err) => {
|
|
5801
|
-
args.logger.warn("data-plane handler threw", { meta: {
|
|
5802
|
-
prefix: best.prefix,
|
|
5803
|
-
error: err instanceof Error ? err.message : String(err)
|
|
5804
|
-
} });
|
|
5805
|
-
if (!res.headersSent) res.writeHead(500).end();
|
|
5806
|
-
else res.end();
|
|
5807
|
-
});
|
|
5808
|
-
};
|
|
5809
|
-
const ensureServer = async () => {
|
|
5810
|
-
if (server) return;
|
|
5811
|
-
const s = (0, node_http.createServer)((req, res) => route(req, res));
|
|
5812
|
-
await new Promise((resolve, reject) => {
|
|
5813
|
-
s.once("error", reject);
|
|
5814
|
-
s.listen(0, bindHost, () => resolve());
|
|
5815
|
-
});
|
|
5816
|
-
const addr = s.address();
|
|
5817
|
-
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
5818
|
-
server = s;
|
|
5819
|
-
baseUrl = `http://${bindHost}:${port}`;
|
|
5820
|
-
};
|
|
5821
|
-
const publish = () => {
|
|
5822
|
-
const endpoints = [...served.entries()].map(([prefix, entry]) => ({
|
|
5823
|
-
prefix,
|
|
5824
|
-
access: entry.access,
|
|
5825
|
-
baseUrl,
|
|
5826
|
-
secret
|
|
5827
|
-
}));
|
|
5828
|
-
args.sink?.set(args.addonId, endpoints);
|
|
5829
|
-
};
|
|
5830
|
-
const dataPlane = { serve: async (options) => {
|
|
5831
|
-
const prefix = trimSlashes(options.prefix);
|
|
5832
|
-
if (prefix.length === 0) throw new Error("dataPlane.serve: a non-empty prefix is required");
|
|
5833
|
-
await ensureServer();
|
|
5834
|
-
served.set(prefix, {
|
|
5835
|
-
access: options.access,
|
|
5836
|
-
handler: options.handler
|
|
5837
|
-
});
|
|
5838
|
-
publish();
|
|
5839
|
-
args.logger.info("data-plane endpoint served", { meta: {
|
|
5840
|
-
prefix,
|
|
5841
|
-
access: options.access,
|
|
5842
|
-
baseUrl
|
|
5843
|
-
} });
|
|
5844
|
-
return {
|
|
5845
|
-
baseUrl,
|
|
5846
|
-
prefix,
|
|
5847
|
-
dispose: async () => {
|
|
5848
|
-
served.delete(prefix);
|
|
5849
|
-
publish();
|
|
5850
|
-
}
|
|
5851
|
-
};
|
|
5852
|
-
} };
|
|
5853
|
-
const dispose = async () => {
|
|
5854
|
-
served.clear();
|
|
5855
|
-
args.sink?.set(args.addonId, []);
|
|
5856
|
-
if (server) {
|
|
5857
|
-
const s = server;
|
|
5858
|
-
server = null;
|
|
5859
|
-
await new Promise((resolve) => s.close(() => resolve()));
|
|
5860
|
-
}
|
|
5861
|
-
};
|
|
5862
|
-
return {
|
|
5863
|
-
dataPlane,
|
|
5864
|
-
dispose
|
|
5865
|
-
};
|
|
5866
|
-
}
|
|
5867
|
-
//#endregion
|
|
5868
6012
|
//#region src/kernel/deps/addon-deps-manager.ts
|
|
5869
6013
|
/**
|
|
5870
6014
|
* Canonical implementation of `IAddonDepsManager`, injected into every
|
|
@@ -7356,6 +7500,12 @@ Object.defineProperty(exports, "createAddonContext", {
|
|
|
7356
7500
|
return createAddonContext;
|
|
7357
7501
|
}
|
|
7358
7502
|
});
|
|
7503
|
+
Object.defineProperty(exports, "createAddonDataPlaneFacility", {
|
|
7504
|
+
enumerable: true,
|
|
7505
|
+
get: function() {
|
|
7506
|
+
return createAddonDataPlaneFacility;
|
|
7507
|
+
}
|
|
7508
|
+
});
|
|
7359
7509
|
Object.defineProperty(exports, "createAddonService", {
|
|
7360
7510
|
enumerable: true,
|
|
7361
7511
|
get: function() {
|
|
@@ -7548,6 +7698,12 @@ Object.defineProperty(exports, "resolveHwAccel", {
|
|
|
7548
7698
|
return resolveHwAccel;
|
|
7549
7699
|
}
|
|
7550
7700
|
});
|
|
7701
|
+
Object.defineProperty(exports, "runNpm", {
|
|
7702
|
+
enumerable: true,
|
|
7703
|
+
get: function() {
|
|
7704
|
+
return runNpm;
|
|
7705
|
+
}
|
|
7706
|
+
});
|
|
7551
7707
|
Object.defineProperty(exports, "serializeTypedArrays", {
|
|
7552
7708
|
enumerable: true,
|
|
7553
7709
|
get: function() {
|