@camstack/server 1.0.1 → 1.0.3
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/api/addon-upload.js +17 -3
- package/dist/api/bridge-addons.router.js +1 -1
- package/dist/api/capabilities.router.js +3 -3
- package/dist/api/core/cap-providers.js +3 -3
- package/dist/api/core/logs.router.js +1 -1
- package/dist/api/core/notifications.router.js +1 -1
- package/dist/api/core/repl.router.js +1 -1
- package/dist/api/health/health.routes.js +7 -2
- package/dist/api/trpc/cap-route-error-formatter.js +3 -3
- package/dist/api/trpc/core-cap-bridge.js +2 -2
- package/dist/api/trpc/generated-cap-routers.js +4 -4
- package/dist/boot/boot-config.js +2 -2
- package/dist/boot/post-boot.service.js +2 -2
- package/dist/core/addon/addon-call-gateway.js +1 -1
- package/dist/core/addon/addon-package.service.js +37 -43
- package/dist/core/addon/addon-registry.service.js +67 -52
- package/dist/core/addon-bridge/addon-bridge.service.js +6 -6
- package/dist/core/addon-pages/addon-pages.service.js +1 -1
- package/dist/core/auth/auth.service.js +2 -2
- package/dist/core/config/config.schema.js +4 -4
- package/dist/core/config/config.service.js +2 -2
- package/dist/core/events/event-bus.service.js +2 -2
- package/dist/core/feature/feature.service.js +2 -2
- package/dist/core/lifecycle/lifecycle-state-machine.js +3 -3
- package/dist/core/logging/log-ring-buffer.js +3 -3
- package/dist/core/logging/logging.service.js +3 -3
- package/dist/core/logging/scoped-logger.js +3 -3
- package/dist/core/moleculer/cap-route-authority.js +2 -2
- package/dist/core/moleculer/moleculer.service.js +29 -23
- package/dist/core/network/network-quality.service.js +2 -2
- package/dist/core/notification/notification-wrapper.service.js +2 -2
- package/dist/core/notification/toast-wrapper.service.js +2 -2
- package/dist/core/repl/repl-engine.service.js +2 -2
- package/dist/core/storage/fs-storage-backend.js +3 -3
- package/dist/core/storage/storage-location-manager.js +3 -3
- package/dist/core/storage/storage.service.js +2 -2
- package/dist/launcher.js +23 -12
- package/dist/main.js +14 -5
- package/package.json +2 -3
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MoleculerService = void 0;
|
|
4
4
|
exports.buildChildUdsManifest = buildChildUdsManifest;
|
|
5
|
-
const
|
|
5
|
+
const system_1 = require("@camstack/system");
|
|
6
6
|
const cap_call_fn_js_1 = require("./cap-call-fn.js");
|
|
7
7
|
const cap_route_authority_js_1 = require("./cap-route-authority.js");
|
|
8
8
|
const types_1 = require("@camstack/types");
|
|
@@ -23,7 +23,7 @@ class MoleculerService {
|
|
|
23
23
|
* D3 authority: union of every node's manifest delivered via
|
|
24
24
|
* `$hub.registerNode`. Populated by `onRegisterNode` in `hubDeps`.
|
|
25
25
|
*/
|
|
26
|
-
nodeRegistry = new
|
|
26
|
+
nodeRegistry = new system_1.HubNodeRegistry();
|
|
27
27
|
nodeCallFns = new Map();
|
|
28
28
|
/**
|
|
29
29
|
* D3: callback invoked whenever a bare-ID agent node completes the
|
|
@@ -107,7 +107,7 @@ class MoleculerService {
|
|
|
107
107
|
// `no-unsafe-assignment`. Going via `unknown` documents the boundary.
|
|
108
108
|
this.clusterSecret =
|
|
109
109
|
process.env['CAMSTACK_CLUSTER_SECRET'] ?? this.config.get('cluster.secret');
|
|
110
|
-
const broker = (0,
|
|
110
|
+
const broker = (0, system_1.createBroker)({
|
|
111
111
|
nodeID: 'hub',
|
|
112
112
|
mode: 'hub',
|
|
113
113
|
logLevel: this.config.get('moleculer.logLevel') ?? 'warn',
|
|
@@ -182,12 +182,18 @@ class MoleculerService {
|
|
|
182
182
|
this.removeNodeFromRegistry(nodeId);
|
|
183
183
|
},
|
|
184
184
|
expectedClusterSecretHash: this.clusterSecret
|
|
185
|
-
? (0,
|
|
185
|
+
? (0, system_1.hashClusterSecret)(this.clusterSecret)
|
|
186
186
|
: undefined,
|
|
187
187
|
};
|
|
188
|
-
const hubService = (0,
|
|
188
|
+
const hubService = (0, system_1.createHubService)(hubDeps);
|
|
189
189
|
this.brokerSafe.createService(hubService);
|
|
190
|
-
|
|
190
|
+
// Forked addon-runner children must root their storage (addons-data,
|
|
191
|
+
// the sqlite settings DB) under the SAME data path as the hub's main
|
|
192
|
+
// process. The configured key is `server.dataPath` (populated from the
|
|
193
|
+
// CAMSTACK_DATA env var); reading the non-existent `dataDir` key here
|
|
194
|
+
// made every child silently fall back to the relative `camstack-data`,
|
|
195
|
+
// so the DB escaped the mounted data dir in container deployments.
|
|
196
|
+
const dataDir = this.config.get('server.dataPath') ?? 'camstack-data';
|
|
191
197
|
// UDS local transport: the hub hosts a LocalChildRegistry so its
|
|
192
198
|
// forked addon-runners route cap calls directly over a Unix-domain
|
|
193
199
|
// socket instead of through Moleculer. The broker stays available as
|
|
@@ -207,7 +213,7 @@ class MoleculerService {
|
|
|
207
213
|
// capabilities the resolver can see. Before F0 this fell through
|
|
208
214
|
// UDS_NO_ROUTE → the child's own brokerTransportLink; F1+F2 removes that
|
|
209
215
|
// child broker, so the parent must own this path.
|
|
210
|
-
const onUnownedCall = (0,
|
|
216
|
+
const onUnownedCall = (0, system_1.createParentUnownedCallHandler)({
|
|
211
217
|
getResolver: () => this.resolver,
|
|
212
218
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- moleculer types unresolvable; see `broker` getter docstring
|
|
213
219
|
broker: this.broker,
|
|
@@ -233,8 +239,8 @@ class MoleculerService {
|
|
|
233
239
|
: undefined),
|
|
234
240
|
},
|
|
235
241
|
});
|
|
236
|
-
const registry = new
|
|
237
|
-
server: (0,
|
|
242
|
+
const registry = new system_1.LocalChildRegistry({
|
|
243
|
+
server: (0, system_1.createLocalTransport)().createServer(nodeId),
|
|
238
244
|
onUnownedCall,
|
|
239
245
|
logger: {
|
|
240
246
|
info: (msg, meta) => logger.info(msg, meta !== null && meta !== undefined
|
|
@@ -278,7 +284,7 @@ class MoleculerService {
|
|
|
278
284
|
// This runs in PARALLEL with the existing $hub.log / onLog broker path —
|
|
279
285
|
// both stay active until Phase F removes the broker path.
|
|
280
286
|
registry.onChildLog((childId, entry) => {
|
|
281
|
-
this.logging.writeFromWorker((0,
|
|
287
|
+
this.logging.writeFromWorker((0, system_1.udsChildLogToWorkerEntry)(childId, entry));
|
|
282
288
|
});
|
|
283
289
|
// D1: answer readiness-snapshot requests from UDS children so they can
|
|
284
290
|
// hydrate without a `$readiness.getSnapshot` Moleculer call.
|
|
@@ -289,7 +295,7 @@ class MoleculerService {
|
|
|
289
295
|
// Keep `$readiness.getSnapshot` intact — Phase F removes it.
|
|
290
296
|
registry.onReadinessSnapshotRequest(() => this.readinessRegistry.getSnapshotForTransport());
|
|
291
297
|
this.localChildRegistry = registry;
|
|
292
|
-
parentUdsPath = (0,
|
|
298
|
+
parentUdsPath = (0, system_1.localEndpointPath)(nodeId);
|
|
293
299
|
logger.info('UDS child registry listening', { meta: { path: parentUdsPath } });
|
|
294
300
|
}
|
|
295
301
|
catch (err) {
|
|
@@ -297,7 +303,7 @@ class MoleculerService {
|
|
|
297
303
|
meta: { err: err instanceof Error ? err.message : String(err) },
|
|
298
304
|
});
|
|
299
305
|
}
|
|
300
|
-
const processService = (0,
|
|
306
|
+
const processService = (0, system_1.createProcessService)(this.brokerSafe.nodeID, dataDir, undefined, undefined, parentUdsPath);
|
|
301
307
|
this.brokerSafe.createService(processService);
|
|
302
308
|
// $addonHost — REMOVED (Sprint 6). Three-level settings are now
|
|
303
309
|
// served by the `addon-settings` singleton capability. Per-addon
|
|
@@ -313,19 +319,19 @@ class MoleculerService {
|
|
|
313
319
|
// handshake, not dynamically after post-start `createService()`.
|
|
314
320
|
// Without this, cross-node broadcasts (camstack.evt.<category>) would
|
|
315
321
|
// arrive unreliably for the first ~10s after each node joins.
|
|
316
|
-
(0,
|
|
322
|
+
(0, system_1.registerEventBusService)(this.broker);
|
|
317
323
|
// Register the hub-authoritative `$readiness.getSnapshot` service
|
|
318
324
|
// BEFORE broker.start() so workers / agents see it in their initial
|
|
319
325
|
// INFO packet — post-start `createService` calls propagate via
|
|
320
326
|
// heartbeat (several seconds) and would force workers to poll.
|
|
321
|
-
this.brokerSafe.createService((0,
|
|
327
|
+
this.brokerSafe.createService((0, system_1.createReadinessServiceForRegistry)(this.readinessRegistry));
|
|
322
328
|
// Register the hub-authoritative `$stream-probe` service —
|
|
323
329
|
// workers route RTSP probe + field-probe through this action when
|
|
324
330
|
// the tRPC WSS link isn't available (default path for forked
|
|
325
331
|
// workers). Keeps ffprobe + HTTP-reachability as a single
|
|
326
332
|
// hub-side implementation; see `createStreamProbeBrokerService`
|
|
327
333
|
// for the shape.
|
|
328
|
-
this.brokerSafe.createService((0,
|
|
334
|
+
this.brokerSafe.createService((0, system_1.createStreamProbeBrokerService)({
|
|
329
335
|
probe: (url, options) => this.streamProbe.probe(url, options),
|
|
330
336
|
probeField: (key, value) => this.streamProbe.probeField(key, value),
|
|
331
337
|
}));
|
|
@@ -334,7 +340,7 @@ class MoleculerService {
|
|
|
334
340
|
// returns the backend list for whichever host the caller targets.
|
|
335
341
|
// Admin UI uses this to show per-agent hwaccel info on the
|
|
336
342
|
// pipeline / NodeDetail pages.
|
|
337
|
-
this.brokerSafe.createService((0,
|
|
343
|
+
this.brokerSafe.createService((0, system_1.createHwAccelService)((0, system_1.createKernelHwAccel)()));
|
|
338
344
|
await this.brokerSafe.start();
|
|
339
345
|
logger.info('Moleculer broker started (TCP transport)');
|
|
340
346
|
// Construct the CapRouteResolver now that both the broker and
|
|
@@ -343,7 +349,7 @@ class MoleculerService {
|
|
|
343
349
|
// children connecting/disconnecting after this point are picked up
|
|
344
350
|
// correctly. The localChildRegistry reference is captured and may be
|
|
345
351
|
// null if UDS failed to start.
|
|
346
|
-
this.resolver = new
|
|
352
|
+
this.resolver = new system_1.CapRouteResolver({
|
|
347
353
|
hubNodeId: this.brokerSafe.nodeID,
|
|
348
354
|
broker: this.brokerSafe,
|
|
349
355
|
hubLocalRegistry: this.localChildRegistry,
|
|
@@ -370,7 +376,7 @@ class MoleculerService {
|
|
|
370
376
|
// shared broker bus and broker.broadcast is live.
|
|
371
377
|
if (this.localChildRegistry !== null) {
|
|
372
378
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
373
|
-
this.udsEventBridgeDispose = (0,
|
|
379
|
+
this.udsEventBridgeDispose = (0, system_1.createUdsEventBridge)({
|
|
374
380
|
registry: this.localChildRegistry,
|
|
375
381
|
parentBus: this.eventBus,
|
|
376
382
|
parentNodeId: hubNodeId,
|
|
@@ -447,7 +453,7 @@ class MoleculerService {
|
|
|
447
453
|
return await resolver.dispatch(route, methodName, params);
|
|
448
454
|
}
|
|
449
455
|
catch (err) {
|
|
450
|
-
if (err instanceof
|
|
456
|
+
if (err instanceof system_1.CapRouteError &&
|
|
451
457
|
(err.reason === 'no-provider' || err.reason === 'node-offline')) {
|
|
452
458
|
// Resolver couldn't find the cap — try the legacy callFn store as a
|
|
453
459
|
// fallback. This covers caps registered in nodeCallFns (e.g. agent
|
|
@@ -482,7 +488,7 @@ class MoleculerService {
|
|
|
482
488
|
if (callFn) {
|
|
483
489
|
return callFn(methodName, params);
|
|
484
490
|
}
|
|
485
|
-
throw new
|
|
491
|
+
throw new system_1.CapRouteError(capabilityName, methodName, {
|
|
486
492
|
reason: 'no-provider',
|
|
487
493
|
nodeId,
|
|
488
494
|
rejected: [
|
|
@@ -562,7 +568,7 @@ class MoleculerService {
|
|
|
562
568
|
for (const addon of manifest) {
|
|
563
569
|
const registryKey = registryKeyFor(addon.addonId);
|
|
564
570
|
for (const capName of addon.capabilities) {
|
|
565
|
-
if ((0,
|
|
571
|
+
if ((0, system_1.isInfraCapability)(capName))
|
|
566
572
|
continue;
|
|
567
573
|
const capDef = registry.getDefinition(capName);
|
|
568
574
|
if (!capDef)
|
|
@@ -614,7 +620,7 @@ class MoleculerService {
|
|
|
614
620
|
udsChildId,
|
|
615
621
|
getLocalChildRegistry: () => this.localChildRegistry,
|
|
616
622
|
getResolver: () => this.resolver,
|
|
617
|
-
legacyBrokerCall: (method, methodParams, targetNode) => (0,
|
|
623
|
+
legacyBrokerCall: (method, methodParams, targetNode) => (0, system_1.callWithServiceDiscovery)(this.brokerSafe, addonId, (0, system_1.capActionName)(addonId, capName, method, false), (0, system_1.serializeTypedArrays)(methodParams), { nodeID: targetNode, timeout: 60_000 }),
|
|
618
624
|
onUdsRoute: (cap) => {
|
|
619
625
|
if (!this.udsRoutedCaps.has(cap)) {
|
|
620
626
|
this.udsRoutedCaps.add(cap);
|
|
@@ -778,7 +784,7 @@ class MoleculerService {
|
|
|
778
784
|
// Resolver found a route — proxy is reachable.
|
|
779
785
|
}
|
|
780
786
|
catch (err) {
|
|
781
|
-
if (err instanceof
|
|
787
|
+
if (err instanceof system_1.CapRouteError &&
|
|
782
788
|
(err.reason === 'no-provider' || err.reason === 'node-offline')) {
|
|
783
789
|
// Check legacy callFn store and hub-local child fallbacks.
|
|
784
790
|
if (!this.isReachableViaLegacy(nodeId, capabilityName))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NetworkQualityService = void 0;
|
|
4
|
-
const
|
|
5
|
-
class NetworkQualityService extends
|
|
4
|
+
const system_1 = require("@camstack/system");
|
|
5
|
+
class NetworkQualityService extends system_1.NetworkQualityTracker {
|
|
6
6
|
}
|
|
7
7
|
exports.NetworkQualityService = NetworkQualityService;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NotificationServiceWrapper = void 0;
|
|
4
|
-
const
|
|
4
|
+
const system_1 = require("@camstack/system");
|
|
5
5
|
/**
|
|
6
6
|
* NestJS-injectable wrapper around the core NotificationService.
|
|
7
7
|
*
|
|
@@ -18,7 +18,7 @@ class NotificationServiceWrapper {
|
|
|
18
18
|
}
|
|
19
19
|
get service() {
|
|
20
20
|
if (!this._service) {
|
|
21
|
-
this._service = new
|
|
21
|
+
this._service = new system_1.NotificationService(this.logging.createLogger('notifications'));
|
|
22
22
|
const registry = this.caps.getRegistry();
|
|
23
23
|
if (registry) {
|
|
24
24
|
this._service.setRegistry(registry);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ToastServiceWrapper = void 0;
|
|
4
|
-
const
|
|
4
|
+
const system_1 = require("@camstack/system");
|
|
5
5
|
/**
|
|
6
6
|
* NestJS-injectable wrapper around the core ToastService.
|
|
7
7
|
*
|
|
8
8
|
* Provides toast broadcasting to connected UI clients.
|
|
9
9
|
*/
|
|
10
10
|
class ToastServiceWrapper {
|
|
11
|
-
_service = new
|
|
11
|
+
_service = new system_1.ToastService();
|
|
12
12
|
get service() {
|
|
13
13
|
return this._service;
|
|
14
14
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ReplEngineService = void 0;
|
|
4
|
-
const
|
|
4
|
+
const system_1 = require("@camstack/system");
|
|
5
5
|
const types_1 = require("@camstack/types");
|
|
6
|
-
class ReplEngineService extends
|
|
6
|
+
class ReplEngineService extends system_1.ReplEngine {
|
|
7
7
|
/**
|
|
8
8
|
* Lazily-instantiated `SystemMirror` shared across REPL sessions.
|
|
9
9
|
* Holds a single warm-boot mirror that every `sm.getDeviceById(id)`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FsStorageBackend = void 0;
|
|
4
|
-
// Re-export from @camstack/
|
|
5
|
-
var
|
|
6
|
-
Object.defineProperty(exports, "FsStorageBackend", { enumerable: true, get: function () { return
|
|
4
|
+
// Re-export from @camstack/system
|
|
5
|
+
var system_1 = require("@camstack/system");
|
|
6
|
+
Object.defineProperty(exports, "FsStorageBackend", { enumerable: true, get: function () { return system_1.FsStorageBackend; } });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StorageLocationManager = void 0;
|
|
4
|
-
// Re-export from @camstack/
|
|
5
|
-
var
|
|
6
|
-
Object.defineProperty(exports, "StorageLocationManager", { enumerable: true, get: function () { return
|
|
4
|
+
// Re-export from @camstack/system
|
|
5
|
+
var system_1 = require("@camstack/system");
|
|
6
|
+
Object.defineProperty(exports, "StorageLocationManager", { enumerable: true, get: function () { return system_1.StorageLocationManager; } });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StorageService = void 0;
|
|
4
|
-
const
|
|
5
|
-
class StorageService extends
|
|
4
|
+
const system_1 = require("@camstack/system");
|
|
5
|
+
class StorageService extends system_1.StorageManager {
|
|
6
6
|
}
|
|
7
7
|
exports.StorageService = StorageService;
|
package/dist/launcher.js
CHANGED
|
@@ -36,18 +36,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
/**
|
|
37
37
|
* Launcher -- entry point that ensures required addons are installed
|
|
38
38
|
* before main.ts loads. This solves the chicken-and-egg problem:
|
|
39
|
-
* main.ts has static imports from @camstack/
|
|
39
|
+
* main.ts has static imports from @camstack/system, but core lives in
|
|
40
40
|
* data/addons/ and may not exist on first boot.
|
|
41
41
|
*
|
|
42
42
|
* 1. Run AddonInstaller.ensureRequiredPackages (zero core dependencies)
|
|
43
|
-
* 2. Create symlinks so @camstack/
|
|
44
|
-
* 3. Dynamically import main.ts (which has static @camstack/
|
|
43
|
+
* 2. Create symlinks so @camstack/system resolves from data/addons/
|
|
44
|
+
* 3. Dynamically import main.ts (which has static @camstack/system imports)
|
|
45
45
|
*/
|
|
46
46
|
const fs = __importStar(require("node:fs"));
|
|
47
47
|
const path = __importStar(require("node:path"));
|
|
48
48
|
const tar = __importStar(require("tar"));
|
|
49
49
|
const yaml = __importStar(require("js-yaml"));
|
|
50
|
-
const
|
|
50
|
+
const system_1 = require("@camstack/system");
|
|
51
51
|
/** Path of the manifest file embedded inside every archive. */
|
|
52
52
|
const ARCHIVE_MANIFEST_NAME = '.camstack-backup-manifest.json';
|
|
53
53
|
/** Resolve the data directory from env or default */
|
|
@@ -135,12 +135,17 @@ function parseInstallSource(value) {
|
|
|
135
135
|
/**
|
|
136
136
|
* Locate + parse `config.yaml` relative to the data dir. Returns the
|
|
137
137
|
* raw object on success, `null` when missing or unparseable. The
|
|
138
|
-
* launcher uses this BEFORE @camstack/
|
|
138
|
+
* launcher uses this BEFORE @camstack/system is imported so it must stay
|
|
139
139
|
* free of core deps — just js-yaml + the kernel's bootstrapSchema for
|
|
140
140
|
* shape validation by the call site.
|
|
141
141
|
*/
|
|
142
142
|
function readConfigYaml(dataDir) {
|
|
143
|
+
// CONFIG_PATH (when set) is the authoritative location and must win — it
|
|
144
|
+
// is what main.ts reads/writes. Without this the launcher would miss the
|
|
145
|
+
// `bootstrap.*` section of a relocated config.yaml (e.g. a separate
|
|
146
|
+
// /config mount) and silently fall back to defaults.
|
|
143
147
|
const candidates = [
|
|
148
|
+
...(process.env.CONFIG_PATH ? [path.resolve(process.env.CONFIG_PATH)] : []),
|
|
144
149
|
path.resolve(dataDir, '..', 'config.yaml'),
|
|
145
150
|
path.resolve(dataDir, 'config.yaml'),
|
|
146
151
|
path.resolve(process.cwd(), 'config.yaml'),
|
|
@@ -169,7 +174,7 @@ function readBootstrapInstallSource(dataDir) {
|
|
|
169
174
|
const raw = readConfigYaml(dataDir);
|
|
170
175
|
if (raw === null)
|
|
171
176
|
return undefined;
|
|
172
|
-
const validation =
|
|
177
|
+
const validation = system_1.bootstrapSchema.safeParse(raw);
|
|
173
178
|
if (!validation.success)
|
|
174
179
|
return undefined;
|
|
175
180
|
return parseInstallSource(validation.data.bootstrap.installSource);
|
|
@@ -181,7 +186,7 @@ function readBootstrapInstallSource(dataDir) {
|
|
|
181
186
|
* kernel's REQUIRED_PACKAGES constant).
|
|
182
187
|
*
|
|
183
188
|
* Inline YAML parse rather than going through ConfigManager: the
|
|
184
|
-
* launcher runs before @camstack/
|
|
189
|
+
* launcher runs before @camstack/system is imported and must stay free of
|
|
185
190
|
* core dependencies. Just yaml.load + bootstrapSchema (kernel) for
|
|
186
191
|
* shape validation.
|
|
187
192
|
*/
|
|
@@ -189,7 +194,7 @@ function readBootstrapRequiredAddons(dataDir) {
|
|
|
189
194
|
const raw = readConfigYaml(dataDir);
|
|
190
195
|
if (raw === null)
|
|
191
196
|
return null;
|
|
192
|
-
const validation =
|
|
197
|
+
const validation = system_1.bootstrapSchema.safeParse(raw);
|
|
193
198
|
if (!validation.success) {
|
|
194
199
|
console.warn(`[launcher] config.yaml failed bootstrapSchema validation: ${validation.error.message}`);
|
|
195
200
|
return null;
|
|
@@ -198,7 +203,13 @@ function readBootstrapRequiredAddons(dataDir) {
|
|
|
198
203
|
}
|
|
199
204
|
async function launch() {
|
|
200
205
|
const dataDir = DATA_DIR;
|
|
201
|
-
|
|
206
|
+
// Addons live in a real `node_modules`-shaped dir so ESM `import` resolves
|
|
207
|
+
// every @camstack/* (system + addon-*) from there via the standard
|
|
208
|
+
// walk-up — NODE_PATH only covers CJS, which is why forked ESM addon
|
|
209
|
+
// runners couldn't resolve `@camstack/types` from a plain `addons/` dir.
|
|
210
|
+
// `CAMSTACK_ADDONS_DIR` lets the Docker/Electron images point at their
|
|
211
|
+
// node_modules path (e.g. /data/node_modules); dev keeps `<dataDir>/addons`.
|
|
212
|
+
const addonsDir = process.env['CAMSTACK_ADDONS_DIR'] ?? path.resolve(dataDir, 'addons');
|
|
202
213
|
// Restore must happen first so the snapshot's `addons/` is what the
|
|
203
214
|
// installer + symlink step pick up — otherwise we'd install a stale
|
|
204
215
|
// set and overwrite it a step later.
|
|
@@ -230,14 +241,14 @@ async function launch() {
|
|
|
230
241
|
console.log(`[launcher] Using bundled addons from ${bundledDir}`);
|
|
231
242
|
}
|
|
232
243
|
else if (explicitSource === 'local' || explicitSource === 'symlink') {
|
|
233
|
-
workspaceDir = (0,
|
|
244
|
+
workspaceDir = (0, system_1.detectWorkspacePackagesDir)(__dirname);
|
|
234
245
|
if (workspaceDir === null) {
|
|
235
246
|
console.warn(`[launcher] installSource=${explicitSource} requested but no workspace ` +
|
|
236
247
|
`packages/ dir found from ${__dirname} — falling back to 'npm'`);
|
|
237
248
|
resolvedSource = 'npm';
|
|
238
249
|
}
|
|
239
250
|
}
|
|
240
|
-
const installer = new
|
|
251
|
+
const installer = new system_1.AddonInstaller({
|
|
241
252
|
addonsDir,
|
|
242
253
|
workspacePackagesDir: workspaceDir ?? undefined,
|
|
243
254
|
installSource: resolvedSource,
|
|
@@ -291,7 +302,7 @@ async function launch() {
|
|
|
291
302
|
Module._initPaths();
|
|
292
303
|
console.log(`[launcher] NODE_PATH extended with: ${extraNodePaths.join(sep)}`);
|
|
293
304
|
}
|
|
294
|
-
// Now safe to load main.ts (which has static imports from @camstack/
|
|
305
|
+
// Now safe to load main.ts (which has static imports from @camstack/system)
|
|
295
306
|
console.log('[launcher] Starting server...');
|
|
296
307
|
await Promise.resolve().then(() => __importStar(require('./main')));
|
|
297
308
|
}
|
package/dist/main.js
CHANGED
|
@@ -77,7 +77,7 @@ const auth_whoami_1 = require("./api/auth-whoami");
|
|
|
77
77
|
const session_cookie_js_1 = require("./auth/session-cookie.js");
|
|
78
78
|
const health_routes_1 = require("./api/health/health.routes");
|
|
79
79
|
const oauth2_routes_js_1 = require("./api/oauth2/oauth2-routes.js");
|
|
80
|
-
const
|
|
80
|
+
const system_1 = require("@camstack/system");
|
|
81
81
|
const boot_config_1 = require("./boot/boot-config");
|
|
82
82
|
const manual_boot_1 = require("./manual-boot");
|
|
83
83
|
const post_boot_service_1 = require("./boot/post-boot.service");
|
|
@@ -173,7 +173,13 @@ async function bootstrap() {
|
|
|
173
173
|
// SPA fallback — set later when admin UI is resolved, used by addon route catch-all
|
|
174
174
|
let spaIndexHtml = null;
|
|
175
175
|
// --- Phase 1 + 2: Load config, setup storage locations, TLS ---
|
|
176
|
-
|
|
176
|
+
// Resolution order: explicit CONFIG_PATH wins (lets operators put the
|
|
177
|
+
// bootstrap config on its own mount, e.g. a /config volume). Otherwise
|
|
178
|
+
// default it UNDER the data root (CAMSTACK_DATA) so a plain data-dir mount
|
|
179
|
+
// also persists config.yaml. Falls back to the historical cwd-relative
|
|
180
|
+
// path for local/dev runs where neither env var is set.
|
|
181
|
+
const configPath = process.env.CONFIG_PATH ??
|
|
182
|
+
path.join(process.env.CAMSTACK_DATA ?? path.join(process.cwd(), 'camstack-data'), 'config.yaml');
|
|
177
183
|
const bootstrapConfig = (0, boot_config_1.loadBootstrapConfig)(configPath);
|
|
178
184
|
const infra = await (0, boot_config_1.setupInfra)(configPath, bootstrapConfig);
|
|
179
185
|
const { dataPath, tlsOptions } = infra;
|
|
@@ -225,8 +231,8 @@ async function bootstrap() {
|
|
|
225
231
|
// If registration fails, start in degraded mode: serve a health warning endpoint.
|
|
226
232
|
// Instantiate new core services
|
|
227
233
|
const loggingService = app.get(logging_service_1.LoggingService);
|
|
228
|
-
const addonRouteRegistry = new
|
|
229
|
-
const dataPlaneRegistry = new
|
|
234
|
+
const addonRouteRegistry = new system_1.AddonRouteRegistry();
|
|
235
|
+
const dataPlaneRegistry = new system_1.DataPlaneRegistry();
|
|
230
236
|
// Use Fastify-managed notification/toast wrappers (globally provided by NotificationModule)
|
|
231
237
|
const { NotificationServiceWrapper } = await Promise.resolve().then(() => __importStar(require('./core/notification/notification-wrapper.service')));
|
|
232
238
|
const { ToastServiceWrapper } = await Promise.resolve().then(() => __importStar(require('./core/notification/toast-wrapper.service')));
|
|
@@ -290,6 +296,9 @@ async function bootstrap() {
|
|
|
290
296
|
moleculer,
|
|
291
297
|
agentRegistry: app.get(agent_registry_service_1.AgentRegistryService),
|
|
292
298
|
hubVersion,
|
|
299
|
+
// Read the live flag at request time — flips to true once the tRPC
|
|
300
|
+
// router finishes registering (see `trpcRegistered = true` below).
|
|
301
|
+
isReady: () => trpcRegistered,
|
|
293
302
|
});
|
|
294
303
|
console.log(`[bootstrap] Health routes registered (hub v${hubVersion})`);
|
|
295
304
|
}
|
|
@@ -707,7 +716,7 @@ async function bootstrap() {
|
|
|
707
716
|
const upstreamPath = `/${subPath}${query}`;
|
|
708
717
|
// Take over the socket — `proxyToUpstream` drives the raw response.
|
|
709
718
|
reply.hijack();
|
|
710
|
-
(0,
|
|
719
|
+
(0, system_1.proxyToUpstream)({
|
|
711
720
|
baseUrl: dpMatch.endpoint.baseUrl,
|
|
712
721
|
secret: dpMatch.endpoint.secret,
|
|
713
722
|
upstreamPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"@camstack/addon-pipeline": "*",
|
|
28
28
|
"@camstack/addon-pipeline-orchestrator": "*",
|
|
29
29
|
"@camstack/addon-post-analysis": "*",
|
|
30
|
-
"@camstack/
|
|
31
|
-
"@camstack/kernel": "*",
|
|
30
|
+
"@camstack/system": "*",
|
|
32
31
|
"@camstack/sdk": "*",
|
|
33
32
|
"@camstack/shm-ring": "*",
|
|
34
33
|
"@camstack/types": "*",
|