@camstack/server 1.1.70 → 1.1.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/api/core/cap-providers.js +0 -11
- package/dist/api/core/lifecycle-job-runner.js +0 -2
- package/dist/api/trpc/generated-cap-routers.js +0 -8
- package/dist/api/trpc/trpc.router.js +49 -2
- package/dist/boot/post-boot.service.js +4 -17
- package/dist/boot/reconcile-lifecycle-jobs.js +6 -5
- package/dist/core/addon/addon-package.service.js +17 -388
- package/dist/core/addon/addon-registry.service.js +26 -17
- package/dist/core/server-update/server-update.service.js +10 -10
- package/dist/launcher.js +22 -19
- package/dist/manual-boot.js +0 -27
- package/dist/server-root/index.js +444 -398
- package/dist/starter.js +3 -3
- package/package.json +8 -8
- package/dist/boot/resume-framework-swap.js +0 -119
- package/dist/core/addon/system-addons-sync.js +0 -121
- package/dist/launcher-framework-swap.js +0 -408
- package/dist/request-framework-swap.js +0 -41
|
@@ -39,7 +39,6 @@ exports.shouldEmitProviderRegisteredReady = shouldEmitProviderRegisteredReady;
|
|
|
39
39
|
const os = __importStar(require("node:os"));
|
|
40
40
|
const addon_row_manifest_1 = require("./addon-row-manifest");
|
|
41
41
|
const runner_convergence_1 = require("./runner-convergence");
|
|
42
|
-
const system_addons_sync_1 = require("./system-addons-sync");
|
|
43
42
|
const package_dir_utils_1 = require("./package-dir-utils");
|
|
44
43
|
const types_1 = require("@camstack/types");
|
|
45
44
|
const system_1 = require("@camstack/system");
|
|
@@ -435,25 +434,30 @@ class AddonRegistryService {
|
|
|
435
434
|
// launcher). Defaults to `<dataDir>/addons` for dev.
|
|
436
435
|
const dataDir = path.resolve(process.env.CAMSTACK_DATA ?? 'camstack-data');
|
|
437
436
|
const addonsDir = process.env.CAMSTACK_ADDONS_DIR ?? path.resolve(dataDir, 'addons');
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
//
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
//
|
|
444
|
-
//
|
|
437
|
+
// Single-copy model (2026-07-18 collapse): the framework BUILTINS
|
|
438
|
+
// (`@camstack/system`: device-manager, snapshot, storage, settings, …) load
|
|
439
|
+
// from the ACTIVE CLOSURE the hub itself runs from — resolved via the same
|
|
440
|
+
// walk-up hub-main uses — NOT from a physical `/data/addons/@camstack/system`
|
|
441
|
+
// copy. That copy drifted because no update channel refreshed it (the
|
|
442
|
+
// recurring "+1 restart" / stale-builtin pain); there is now ONE copy and no
|
|
443
|
+
// `syncSystemAddonsCopy` race. Non-framework addons still load from the
|
|
444
|
+
// addons dir unchanged. The addons-dir `@camstack/system` copy (if present,
|
|
445
|
+
// seeded by the image entrypoint) is SKIPPED to avoid a double-load.
|
|
445
446
|
const closureSystemDir = (0, package_dir_utils_1.resolveHubClosurePackageDir)('@camstack/system');
|
|
446
447
|
if (closureSystemDir) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
closureSystemDir,
|
|
450
|
-
log: (msg, meta) => this.logger.info(msg, { meta: meta ?? {} }),
|
|
451
|
-
});
|
|
452
|
-
if (outcome === 'failed') {
|
|
453
|
-
this.logger.warn('system addons-copy sync failed — builtins may run stale code');
|
|
448
|
+
try {
|
|
449
|
+
await this.addonLoader.loadFromAddonDir(closureSystemDir);
|
|
454
450
|
}
|
|
451
|
+
catch (err) {
|
|
452
|
+
this.logger.error('Failed to load @camstack/system builtins from the active closure', {
|
|
453
|
+
meta: { closureSystemDir, error: err instanceof Error ? err.message : String(err) },
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
this.logger.warn('@camstack/system not resolvable from the hub closure — builtins may be missing');
|
|
455
459
|
}
|
|
456
|
-
await this.addonLoader.loadFromDirectory(addonsDir);
|
|
460
|
+
await this.addonLoader.loadFromDirectory(addonsDir, ['@camstack/system']);
|
|
457
461
|
// Hand pre-init load failures (broken package.json, missing
|
|
458
462
|
// dist, import-time errors) to the health monitor so its retry
|
|
459
463
|
// loop and post-grace alerting cover them. Failures during the
|
|
@@ -494,7 +498,12 @@ class AddonRegistryService {
|
|
|
494
498
|
packageDisplayName: registered.packageDisplayName,
|
|
495
499
|
...(registered.bundle !== undefined ? { bundle: registered.bundle } : {}),
|
|
496
500
|
declaredCapabilities: declCaps,
|
|
497
|
-
|
|
501
|
+
// @camstack/system builtins load from the active closure (single-copy
|
|
502
|
+
// model), not the inert addons-dir copy — point their dir there so
|
|
503
|
+
// any dir-derived path (data dir, markers) resolves to the real copy.
|
|
504
|
+
addonDir: registered.packageName === '@camstack/system' && closureSystemDir
|
|
505
|
+
? closureSystemDir
|
|
506
|
+
: path.join(addonsDir, registered.packageName),
|
|
498
507
|
declaration: registered.declaration,
|
|
499
508
|
});
|
|
500
509
|
}
|
|
@@ -38,18 +38,18 @@ exports.ServerUpdateService = void 0;
|
|
|
38
38
|
* ServerUpdateService — hub-side adapter over the SHARED `RootUpdateService`
|
|
39
39
|
* (`@camstack/node-root`, bundled into `dist/server-root/index.js`).
|
|
40
40
|
*
|
|
41
|
-
* The stage/apply
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* the
|
|
45
|
-
* the
|
|
46
|
-
* the `CAMSTACK_DATA` data-dir default and the running package.json path.
|
|
41
|
+
* The single-copy stage/apply engine — stage a closure into `.staging-*`,
|
|
42
|
+
* arm the pending-root-swap marker, restart — lives in the shared core so the
|
|
43
|
+
* hub and the agent can never drift. This class only binds the hub's
|
|
44
|
+
* parameters: the `@camstack/server` root spec, the `CAMSTACK_SERVER_*` env
|
|
45
|
+
* markers, the `CAMSTACK_DATA` data-dir default and the running package.json.
|
|
47
46
|
*
|
|
48
|
-
* The starter (server/backend/src/starter.ts → shared `runNodeStarter`)
|
|
49
|
-
*
|
|
50
|
-
*
|
|
47
|
+
* The starter (server/backend/src/starter.ts → shared `runNodeStarter`) owns
|
|
48
|
+
* the OTHER half: applying the staged swap of the single `current/` copy at
|
|
49
|
+
* boot + the one-time migration off the legacy multi-copy layout. There is no
|
|
50
|
+
* probation boot / N-1 / auto-rollback (2026-07-18 collapse).
|
|
51
51
|
*
|
|
52
|
-
* Spec: docs/superpowers/specs/2026-07-
|
|
52
|
+
* Spec: docs/superpowers/specs/2026-07-18-single-framework-copy-collapse-design.md
|
|
53
53
|
*/
|
|
54
54
|
const path = __importStar(require("node:path"));
|
|
55
55
|
const index_js_1 = require("../../server-root/index.js");
|
package/dist/launcher.js
CHANGED
|
@@ -44,15 +44,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
44
44
|
* 3. Dynamically import main.ts (which has static @camstack/system imports)
|
|
45
45
|
*
|
|
46
46
|
* Load-order invariant: @camstack/system is imported DYNAMICALLY inside
|
|
47
|
-
* launch() AFTER
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* launch() AFTER the active framework dir is resolved (the single-copy
|
|
48
|
+
* collapse points CAMSTACK_FRAMEWORK_DIR at CAMSTACK_SERVER_ACTIVE_ROOT in
|
|
49
|
+
* data-root mode, or the baked seed otherwise). NEVER add a top-level static
|
|
50
|
+
* import of @camstack/system here — that would pin the resolver before the
|
|
51
|
+
* framework dir + NODE_PATH are set up. Framework/cap updates ship via the
|
|
52
|
+
* node-root `applyServerUpdate` flow, not an in-launcher package swap.
|
|
50
53
|
*/
|
|
51
54
|
const fs = __importStar(require("node:fs"));
|
|
52
55
|
const path = __importStar(require("node:path"));
|
|
53
56
|
const tar = __importStar(require("tar"));
|
|
54
57
|
const yaml = __importStar(require("js-yaml"));
|
|
55
|
-
const launcher_framework_swap_js_1 = require("./launcher-framework-swap.js");
|
|
56
58
|
const framework_nodepath_js_1 = require("./framework-nodepath.js");
|
|
57
59
|
/** Path of the manifest file embedded inside every archive. */
|
|
58
60
|
const ARCHIVE_MANIFEST_NAME = '.camstack-backup-manifest.json';
|
|
@@ -241,25 +243,26 @@ async function launch() {
|
|
|
241
243
|
// `CAMSTACK_ADDONS_DIR` lets the Docker/Electron images point at their
|
|
242
244
|
// node_modules path (e.g. /data/node_modules); dev keeps `<dataDir>/addons`.
|
|
243
245
|
const addonsDir = process.env['CAMSTACK_ADDONS_DIR'] ?? path.resolve(dataDir, 'addons');
|
|
244
|
-
// ──
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
//
|
|
246
|
+
// ── Single-copy framework dir (2026-07-18 collapse) ────────────────────────
|
|
247
|
+
// In data-root mode the ACTIVE single copy (set by the starter as
|
|
248
|
+
// CAMSTACK_SERVER_ACTIVE_ROOT) IS the framework — point the forked
|
|
249
|
+
// addon-runners' resolver + NODE_PATH at it instead of the (now-inert)
|
|
250
|
+
// static /data/framework copy. Forked children inherit this via process.env,
|
|
251
|
+
// and the NODE_PATH extension below reads CAMSTACK_FRAMEWORK_DIR. No-op in
|
|
252
|
+
// baked mode (activeRoot unset → keep the image's /data/framework).
|
|
253
|
+
const activeRoot = process.env['CAMSTACK_SERVER_ACTIVE_ROOT'];
|
|
254
|
+
if (activeRoot && fs.existsSync(path.join(activeRoot, 'node_modules', '@camstack', 'system'))) {
|
|
255
|
+
process.env['CAMSTACK_FRAMEWORK_DIR'] = activeRoot;
|
|
256
|
+
console.log(`[launcher] Single-copy framework dir → ${activeRoot}`);
|
|
257
|
+
}
|
|
258
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
257
259
|
// Restore must happen first so the snapshot's `addons/` is what the
|
|
258
260
|
// installer + symlink step pick up — otherwise we'd install a stale
|
|
259
261
|
// set and overwrite it a step later.
|
|
260
262
|
await applyPendingRestore(dataDir);
|
|
261
|
-
// @camstack/system is imported DYNAMICALLY here — AFTER
|
|
262
|
-
//
|
|
263
|
+
// @camstack/system is imported DYNAMICALLY here — AFTER the active framework
|
|
264
|
+
// dir + NODE_PATH are resolved above — so the correct framework copy is what
|
|
265
|
+
// gets loaded. Never import it at module top.
|
|
263
266
|
const { AddonInstaller, bootstrapSchema, detectWorkspacePackagesDir } = await Promise.resolve().then(() => __importStar(require('@camstack/system')));
|
|
264
267
|
// Install source resolution:
|
|
265
268
|
// 1. CAMSTACK_BUNDLED_ADDONS_DIR — set by Electron-packaged builds
|
package/dist/manual-boot.js
CHANGED
|
@@ -47,10 +47,8 @@ exports.bootManual = bootManual;
|
|
|
47
47
|
const path = __importStar(require("node:path"));
|
|
48
48
|
const node_crypto_1 = require("node:crypto");
|
|
49
49
|
const fastify_1 = __importDefault(require("fastify"));
|
|
50
|
-
const system_1 = require("@camstack/system");
|
|
51
50
|
const types_1 = require("@camstack/types");
|
|
52
51
|
const lifecycle_runner_singleton_1 = require("./core/lifecycle/lifecycle-runner.singleton");
|
|
53
|
-
const request_framework_swap_1 = require("./request-framework-swap");
|
|
54
52
|
const config_service_1 = require("./core/config/config.service");
|
|
55
53
|
const logging_service_1 = require("./core/logging/logging.service");
|
|
56
54
|
const event_bus_service_1 = require("./core/events/event-bus.service");
|
|
@@ -175,31 +173,6 @@ async function bootManual(opts) {
|
|
|
175
173
|
});
|
|
176
174
|
}
|
|
177
175
|
},
|
|
178
|
-
// ── Framework swap deps ──────────────────────────────────────────
|
|
179
|
-
stageFramework: (task, signal) => (0, system_1.stageFrameworkLockstep)({
|
|
180
|
-
jobId: task.taskId,
|
|
181
|
-
toVersion: task.toVersion,
|
|
182
|
-
frameworkDir: process.env['CAMSTACK_FRAMEWORK_DIR'] ?? '',
|
|
183
|
-
stagingDir: path.join(lifecycleDataDir, 'lifecycle', 'staging'),
|
|
184
|
-
fetchTarball: (name, version, sig) => addonPackageService.fetchAddonTarball(name, version, sig),
|
|
185
|
-
extract: (tgz, destDir) => addonPackageService.extractTarball(tgz, destDir),
|
|
186
|
-
// Called only for the lockstep COMPANIONS (types/sdk/shm-ring) — the
|
|
187
|
-
// primary (@camstack/system) uses `task.toVersion` directly inside
|
|
188
|
-
// stageFrameworkLockstep. Companions are versioned independently on npm
|
|
189
|
-
// (system's package.json declares them as `*`), so resolve each to its
|
|
190
|
-
// own latest — pinning to `task.toVersion` fetches a non-existent
|
|
191
|
-
// `@camstack/types@<system-version>` tarball and fails the swap.
|
|
192
|
-
resolveVersion: (name) => addonPackageService.resolveFrameworkVersion(name, 'latest'),
|
|
193
|
-
currentVersionOf: (name) => addonPackageService.currentFrameworkVersionOf(name),
|
|
194
|
-
signal,
|
|
195
|
-
}),
|
|
196
|
-
requestFrameworkSwap: ({ jobId, taskId, packages }) => {
|
|
197
|
-
// Write the pending-swap marker AND schedule the reboot that applies it.
|
|
198
|
-
// The engine assumes the process exits after this returns; the restart is
|
|
199
|
-
// owned here (the single seam for every engine path: single update,
|
|
200
|
-
// "Update all" bulk, and the auto-update scheduler).
|
|
201
|
-
(0, request_framework_swap_1.requestFrameworkSwapAndRestart)(lifecycleDataDir, { jobId, taskId, packages });
|
|
202
|
-
},
|
|
203
176
|
});
|
|
204
177
|
const replEngineService = new repl_engine_service_1.ReplEngineService(addonRegistryService, eventBusService, loggingService);
|
|
205
178
|
// Runtime-updatable root package (phase 1: hub only). The service owns the
|