@camstack/server 1.2.70 → 1.2.72
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-root-inventory.js +165 -0
- package/dist/agent/agent-service.js +9 -12
- package/dist/api/addon-upload.js +96 -89
- package/dist/api/agent-addon-delivery.js +135 -0
- package/dist/api/core/cluster-nodes.router.js +22 -0
- package/dist/api/core/logs.router.js +2 -1
- package/dist/api/trpc/generated-cap-routers.js +88 -0
- package/dist/core/addon/addon-package.service.js +53 -5
- package/dist/core/agent/agent-addon-backfill.js +26 -3
- package/dist/core/agent/agent-delivery-ledger.js +112 -0
- package/dist/core/agent/agent-registry.service.js +107 -9
- package/dist/launcher.js +77 -6
- package/dist/main.js +4 -1
- package/dist/manual-boot.js +44 -25
- package/package.json +8 -8
package/dist/launcher.js
CHANGED
|
@@ -52,10 +52,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
52
52
|
* node-root `applyServerUpdate` flow, not an in-launcher package swap.
|
|
53
53
|
*/
|
|
54
54
|
const fs = __importStar(require("node:fs"));
|
|
55
|
+
const os = __importStar(require("node:os"));
|
|
55
56
|
const path = __importStar(require("node:path"));
|
|
56
57
|
const tar = __importStar(require("tar"));
|
|
57
58
|
const yaml = __importStar(require("js-yaml"));
|
|
58
59
|
const framework_nodepath_js_1 = require("./framework-nodepath.js");
|
|
60
|
+
const addon_root_inventory_js_1 = require("./addon-root-inventory.js");
|
|
59
61
|
const package_inventory_js_1 = require("./package-inventory.js");
|
|
60
62
|
const bootstrap_packages_js_1 = require("./bootstrap-packages.js");
|
|
61
63
|
/** Path of the manifest file embedded inside every archive. */
|
|
@@ -340,11 +342,14 @@ async function launch() {
|
|
|
340
342
|
const bootstrapRequired = readBootstrapRequiredAddons(dataDir, bootstrapSchema) ?? roleDefaultBootstrap;
|
|
341
343
|
console.log(`[launcher] bootstrap (${role}): ${bootstrapRequired.length} required package(s)`);
|
|
342
344
|
await installer.ensureRequiredPackages(bootstrapRequired);
|
|
343
|
-
// Reconcile the install manifest
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
345
|
+
// Reconcile the install manifest against what is on disk — the directory is
|
|
346
|
+
// the truth, because it is what the loader runs. Registers image-seeded
|
|
347
|
+
// addons (copied straight into addonsDir by the container entrypoint, never
|
|
348
|
+
// through an install codepath, so runtime "Update" rejected them as "not
|
|
349
|
+
// currently tracked"), corrects a recorded version that disagrees with the
|
|
350
|
+
// installed package.json, and drops entries naming a directory that does not
|
|
351
|
+
// exist. Idempotent; safe every boot. Runs BEFORE any update can be in
|
|
352
|
+
// flight, and never touches an entry holding a rollback pointer.
|
|
348
353
|
//
|
|
349
354
|
// Guarded with a typeof check: the backend and @camstack/system normally
|
|
350
355
|
// ship together in one image, but a system-only framework update can
|
|
@@ -354,7 +359,7 @@ async function launch() {
|
|
|
354
359
|
if (typeof installer.reconcileManifest === 'function') {
|
|
355
360
|
const reconciled = installer.reconcileManifest();
|
|
356
361
|
if (reconciled > 0) {
|
|
357
|
-
console.log(`[launcher] Manifest reconciled — ${reconciled}
|
|
362
|
+
console.log(`[launcher] Manifest reconciled — ${reconciled} correction(s)`);
|
|
358
363
|
}
|
|
359
364
|
}
|
|
360
365
|
else {
|
|
@@ -494,6 +499,72 @@ async function launch() {
|
|
|
494
499
|
// to boot when it is the thing that was supposed to explain failures.
|
|
495
500
|
console.warn(`[launcher] package inventory failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
496
501
|
}
|
|
502
|
+
// A SECOND ADDON ROOT on this machine.
|
|
503
|
+
//
|
|
504
|
+
// The inventory above scans roots derived from the ACTIVE data dir, so an
|
|
505
|
+
// entire rival data root — a retired Electron app id, the pre-Electron
|
|
506
|
+
// `~/camstack-agent-data` — is structurally invisible to it. Those trees hold
|
|
507
|
+
// complete `addons/@camstack/` installs with plausible package.json files,
|
|
508
|
+
// and reading a version out of one answers a question about a node that has
|
|
509
|
+
// not run since June. Measured on the Mac agent: four addon roots, four
|
|
510
|
+
// truthful `addon-pipeline` versions, one of them the running node's.
|
|
511
|
+
//
|
|
512
|
+
// Reported, never fatal, never deleted here — same policy as D45. Making the
|
|
513
|
+
// second location LOUD is the whole job; removing it stays an operator
|
|
514
|
+
// decision. `CAMSTACK_INVENTORY_STRICT=1` makes it fatal, which is how a
|
|
515
|
+
// machine that HAS been cleaned keeps itself clean.
|
|
516
|
+
try {
|
|
517
|
+
const addonRoots = (0, addon_root_inventory_js_1.inventoryAddonRoots)({
|
|
518
|
+
dataDir: DATA_DIR,
|
|
519
|
+
activeAddonsDir: addonsDir,
|
|
520
|
+
homeDir: os.homedir(),
|
|
521
|
+
platform: process.platform,
|
|
522
|
+
}, {
|
|
523
|
+
exists: (dir) => fs.existsSync(dir),
|
|
524
|
+
listPackages: (scopeDir) => {
|
|
525
|
+
try {
|
|
526
|
+
return fs
|
|
527
|
+
.readdirSync(scopeDir, { withFileTypes: true })
|
|
528
|
+
.filter((e) => e.isDirectory() || e.isSymbolicLink())
|
|
529
|
+
.map((e) => e.name);
|
|
530
|
+
}
|
|
531
|
+
catch {
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
readVersion: (packageDir) => {
|
|
536
|
+
try {
|
|
537
|
+
const raw = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf-8'));
|
|
538
|
+
const version = typeof raw === 'object' && raw !== null
|
|
539
|
+
? raw.version
|
|
540
|
+
: undefined;
|
|
541
|
+
return typeof version === 'string' ? version : null;
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
realPath: (dir) => {
|
|
548
|
+
try {
|
|
549
|
+
return fs.realpathSync(dir);
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
return dir;
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
});
|
|
556
|
+
const addonRootReport = (0, addon_root_inventory_js_1.formatAddonRootReport)(addonRoots);
|
|
557
|
+
if (addonRootReport !== '') {
|
|
558
|
+
console.error(`[launcher] ${addonRootReport}`);
|
|
559
|
+
if (process.env['CAMSTACK_INVENTORY_STRICT'] === '1') {
|
|
560
|
+
console.error('[launcher] CAMSTACK_INVENTORY_STRICT=1 — refusing to boot on this layout');
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
catch (err) {
|
|
566
|
+
console.warn(`[launcher] addon-root inventory failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
567
|
+
}
|
|
497
568
|
// Now safe to load the role's runtime entry (both have static imports from
|
|
498
569
|
// @camstack/system, resolved only after the framework dir + NODE_PATH setup
|
|
499
570
|
// above). The agent boots from THIS same @camstack/server closure — there is
|
package/dist/main.js
CHANGED
|
@@ -276,7 +276,10 @@ async function bootstrap() {
|
|
|
276
276
|
const uploadAddonRegistry = app.get(addon_registry_service_1.AddonRegistryService);
|
|
277
277
|
const uploadAddonPackage = app.get(addon_package_service_1.AddonPackageService);
|
|
278
278
|
const uploadLogger = app.get(logging_service_1.LoggingService).createLogger('addon-upload');
|
|
279
|
-
|
|
279
|
+
// The delivery ledger marks "the hub is writing addons to this node" for
|
|
280
|
+
// the whole deploy+reload, so the addon back-fill cannot read the roster
|
|
281
|
+
// gap a deploy opens and repair the node over the bytes just shipped.
|
|
282
|
+
await (0, addon_upload_1.registerAddonUploadRoute)(fastify, uploadAddonBridge, uploadAuthService, uploadMoleculer, uploadAddonRegistry, uploadAddonPackage, uploadLogger, app.get(agent_registry_service_1.AgentRegistryService).deliveryLedger);
|
|
280
283
|
console.log('[bootstrap] Addon upload route registered at POST /api/addons/upload');
|
|
281
284
|
// Dev server-deploy channel gateway (`camstack deploy-server`). MUST be
|
|
282
285
|
// registered AFTER registerAddonUploadRoute — it reuses the multipart
|
package/dist/manual-boot.js
CHANGED
|
@@ -44,37 +44,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
44
44
|
};
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
46
|
exports.bootManual = bootManual;
|
|
47
|
-
const path = __importStar(require("node:path"));
|
|
48
47
|
const node_crypto_1 = require("node:crypto");
|
|
49
|
-
const
|
|
48
|
+
const path = __importStar(require("node:path"));
|
|
50
49
|
const types_1 = require("@camstack/types");
|
|
51
|
-
const
|
|
50
|
+
const fastify_1 = __importDefault(require("fastify"));
|
|
51
|
+
const addon_upload_1 = require("./api/addon-upload");
|
|
52
|
+
const cap_providers_1 = require("./api/core/cap-providers");
|
|
53
|
+
const post_boot_service_1 = require("./boot/post-boot.service");
|
|
54
|
+
const addon_package_service_1 = require("./core/addon/addon-package.service");
|
|
55
|
+
const addon_registry_service_1 = require("./core/addon/addon-registry.service");
|
|
56
|
+
const addon_search_service_1 = require("./core/addon/addon-search.service");
|
|
57
|
+
const addon_bridge_service_1 = require("./core/addon-bridge/addon-bridge.service");
|
|
58
|
+
const addon_pages_service_1 = require("./core/addon-pages/addon-pages.service");
|
|
59
|
+
const addon_widgets_service_1 = require("./core/addon-widgets/addon-widgets.service");
|
|
60
|
+
const agent_registry_service_1 = require("./core/agent/agent-registry.service");
|
|
61
|
+
const cluster_node_history_store_1 = require("./core/agent/cluster-node-history-store");
|
|
62
|
+
const auth_service_1 = require("./core/auth/auth.service");
|
|
63
|
+
const capability_service_1 = require("./core/capability/capability.service");
|
|
52
64
|
const config_service_1 = require("./core/config/config.service");
|
|
53
|
-
const logging_service_1 = require("./core/logging/logging.service");
|
|
54
65
|
const event_bus_service_1 = require("./core/events/event-bus.service");
|
|
55
|
-
const storage_service_1 = require("./core/storage/storage.service");
|
|
56
|
-
const capability_service_1 = require("./core/capability/capability.service");
|
|
57
66
|
const feature_service_1 = require("./core/feature/feature.service");
|
|
58
|
-
const
|
|
59
|
-
const
|
|
67
|
+
const lifecycle_runner_singleton_1 = require("./core/lifecycle/lifecycle-runner.singleton");
|
|
68
|
+
const logging_service_1 = require("./core/logging/logging.service");
|
|
69
|
+
const moleculer_service_1 = require("./core/moleculer/moleculer.service");
|
|
60
70
|
const network_quality_service_1 = require("./core/network/network-quality.service");
|
|
61
|
-
const toast_wrapper_service_1 = require("./core/notification/toast-wrapper.service");
|
|
62
71
|
const notification_wrapper_service_1 = require("./core/notification/notification-wrapper.service");
|
|
63
|
-
const
|
|
64
|
-
const addon_widgets_service_1 = require("./core/addon-widgets/addon-widgets.service");
|
|
65
|
-
const addon_bridge_service_1 = require("./core/addon-bridge/addon-bridge.service");
|
|
66
|
-
const moleculer_service_1 = require("./core/moleculer/moleculer.service");
|
|
67
|
-
const agent_registry_service_1 = require("./core/agent/agent-registry.service");
|
|
68
|
-
const cluster_node_history_store_1 = require("./core/agent/cluster-node-history-store");
|
|
69
|
-
const addon_upload_1 = require("./api/addon-upload");
|
|
70
|
-
const addon_registry_service_1 = require("./core/addon/addon-registry.service");
|
|
71
|
-
const addon_search_service_1 = require("./core/addon/addon-search.service");
|
|
72
|
-
const addon_package_service_1 = require("./core/addon/addon-package.service");
|
|
72
|
+
const toast_wrapper_service_1 = require("./core/notification/toast-wrapper.service");
|
|
73
73
|
const repl_engine_service_1 = require("./core/repl/repl-engine.service");
|
|
74
74
|
const server_update_service_1 = require("./core/server-update/server-update.service");
|
|
75
|
+
const storage_service_1 = require("./core/storage/storage.service");
|
|
76
|
+
const stream_probe_service_1 = require("./core/streaming/stream-probe.service");
|
|
75
77
|
const topology_emitter_service_1 = require("./core/topology/topology-emitter.service");
|
|
76
|
-
const cap_providers_1 = require("./api/core/cap-providers");
|
|
77
|
-
const post_boot_service_1 = require("./boot/post-boot.service");
|
|
78
78
|
// ---------------------------------------------------------------------------
|
|
79
79
|
// Service container — narrowing via `instanceof`, no casts.
|
|
80
80
|
// ---------------------------------------------------------------------------
|
|
@@ -147,11 +147,30 @@ async function bootManual(opts) {
|
|
|
147
147
|
const addonPackageService = new addon_package_service_1.AddonPackageService(loggingService, eventBusService, configService, addonRegistryService, notificationWrapper, toastWrapper);
|
|
148
148
|
// Addon back-fill delivery seam. Wired here (not in the AgentRegistryService
|
|
149
149
|
// constructor) because it needs AddonPackageService, which is a later layer.
|
|
150
|
-
// Bytes come from
|
|
151
|
-
//
|
|
152
|
-
//
|
|
150
|
+
// Bytes come from THE HUB'S OWN INSTALLED COPY whenever it has one, and only
|
|
151
|
+
// from the registry when it does not. This line used to claim that while
|
|
152
|
+
// calling `packPackage` — `npm pack <name>@<version>` — so the back-fill
|
|
153
|
+
// repaired a node by installing the REGISTRY's build of a version over the
|
|
154
|
+
// one an operator had just deployed. Same name, same version, different
|
|
155
|
+
// bundle: every `camstack deploy … -n little-unraid` on 2026-08-07 was
|
|
156
|
+
// reverted ~1.5s later and read as a stale dist. See `packInstalledPackage`.
|
|
157
|
+
// Delivery is an RPC pair, never an event (D8/D11).
|
|
158
|
+
const backfillLogger = loggingService.createLogger('agent-backfill');
|
|
153
159
|
agentRegistryService.setAddonBackfill({
|
|
154
|
-
pack: async (name, version) =>
|
|
160
|
+
pack: async (name, version) => {
|
|
161
|
+
const installed = await addonPackageService.packInstalledPackage(name);
|
|
162
|
+
// Name the SOURCE. This delivery overwrites whatever the node has, so
|
|
163
|
+
// "which build did we just push" is the first question every time it goes
|
|
164
|
+
// wrong — and for a whole day it was answered nowhere at all.
|
|
165
|
+
backfillLogger.info('back-fill packing', {
|
|
166
|
+
meta: {
|
|
167
|
+
packageName: name,
|
|
168
|
+
source: installed ? 'hub-installed' : 'npm-registry',
|
|
169
|
+
version: installed?.version ?? version,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
return (installed ?? (await addonPackageService.packPackage(name, version))).buffer;
|
|
173
|
+
},
|
|
155
174
|
deploy: async (nodeId, packageName, bundle) => {
|
|
156
175
|
await moleculerService.broker.call('$agent.deploy', { addonId: packageName, source: (0, addon_upload_1.buildHubHttpSource)(bundle, (0, addon_upload_1.hubBundleBaseUrl)()) }, { nodeID: nodeId, timeout: agent_registry_service_1.AGENT_BACKFILL_RPC_TIMEOUT_MS });
|
|
157
176
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.72",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -33,19 +33,19 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@camstack/addon-admin-ui": "1.2.
|
|
36
|
+
"@camstack/addon-admin-ui": "1.2.33",
|
|
37
37
|
"@camstack/addon-agent-ui": "1.2.10",
|
|
38
38
|
"@camstack/addon-auth": "1.2.11",
|
|
39
39
|
"@camstack/addon-decoder-nodeav": "1.2.9",
|
|
40
40
|
"@camstack/addon-notifiers": "1.2.13",
|
|
41
|
-
"@camstack/addon-pipeline": "1.2.
|
|
42
|
-
"@camstack/addon-pipeline-orchestrator": "1.2.
|
|
43
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
41
|
+
"@camstack/addon-pipeline": "1.2.44",
|
|
42
|
+
"@camstack/addon-pipeline-orchestrator": "1.2.27",
|
|
43
|
+
"@camstack/addon-post-analysis": "1.2.49",
|
|
44
44
|
"@camstack/sdk": "1.2.10",
|
|
45
45
|
"@camstack/shm-ring": "1.1.9",
|
|
46
|
-
"@camstack/system": "1.2.
|
|
47
|
-
"@camstack/types": "1.2.
|
|
48
|
-
"@camstack/ui-library": "1.2.
|
|
46
|
+
"@camstack/system": "1.2.59",
|
|
47
|
+
"@camstack/types": "1.2.43",
|
|
48
|
+
"@camstack/ui-library": "1.2.31",
|
|
49
49
|
"@fastify/compress": "^9.0.0",
|
|
50
50
|
"@fastify/cookie": "^11.0.2",
|
|
51
51
|
"@fastify/cors": "^11.2.0",
|