@camstack/system 1.1.34 → 1.1.36
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 +87 -35
- package/dist/addon-runner.mjs +87 -35
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +6 -6
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +6 -6
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +12 -12
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +5 -5
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +3 -3
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +69 -69
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/hub-forwarder/index.js +3 -3
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +7 -7
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +7 -7
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +5 -5
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +13 -13
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +4 -4
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +13 -13
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +4 -4
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +9 -9
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +5 -5
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +5 -5
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +3 -3
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/dist-C7oOY4iL.mjs +24559 -0
- package/dist/dist-COIhbSao.js +25032 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +92 -115
- package/dist/index.mjs +52 -52
- package/dist/kernel/moleculer/child-cap-descriptors.d.ts +34 -0
- package/dist/kernel/moleculer/child-cap-dispatch.d.ts +10 -2
- package/dist/kernel/transport/child-cap-protocol.d.ts +20 -0
- package/dist/{manifest-python-deps-u-AQBQY6.mjs → manifest-python-deps-BYIV8-rc.mjs} +31 -29
- package/dist/{manifest-python-deps-CAnO1wPx.js → manifest-python-deps-CZFqI4wp.js} +7 -5
- package/package.json +1 -1
package/dist/addon-runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
2
|
+
const require_manifest_python_deps = require("./manifest-python-deps-CZFqI4wp.js");
|
|
3
3
|
const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
node_fs = require_chunk.__toESM(node_fs);
|
|
@@ -115,6 +115,17 @@ function isCapMethod(value) {
|
|
|
115
115
|
return typeof value === "function";
|
|
116
116
|
}
|
|
117
117
|
/**
|
|
118
|
+
* Extract a string `addonId` out of arbitrary method args — the fallback
|
|
119
|
+
* provider-selection hint for calls that arrive WITHOUT the out-of-band
|
|
120
|
+
* `CapCallMessage.addonId` (e.g. a sibling child's unowned collection call
|
|
121
|
+
* forwarded by the parent, where the raw input still carries `addonId`).
|
|
122
|
+
*/
|
|
123
|
+
function extractArgsAddonId(args) {
|
|
124
|
+
if (args === null || typeof args !== "object") return void 0;
|
|
125
|
+
const raw = Reflect.get(args, "addonId");
|
|
126
|
+
return typeof raw === "string" ? raw : void 0;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
118
129
|
* Creates a unified dispatcher that routes a {@link CapCallInput} to either a
|
|
119
130
|
* device-scoped or singleton provider, then invokes the requested method.
|
|
120
131
|
*
|
|
@@ -123,12 +134,13 @@ function isCapMethod(value) {
|
|
|
123
134
|
function createChildCapDispatch(deps) {
|
|
124
135
|
return async (call) => {
|
|
125
136
|
const { capName, method, args, deviceId } = call;
|
|
137
|
+
const addonId = call.addonId ?? extractArgsAddonId(args);
|
|
126
138
|
let provider;
|
|
127
139
|
if (deviceId !== void 0) {
|
|
128
|
-
provider = deps.getDeviceProvider(capName, deviceId) ?? deps.getSingletonProvider(capName);
|
|
140
|
+
provider = deps.getDeviceProvider(capName, deviceId) ?? deps.getSingletonProvider(capName, addonId);
|
|
129
141
|
if (provider == null) throw new Error(`child-cap-dispatch: no provider for '${capName}' (device '${String(deviceId)}' or singleton)`);
|
|
130
142
|
} else {
|
|
131
|
-
provider = deps.getSingletonProvider(capName);
|
|
143
|
+
provider = deps.getSingletonProvider(capName, addonId);
|
|
132
144
|
if (provider == null) throw new Error(`child-cap-dispatch: no singleton provider for '${capName}'`);
|
|
133
145
|
}
|
|
134
146
|
const fn = Reflect.get(provider, method);
|
|
@@ -137,6 +149,51 @@ function createChildCapDispatch(deps) {
|
|
|
137
149
|
};
|
|
138
150
|
}
|
|
139
151
|
//#endregion
|
|
152
|
+
//#region src/kernel/moleculer/child-cap-descriptors.ts
|
|
153
|
+
/**
|
|
154
|
+
* Builds the {@link ChildCapDescriptor} array that the UDS child transport
|
|
155
|
+
* sends to the parent on registration.
|
|
156
|
+
*
|
|
157
|
+
* - Singleton caps come from the runner UNION manifest (all caps registered by
|
|
158
|
+
* every addon in this runner). One descriptor per unique (addonId, capName)
|
|
159
|
+
* pair, mode `'singleton'` and no `deviceId`. The `addonId` stamp lets the
|
|
160
|
+
* parent's manifest builder register each hosted addon's caps under the
|
|
161
|
+
* REAL addon id — a GROUPED runner (`execution.group`) hosts several addons,
|
|
162
|
+
* and deduplicating by capName alone would collapse two co-located providers
|
|
163
|
+
* of the same collection cap (e.g. `network-access` from cloudflare-tunnel
|
|
164
|
+
* AND tailscale-ingress) into one descriptor registered under a synthetic
|
|
165
|
+
* `addonId = childId` (the group name) — the "@camstack/addon-net-access"
|
|
166
|
+
* admin-UI bug.
|
|
167
|
+
* - Device-scoped caps come from the native-cap snapshot — one descriptor per
|
|
168
|
+
* (capName, deviceId) pair, mode `'collection'`, stamped with the owning
|
|
169
|
+
* addon's id.
|
|
170
|
+
*
|
|
171
|
+
* Pure module — extracted from `addon-runner.ts` (whose `main()` runs at
|
|
172
|
+
* import time) so it is unit-testable.
|
|
173
|
+
*/
|
|
174
|
+
function buildChildCapDescriptors(manifest, nativeCapSnapshot) {
|
|
175
|
+
const descriptors = [];
|
|
176
|
+
const seen = /* @__PURE__ */ new Set();
|
|
177
|
+
for (const entry of manifest) for (const capName of entry.capabilities) {
|
|
178
|
+
const key = `${entry.addonId}::${capName}`;
|
|
179
|
+
if (!seen.has(key)) {
|
|
180
|
+
seen.add(key);
|
|
181
|
+
descriptors.push({
|
|
182
|
+
capName,
|
|
183
|
+
mode: "singleton",
|
|
184
|
+
addonId: entry.addonId
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const { capName, addonId, deviceIds } of nativeCapSnapshot) for (const deviceId of deviceIds) descriptors.push({
|
|
189
|
+
capName,
|
|
190
|
+
mode: "collection",
|
|
191
|
+
deviceId,
|
|
192
|
+
addonId
|
|
193
|
+
});
|
|
194
|
+
return descriptors;
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
140
197
|
//#region src/kernel/moleculer/child-addon-call-dispatch.ts
|
|
141
198
|
function isRecord$1(value) {
|
|
142
199
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -333,33 +390,6 @@ async function loadAddonModule(addonId, addonDir) {
|
|
|
333
390
|
AddonClass
|
|
334
391
|
};
|
|
335
392
|
}
|
|
336
|
-
/**
|
|
337
|
-
* Builds the {@link ChildCapDescriptor} array that the UDS child transport
|
|
338
|
-
* sends to the parent on registration.
|
|
339
|
-
*
|
|
340
|
-
* - Singleton caps come from the runner UNION manifest (all caps registered by
|
|
341
|
-
* every addon in this runner). One descriptor per unique cap name, mode
|
|
342
|
-
* `'singleton'` and no `deviceId`.
|
|
343
|
-
* - Device-scoped caps come from the native-cap snapshot — one descriptor per
|
|
344
|
-
* (capName, deviceId) pair, mode `'collection'`.
|
|
345
|
-
*/
|
|
346
|
-
function buildChildCapDescriptors(manifest, nativeCapSnapshot) {
|
|
347
|
-
const descriptors = [];
|
|
348
|
-
const seen = /* @__PURE__ */ new Set();
|
|
349
|
-
for (const entry of manifest) for (const capName of entry.capabilities) if (!seen.has(capName)) {
|
|
350
|
-
seen.add(capName);
|
|
351
|
-
descriptors.push({
|
|
352
|
-
capName,
|
|
353
|
-
mode: "singleton"
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
for (const { capName, deviceIds } of nativeCapSnapshot) for (const deviceId of deviceIds) descriptors.push({
|
|
357
|
-
capName,
|
|
358
|
-
mode: "collection",
|
|
359
|
-
deviceId
|
|
360
|
-
});
|
|
361
|
-
return descriptors;
|
|
362
|
-
}
|
|
363
393
|
async function main() {
|
|
364
394
|
const runnerId = process.env["CAMSTACK_RUNNER_ID"];
|
|
365
395
|
const addonsJson = process.env["CAMSTACK_RUNNER_ADDONS"];
|
|
@@ -427,14 +457,34 @@ async function main() {
|
|
|
427
457
|
const runnerManifest = [];
|
|
428
458
|
/**
|
|
429
459
|
* Aggregated singleton (system-scoped) providers across ALL addons in this
|
|
430
|
-
* runner — keyed
|
|
431
|
-
* singleton calls (no Moleculer).
|
|
432
|
-
*
|
|
460
|
+
* runner — keyed capName → (addonId → provider). Read by the UDS child-cap
|
|
461
|
+
* dispatch to resolve singleton calls (no Moleculer). The nested per-addon
|
|
462
|
+
* keying matters for GROUPED runners (`execution.group`): two co-located
|
|
463
|
+
* addons can register the SAME collection cap (e.g. `network-access` from
|
|
464
|
+
* cloudflare-tunnel AND tailscale-ingress) — a flat capName map would keep
|
|
465
|
+
* only the last-registered provider. Populated during the addon-init loop;
|
|
466
|
+
* a cap-call that arrives before its addon initialises is rejected by the
|
|
433
467
|
* dispatcher (the parent only routes caps the child has registered via the
|
|
434
468
|
* UDS `register`/`updateCaps` handshake).
|
|
435
469
|
*/
|
|
436
470
|
const allSingletonProviders = /* @__PURE__ */ new Map();
|
|
437
471
|
/**
|
|
472
|
+
* Resolve a singleton provider for the dispatcher: exact (capName, addonId)
|
|
473
|
+
* match when the hint names a hosted provider, else the cap's first
|
|
474
|
+
* registered provider (an unmatched `addonId` is method DATA — e.g.
|
|
475
|
+
* `addons.restartAddon({ addonId })` — not a routing key).
|
|
476
|
+
*/
|
|
477
|
+
const resolveSingletonProvider = (capName, addonId) => {
|
|
478
|
+
const byAddon = allSingletonProviders.get(capName);
|
|
479
|
+
if (!byAddon || byAddon.size === 0) return null;
|
|
480
|
+
if (addonId !== void 0) {
|
|
481
|
+
const exact = byAddon.get(addonId);
|
|
482
|
+
if (exact !== void 0) return exact;
|
|
483
|
+
}
|
|
484
|
+
const first = byAddon.values().next();
|
|
485
|
+
return first.done ? null : first.value;
|
|
486
|
+
};
|
|
487
|
+
/**
|
|
438
488
|
* Per-addon route-provider map — keyed by addonId → the addon's live
|
|
439
489
|
* `addon-routes` provider object. Resolves `getRouteProvider(addonId)`
|
|
440
490
|
* per-addon rather than returning a single global provider. Populated
|
|
@@ -473,7 +523,7 @@ async function main() {
|
|
|
473
523
|
childId: runnerId,
|
|
474
524
|
caps: buildChildCapDescriptors(runnerManifest, require_manifest_python_deps.getWorkerNativeCapSnapshot()),
|
|
475
525
|
dispatch: createChildCapDispatch({
|
|
476
|
-
getSingletonProvider: (capName) =>
|
|
526
|
+
getSingletonProvider: (capName, addonId) => resolveSingletonProvider(capName, addonId),
|
|
477
527
|
getDeviceProvider: (capName, deviceId) => require_manifest_python_deps.getWorkerNativeCapProvider(capName, deviceId) ?? null
|
|
478
528
|
})
|
|
479
529
|
});
|
|
@@ -538,7 +588,9 @@ async function main() {
|
|
|
538
588
|
context.registerProvider(capName, reg.provider);
|
|
539
589
|
const prov = reg.provider;
|
|
540
590
|
if (prov !== null && typeof prov === "object" && !Array.isArray(prov)) {
|
|
541
|
-
allSingletonProviders.
|
|
591
|
+
const byAddon = allSingletonProviders.get(capName) ?? /* @__PURE__ */ new Map();
|
|
592
|
+
byAddon.set(spec.addonId, prov);
|
|
593
|
+
allSingletonProviders.set(capName, byAddon);
|
|
542
594
|
if (capName === "addon-routes") addonRouteProviders.set(spec.addonId, prov);
|
|
543
595
|
}
|
|
544
596
|
}
|
package/dist/addon-runner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as createUdsLoggerWithControl, I as LocalChildClient, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps, yt as resolveAddonClass } from "./manifest-python-deps-
|
|
1
|
+
import { F as createUdsLoggerWithControl, I as LocalChildClient, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps, yt as resolveAddonClass } from "./manifest-python-deps-BYIV8-rc.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
3
3
|
import { register } from "node:module";
|
|
4
4
|
import * as fs from "node:fs";
|
|
@@ -111,6 +111,17 @@ function isCapMethod(value) {
|
|
|
111
111
|
return typeof value === "function";
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
+
* Extract a string `addonId` out of arbitrary method args — the fallback
|
|
115
|
+
* provider-selection hint for calls that arrive WITHOUT the out-of-band
|
|
116
|
+
* `CapCallMessage.addonId` (e.g. a sibling child's unowned collection call
|
|
117
|
+
* forwarded by the parent, where the raw input still carries `addonId`).
|
|
118
|
+
*/
|
|
119
|
+
function extractArgsAddonId(args) {
|
|
120
|
+
if (args === null || typeof args !== "object") return void 0;
|
|
121
|
+
const raw = Reflect.get(args, "addonId");
|
|
122
|
+
return typeof raw === "string" ? raw : void 0;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
114
125
|
* Creates a unified dispatcher that routes a {@link CapCallInput} to either a
|
|
115
126
|
* device-scoped or singleton provider, then invokes the requested method.
|
|
116
127
|
*
|
|
@@ -119,12 +130,13 @@ function isCapMethod(value) {
|
|
|
119
130
|
function createChildCapDispatch(deps) {
|
|
120
131
|
return async (call) => {
|
|
121
132
|
const { capName, method, args, deviceId } = call;
|
|
133
|
+
const addonId = call.addonId ?? extractArgsAddonId(args);
|
|
122
134
|
let provider;
|
|
123
135
|
if (deviceId !== void 0) {
|
|
124
|
-
provider = deps.getDeviceProvider(capName, deviceId) ?? deps.getSingletonProvider(capName);
|
|
136
|
+
provider = deps.getDeviceProvider(capName, deviceId) ?? deps.getSingletonProvider(capName, addonId);
|
|
125
137
|
if (provider == null) throw new Error(`child-cap-dispatch: no provider for '${capName}' (device '${String(deviceId)}' or singleton)`);
|
|
126
138
|
} else {
|
|
127
|
-
provider = deps.getSingletonProvider(capName);
|
|
139
|
+
provider = deps.getSingletonProvider(capName, addonId);
|
|
128
140
|
if (provider == null) throw new Error(`child-cap-dispatch: no singleton provider for '${capName}'`);
|
|
129
141
|
}
|
|
130
142
|
const fn = Reflect.get(provider, method);
|
|
@@ -133,6 +145,51 @@ function createChildCapDispatch(deps) {
|
|
|
133
145
|
};
|
|
134
146
|
}
|
|
135
147
|
//#endregion
|
|
148
|
+
//#region src/kernel/moleculer/child-cap-descriptors.ts
|
|
149
|
+
/**
|
|
150
|
+
* Builds the {@link ChildCapDescriptor} array that the UDS child transport
|
|
151
|
+
* sends to the parent on registration.
|
|
152
|
+
*
|
|
153
|
+
* - Singleton caps come from the runner UNION manifest (all caps registered by
|
|
154
|
+
* every addon in this runner). One descriptor per unique (addonId, capName)
|
|
155
|
+
* pair, mode `'singleton'` and no `deviceId`. The `addonId` stamp lets the
|
|
156
|
+
* parent's manifest builder register each hosted addon's caps under the
|
|
157
|
+
* REAL addon id — a GROUPED runner (`execution.group`) hosts several addons,
|
|
158
|
+
* and deduplicating by capName alone would collapse two co-located providers
|
|
159
|
+
* of the same collection cap (e.g. `network-access` from cloudflare-tunnel
|
|
160
|
+
* AND tailscale-ingress) into one descriptor registered under a synthetic
|
|
161
|
+
* `addonId = childId` (the group name) — the "@camstack/addon-net-access"
|
|
162
|
+
* admin-UI bug.
|
|
163
|
+
* - Device-scoped caps come from the native-cap snapshot — one descriptor per
|
|
164
|
+
* (capName, deviceId) pair, mode `'collection'`, stamped with the owning
|
|
165
|
+
* addon's id.
|
|
166
|
+
*
|
|
167
|
+
* Pure module — extracted from `addon-runner.ts` (whose `main()` runs at
|
|
168
|
+
* import time) so it is unit-testable.
|
|
169
|
+
*/
|
|
170
|
+
function buildChildCapDescriptors(manifest, nativeCapSnapshot) {
|
|
171
|
+
const descriptors = [];
|
|
172
|
+
const seen = /* @__PURE__ */ new Set();
|
|
173
|
+
for (const entry of manifest) for (const capName of entry.capabilities) {
|
|
174
|
+
const key = `${entry.addonId}::${capName}`;
|
|
175
|
+
if (!seen.has(key)) {
|
|
176
|
+
seen.add(key);
|
|
177
|
+
descriptors.push({
|
|
178
|
+
capName,
|
|
179
|
+
mode: "singleton",
|
|
180
|
+
addonId: entry.addonId
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (const { capName, addonId, deviceIds } of nativeCapSnapshot) for (const deviceId of deviceIds) descriptors.push({
|
|
185
|
+
capName,
|
|
186
|
+
mode: "collection",
|
|
187
|
+
deviceId,
|
|
188
|
+
addonId
|
|
189
|
+
});
|
|
190
|
+
return descriptors;
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
136
193
|
//#region src/kernel/moleculer/child-addon-call-dispatch.ts
|
|
137
194
|
function isRecord$1(value) {
|
|
138
195
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -329,33 +386,6 @@ async function loadAddonModule(addonId, addonDir) {
|
|
|
329
386
|
AddonClass
|
|
330
387
|
};
|
|
331
388
|
}
|
|
332
|
-
/**
|
|
333
|
-
* Builds the {@link ChildCapDescriptor} array that the UDS child transport
|
|
334
|
-
* sends to the parent on registration.
|
|
335
|
-
*
|
|
336
|
-
* - Singleton caps come from the runner UNION manifest (all caps registered by
|
|
337
|
-
* every addon in this runner). One descriptor per unique cap name, mode
|
|
338
|
-
* `'singleton'` and no `deviceId`.
|
|
339
|
-
* - Device-scoped caps come from the native-cap snapshot — one descriptor per
|
|
340
|
-
* (capName, deviceId) pair, mode `'collection'`.
|
|
341
|
-
*/
|
|
342
|
-
function buildChildCapDescriptors(manifest, nativeCapSnapshot) {
|
|
343
|
-
const descriptors = [];
|
|
344
|
-
const seen = /* @__PURE__ */ new Set();
|
|
345
|
-
for (const entry of manifest) for (const capName of entry.capabilities) if (!seen.has(capName)) {
|
|
346
|
-
seen.add(capName);
|
|
347
|
-
descriptors.push({
|
|
348
|
-
capName,
|
|
349
|
-
mode: "singleton"
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
for (const { capName, deviceIds } of nativeCapSnapshot) for (const deviceId of deviceIds) descriptors.push({
|
|
353
|
-
capName,
|
|
354
|
-
mode: "collection",
|
|
355
|
-
deviceId
|
|
356
|
-
});
|
|
357
|
-
return descriptors;
|
|
358
|
-
}
|
|
359
389
|
async function main() {
|
|
360
390
|
const runnerId = process.env["CAMSTACK_RUNNER_ID"];
|
|
361
391
|
const addonsJson = process.env["CAMSTACK_RUNNER_ADDONS"];
|
|
@@ -423,14 +453,34 @@ async function main() {
|
|
|
423
453
|
const runnerManifest = [];
|
|
424
454
|
/**
|
|
425
455
|
* Aggregated singleton (system-scoped) providers across ALL addons in this
|
|
426
|
-
* runner — keyed
|
|
427
|
-
* singleton calls (no Moleculer).
|
|
428
|
-
*
|
|
456
|
+
* runner — keyed capName → (addonId → provider). Read by the UDS child-cap
|
|
457
|
+
* dispatch to resolve singleton calls (no Moleculer). The nested per-addon
|
|
458
|
+
* keying matters for GROUPED runners (`execution.group`): two co-located
|
|
459
|
+
* addons can register the SAME collection cap (e.g. `network-access` from
|
|
460
|
+
* cloudflare-tunnel AND tailscale-ingress) — a flat capName map would keep
|
|
461
|
+
* only the last-registered provider. Populated during the addon-init loop;
|
|
462
|
+
* a cap-call that arrives before its addon initialises is rejected by the
|
|
429
463
|
* dispatcher (the parent only routes caps the child has registered via the
|
|
430
464
|
* UDS `register`/`updateCaps` handshake).
|
|
431
465
|
*/
|
|
432
466
|
const allSingletonProviders = /* @__PURE__ */ new Map();
|
|
433
467
|
/**
|
|
468
|
+
* Resolve a singleton provider for the dispatcher: exact (capName, addonId)
|
|
469
|
+
* match when the hint names a hosted provider, else the cap's first
|
|
470
|
+
* registered provider (an unmatched `addonId` is method DATA — e.g.
|
|
471
|
+
* `addons.restartAddon({ addonId })` — not a routing key).
|
|
472
|
+
*/
|
|
473
|
+
const resolveSingletonProvider = (capName, addonId) => {
|
|
474
|
+
const byAddon = allSingletonProviders.get(capName);
|
|
475
|
+
if (!byAddon || byAddon.size === 0) return null;
|
|
476
|
+
if (addonId !== void 0) {
|
|
477
|
+
const exact = byAddon.get(addonId);
|
|
478
|
+
if (exact !== void 0) return exact;
|
|
479
|
+
}
|
|
480
|
+
const first = byAddon.values().next();
|
|
481
|
+
return first.done ? null : first.value;
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
434
484
|
* Per-addon route-provider map — keyed by addonId → the addon's live
|
|
435
485
|
* `addon-routes` provider object. Resolves `getRouteProvider(addonId)`
|
|
436
486
|
* per-addon rather than returning a single global provider. Populated
|
|
@@ -469,7 +519,7 @@ async function main() {
|
|
|
469
519
|
childId: runnerId,
|
|
470
520
|
caps: buildChildCapDescriptors(runnerManifest, getWorkerNativeCapSnapshot()),
|
|
471
521
|
dispatch: createChildCapDispatch({
|
|
472
|
-
getSingletonProvider: (capName) =>
|
|
522
|
+
getSingletonProvider: (capName, addonId) => resolveSingletonProvider(capName, addonId),
|
|
473
523
|
getDeviceProvider: (capName, deviceId) => getWorkerNativeCapProvider(capName, deviceId) ?? null
|
|
474
524
|
})
|
|
475
525
|
});
|
|
@@ -534,7 +584,9 @@ async function main() {
|
|
|
534
584
|
context.registerProvider(capName, reg.provider);
|
|
535
585
|
const prov = reg.provider;
|
|
536
586
|
if (prov !== null && typeof prov === "object" && !Array.isArray(prov)) {
|
|
537
|
-
allSingletonProviders.
|
|
587
|
+
const byAddon = allSingletonProviders.get(capName) ?? /* @__PURE__ */ new Map();
|
|
588
|
+
byAddon.set(spec.addonId, prov);
|
|
589
|
+
allSingletonProviders.set(capName, byAddon);
|
|
538
590
|
if (capName === "addon-routes") addonRouteProviders.set(spec.addonId, prov);
|
|
539
591
|
}
|
|
540
592
|
}
|
|
@@ -3,12 +3,12 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
+
const require_dist = require("../../dist-COIhbSao.js");
|
|
6
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
7
8
|
let node_fs = require("node:fs");
|
|
8
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
9
10
|
let node_path = require("node:path");
|
|
10
11
|
node_path = require_chunk.__toESM(node_path);
|
|
11
|
-
let _camstack_types = require("@camstack/types");
|
|
12
12
|
let node_crypto = require("node:crypto");
|
|
13
13
|
//#region src/builtins/addon-pages-aggregator/dedupe-pages.ts
|
|
14
14
|
/** Stable identity for an addon page, independent of which node reported it. */
|
|
@@ -85,7 +85,7 @@ var RETRY_BACKOFF_MS = [
|
|
|
85
85
|
* keepalive → "server unreachable").
|
|
86
86
|
*/
|
|
87
87
|
var PER_SOURCE_TIMEOUT_MS = 2500;
|
|
88
|
-
var AddonPagesAggregatorAddon = class extends
|
|
88
|
+
var AddonPagesAggregatorAddon = class extends require_dist.BaseAddon {
|
|
89
89
|
id = "addon-pages-aggregator";
|
|
90
90
|
resolvedPaths = null;
|
|
91
91
|
/**
|
|
@@ -107,7 +107,7 @@ var AddonPagesAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
107
107
|
const provider = { listPages: async () => this.aggregate() };
|
|
108
108
|
this.ctx.logger.info("Initialized — aggregating addon-pages-source providers");
|
|
109
109
|
return [{
|
|
110
|
-
capability:
|
|
110
|
+
capability: require_dist.addonPagesCapability,
|
|
111
111
|
provider
|
|
112
112
|
}];
|
|
113
113
|
}
|
|
@@ -189,7 +189,7 @@ var AddonPagesAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
189
189
|
type: "addon",
|
|
190
190
|
id: this.id
|
|
191
191
|
},
|
|
192
|
-
category:
|
|
192
|
+
category: require_dist.EventCategory.AddonPageReady,
|
|
193
193
|
data: {
|
|
194
194
|
addonId: sourceId,
|
|
195
195
|
recovered: true
|
|
@@ -199,7 +199,7 @@ var AddonPagesAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
199
199
|
this.ctx.logger.debug("addon-pages-source retry failed", { meta: {
|
|
200
200
|
sourceId,
|
|
201
201
|
attempt: attempt + 1,
|
|
202
|
-
error:
|
|
202
|
+
error: require_dist.errMsg(err)
|
|
203
203
|
} });
|
|
204
204
|
this.scheduleRetry(sourceId, attempt + 1);
|
|
205
205
|
}
|
|
@@ -249,7 +249,7 @@ var AddonPagesAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
249
249
|
const dataPath = typeof server["dataPath"] === "string" && server["dataPath"] ? server["dataPath"] : "camstack-data";
|
|
250
250
|
return { addonsDir: node_path.resolve(dataPath, "addons") };
|
|
251
251
|
} catch (err) {
|
|
252
|
-
this.ctx.logger.debug("Failed to read server.dataPath — falling back", { meta: { error:
|
|
252
|
+
this.ctx.logger.debug("Failed to read server.dataPath — falling back", { meta: { error: require_dist.errMsg(err) } });
|
|
253
253
|
return fallback;
|
|
254
254
|
}
|
|
255
255
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { $ as errMsg, et as BaseAddon, f as addonPagesCapability, st as EventCategory } from "../../dist-C7oOY4iL.mjs";
|
|
1
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
4
|
import * as path$1 from "node:path";
|
|
4
|
-
import { BaseAddon, EventCategory, addonPagesCapability, errMsg } from "@camstack/types";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
//#region src/builtins/addon-pages-aggregator/dedupe-pages.ts
|
|
7
7
|
/** Stable identity for an addon page, independent of which node reported it. */
|
|
@@ -3,12 +3,12 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
+
const require_dist = require("../../dist-COIhbSao.js");
|
|
6
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
7
8
|
let node_fs = require("node:fs");
|
|
8
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
9
10
|
let node_path = require("node:path");
|
|
10
11
|
node_path = require_chunk.__toESM(node_path);
|
|
11
|
-
let _camstack_types = require("@camstack/types");
|
|
12
12
|
let node_crypto = require("node:crypto");
|
|
13
13
|
//#region src/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.ts
|
|
14
14
|
/**
|
|
@@ -54,7 +54,7 @@ var RETRY_BACKOFF_MS = [
|
|
|
54
54
|
* out fast and serves its cached snapshot instead of blocking the dashboard.
|
|
55
55
|
*/
|
|
56
56
|
var PER_SOURCE_TIMEOUT_MS = 2500;
|
|
57
|
-
var AddonWidgetsAggregatorAddon = class extends
|
|
57
|
+
var AddonWidgetsAggregatorAddon = class extends require_dist.BaseAddon {
|
|
58
58
|
id = "addon-widgets-aggregator";
|
|
59
59
|
resolvedPaths = null;
|
|
60
60
|
/**
|
|
@@ -76,7 +76,7 @@ var AddonWidgetsAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
76
76
|
const provider = { listWidgets: async () => this.aggregate() };
|
|
77
77
|
this.ctx.logger.info("Initialized — aggregating addon-widgets-source providers");
|
|
78
78
|
return [{
|
|
79
|
-
capability:
|
|
79
|
+
capability: require_dist.addonWidgetsCapability,
|
|
80
80
|
provider
|
|
81
81
|
}];
|
|
82
82
|
}
|
|
@@ -172,7 +172,7 @@ var AddonWidgetsAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
172
172
|
type: "addon",
|
|
173
173
|
id: this.id
|
|
174
174
|
},
|
|
175
|
-
category:
|
|
175
|
+
category: require_dist.EventCategory.AddonWidgetReady,
|
|
176
176
|
data: {
|
|
177
177
|
addonId: publicAddonId,
|
|
178
178
|
recovered: true
|
|
@@ -182,7 +182,7 @@ var AddonWidgetsAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
182
182
|
this.ctx.logger.debug("addon-widgets-source retry failed", { meta: {
|
|
183
183
|
sourceId,
|
|
184
184
|
attempt: attempt + 1,
|
|
185
|
-
error:
|
|
185
|
+
error: require_dist.errMsg(err)
|
|
186
186
|
} });
|
|
187
187
|
this.scheduleRetry(sourceId, attempt + 1);
|
|
188
188
|
}
|
|
@@ -218,7 +218,7 @@ var AddonWidgetsAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
|
218
218
|
const dataPath = typeof server["dataPath"] === "string" && server["dataPath"] ? server["dataPath"] : "camstack-data";
|
|
219
219
|
return { addonsDir: node_path.resolve(dataPath, "addons") };
|
|
220
220
|
} catch (err) {
|
|
221
|
-
this.ctx.logger.debug("Failed to read server.dataPath — falling back", { meta: { error:
|
|
221
|
+
this.ctx.logger.debug("Failed to read server.dataPath — falling back", { meta: { error: require_dist.errMsg(err) } });
|
|
222
222
|
return fallback;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { $ as errMsg, et as BaseAddon, m as addonWidgetsCapability, st as EventCategory } from "../../dist-C7oOY4iL.mjs";
|
|
1
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
4
|
import * as path$1 from "node:path";
|
|
4
|
-
import { BaseAddon, EventCategory, addonWidgetsCapability, errMsg } from "@camstack/types";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
//#region src/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.ts
|
|
7
7
|
/**
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
|
|
6
|
+
const require_dist = require("../../dist-COIhbSao.js");
|
|
7
7
|
//#region src/builtins/alerts/alerts.addon.ts
|
|
8
8
|
var CATEGORY_MAPPINGS = {
|
|
9
9
|
"addon.crashed": {
|
|
@@ -106,7 +106,7 @@ var RATE_LIMIT_WINDOW_MS = 6e4;
|
|
|
106
106
|
var RATE_LIMIT_MAX_PER_SOURCE = 5;
|
|
107
107
|
/** Parse a raw settings record into a validated Alert. */
|
|
108
108
|
function parseAlert(record) {
|
|
109
|
-
return
|
|
109
|
+
return require_dist.AlertSchema.parse({
|
|
110
110
|
id: record.id,
|
|
111
111
|
...record.data
|
|
112
112
|
});
|
|
@@ -127,7 +127,7 @@ var EVENT_SOURCE = {
|
|
|
127
127
|
* Registers as an `alerts` collection provider and subscribes to
|
|
128
128
|
* important EventBus categories to create/update alerts automatically.
|
|
129
129
|
*/
|
|
130
|
-
var AlertCenterAddon = class extends
|
|
130
|
+
var AlertCenterAddon = class extends require_dist.BaseAddon {
|
|
131
131
|
unsubscribers = [];
|
|
132
132
|
constructor() {
|
|
133
133
|
super({ ...DEFAULT_CONFIG });
|
|
@@ -158,7 +158,7 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
158
158
|
this.unsubscribers = unsubs;
|
|
159
159
|
this.ctx.logger.info("Initialized", { meta: { trackedCategoriesCount: TRACKED_CATEGORIES.length } });
|
|
160
160
|
return [{
|
|
161
|
-
capability:
|
|
161
|
+
capability: require_dist.alertsCapability,
|
|
162
162
|
provider: alertsProvider
|
|
163
163
|
}];
|
|
164
164
|
}
|
|
@@ -250,11 +250,11 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
250
250
|
await this.handleModelDownload(event);
|
|
251
251
|
return;
|
|
252
252
|
}
|
|
253
|
-
if (category ===
|
|
253
|
+
if (category === require_dist.EventCategory.AddonLoadFailed) {
|
|
254
254
|
await this.handleAddonLoadFailed(event);
|
|
255
255
|
return;
|
|
256
256
|
}
|
|
257
|
-
if (category ===
|
|
257
|
+
if (category === require_dist.EventCategory.AddonLoadRecovered) {
|
|
258
258
|
await this.handleAddonLoadRecovered(event);
|
|
259
259
|
return;
|
|
260
260
|
}
|
|
@@ -282,7 +282,7 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
282
282
|
} catch (err) {
|
|
283
283
|
this.ctx.logger?.warn("Failed to handle event", { meta: {
|
|
284
284
|
category,
|
|
285
|
-
error:
|
|
285
|
+
error: require_dist.errMsg(err)
|
|
286
286
|
} });
|
|
287
287
|
}
|
|
288
288
|
}
|
|
@@ -327,7 +327,7 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
327
327
|
} else {
|
|
328
328
|
const alert = {
|
|
329
329
|
id: stableId,
|
|
330
|
-
category:
|
|
330
|
+
category: require_dist.EventCategory.ModelDownloadProgress,
|
|
331
331
|
severity: "info",
|
|
332
332
|
title: "Model download",
|
|
333
333
|
message: `Downloading model "${modelId}" — ${String(Math.round(progress))}%`,
|
|
@@ -409,7 +409,7 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
409
409
|
const message = errorObj?.message ? `${addonId ? `${addonId} — ` : ""}${errorObj.message}` : `Addon ${addonId ?? packageName} failed to load`;
|
|
410
410
|
const alert = {
|
|
411
411
|
id: stableId,
|
|
412
|
-
category:
|
|
412
|
+
category: require_dist.EventCategory.AddonLoadFailed,
|
|
413
413
|
severity: "error",
|
|
414
414
|
title: `Addon failed: ${addonId ?? packageName}`,
|
|
415
415
|
message,
|
|
@@ -445,7 +445,7 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
445
445
|
} catch (err) {
|
|
446
446
|
this.ctx.logger?.debug("AddonLoadRecovered: dismiss failed (likely already dismissed)", { meta: {
|
|
447
447
|
stableId,
|
|
448
|
-
error:
|
|
448
|
+
error: require_dist.errMsg(err)
|
|
449
449
|
} });
|
|
450
450
|
}
|
|
451
451
|
}
|
|
@@ -538,7 +538,7 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
538
538
|
});
|
|
539
539
|
}
|
|
540
540
|
emitAlertCreatedEvent(alert) {
|
|
541
|
-
this.ctx.eventBus?.emit(
|
|
541
|
+
this.ctx.eventBus?.emit(require_dist.createEvent("alert.created", EVENT_SOURCE, {
|
|
542
542
|
id: alert.id,
|
|
543
543
|
category: alert.category,
|
|
544
544
|
severity: alert.severity,
|
|
@@ -549,7 +549,7 @@ var AlertCenterAddon = class extends _camstack_types.BaseAddon {
|
|
|
549
549
|
emitAlertUpdatedEvent(alertId, patch) {
|
|
550
550
|
const patchRecord = {};
|
|
551
551
|
for (const [key, value] of Object.entries(patch)) patchRecord[key] = value;
|
|
552
|
-
this.ctx.eventBus?.emit(
|
|
552
|
+
this.ctx.eventBus?.emit(require_dist.createEvent("alert.updated", EVENT_SOURCE, {
|
|
553
553
|
alertId,
|
|
554
554
|
patch: patchRecord
|
|
555
555
|
}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as errMsg, et as BaseAddon, h as alertsCapability, mt as createEvent, n as AlertSchema, st as EventCategory } from "../../dist-C7oOY4iL.mjs";
|
|
2
2
|
//#region src/builtins/alerts/alerts.addon.ts
|
|
3
3
|
var CATEGORY_MAPPINGS = {
|
|
4
4
|
"addon.crashed": {
|
|
@@ -3,13 +3,13 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
+
const require_dist = require("../../dist-COIhbSao.js");
|
|
6
7
|
let node_fs = require("node:fs");
|
|
7
8
|
let node_fs$1 = require_chunk.__toESM(node_fs, 1);
|
|
8
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
9
10
|
let node_path = require("node:path");
|
|
10
11
|
let node_path$1 = require_chunk.__toESM(node_path, 1);
|
|
11
12
|
node_path = require_chunk.__toESM(node_path);
|
|
12
|
-
let _camstack_types = require("@camstack/types");
|
|
13
13
|
let node_crypto = require("node:crypto");
|
|
14
14
|
let node_fs_promises = require("node:fs/promises");
|
|
15
15
|
let node_fs_promises$1 = require_chunk.__toESM(node_fs_promises, 1);
|
|
@@ -4538,7 +4538,7 @@ function formatArchiveLabel(destinationId, createdAt, suffix) {
|
|
|
4538
4538
|
var UPLOAD_CHUNK_BYTES = 8 * 1024 * 1024;
|
|
4539
4539
|
/** Manifest id this addon registers under (must match `package.json`). */
|
|
4540
4540
|
var ORCHESTRATOR_ADDON_ID = "backup-orchestrator";
|
|
4541
|
-
var BackupOrchestratorAddon = class extends
|
|
4541
|
+
var BackupOrchestratorAddon = class extends require_dist.BaseAddon {
|
|
4542
4542
|
systemBackup = null;
|
|
4543
4543
|
policies = null;
|
|
4544
4544
|
dataDir = "";
|
|
@@ -4582,7 +4582,7 @@ var BackupOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
4582
4582
|
policies: this.policies !== null
|
|
4583
4583
|
} });
|
|
4584
4584
|
return [{
|
|
4585
|
-
capability:
|
|
4585
|
+
capability: require_dist.backupCapability,
|
|
4586
4586
|
provider
|
|
4587
4587
|
}];
|
|
4588
4588
|
}
|
|
@@ -4783,7 +4783,7 @@ var BackupOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
4783
4783
|
type: "addon",
|
|
4784
4784
|
id: "backup-orchestrator"
|
|
4785
4785
|
},
|
|
4786
|
-
category:
|
|
4786
|
+
category: require_dist.EventCategory.BackupCompleted,
|
|
4787
4787
|
data: {
|
|
4788
4788
|
destinationId: dest.id,
|
|
4789
4789
|
backupId: stored.id,
|
|
@@ -5034,7 +5034,7 @@ var BackupOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
5034
5034
|
type: "addon",
|
|
5035
5035
|
id: "backup-orchestrator"
|
|
5036
5036
|
},
|
|
5037
|
-
category:
|
|
5037
|
+
category: require_dist.EventCategory.BackupRestored,
|
|
5038
5038
|
data: {
|
|
5039
5039
|
backupId: input.backupId,
|
|
5040
5040
|
destinationId: input.destinationId
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { et as BaseAddon, st as EventCategory, v as backupCapability } from "../../dist-C7oOY4iL.mjs";
|
|
1
2
|
import * as fs from "node:fs";
|
|
2
3
|
import Kt from "node:fs";
|
|
3
4
|
import * as path$1 from "node:path";
|
|
4
5
|
import path, { basename, join, posix, win32 } from "node:path";
|
|
5
|
-
import { BaseAddon, EventCategory, backupCapability } from "@camstack/types";
|
|
6
6
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import * as fsp from "node:fs/promises";
|
|
8
8
|
import jn from "node:fs/promises";
|