@camstack/server 1.1.71 → 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/launcher.js +9 -19
- package/dist/manual-boot.js +0 -27
- package/dist/server-root/index.js +68 -42
- package/package.json +8 -8
- package/dist/boot/resume-framework-swap.js +0 -119
- package/dist/launcher-framework-swap.js +0 -408
- package/dist/request-framework-swap.js +0 -41
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,19 +243,6 @@ 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
|
-
// ── Framework swap ────────────────────────────────────────────────────────
|
|
245
|
-
// MUST run before @camstack/system is loaded so the NEW framework code is
|
|
246
|
-
// what gets required. Both helpers are zero-dep (no @camstack imports).
|
|
247
|
-
// Roll back an unconfirmed prior swap (crash-loop guard), then apply any
|
|
248
|
-
// freshly-staged swap.
|
|
249
|
-
const frameworkDir = process.env['CAMSTACK_FRAMEWORK_DIR'];
|
|
250
|
-
const rb = (0, launcher_framework_swap_js_1.rollbackUnconfirmedFrameworkSwap)(dataDir, frameworkDir);
|
|
251
|
-
if (rb.rolledBack)
|
|
252
|
-
console.log('[launcher] Rolled back an unconfirmed framework update');
|
|
253
|
-
const fsw = (0, launcher_framework_swap_js_1.applyPendingFrameworkSwap)(dataDir, frameworkDir);
|
|
254
|
-
if (fsw.applied)
|
|
255
|
-
console.log(`[launcher] Applied staged framework update (job ${fsw.jobId ?? '?'})`);
|
|
256
|
-
// ── End framework swap ────────────────────────────────────────────────────
|
|
257
246
|
// ── Single-copy framework dir (2026-07-18 collapse) ────────────────────────
|
|
258
247
|
// In data-root mode the ACTIVE single copy (set by the starter as
|
|
259
248
|
// CAMSTACK_SERVER_ACTIVE_ROOT) IS the framework — point the forked
|
|
@@ -271,8 +260,9 @@ async function launch() {
|
|
|
271
260
|
// installer + symlink step pick up — otherwise we'd install a stale
|
|
272
261
|
// set and overwrite it a step later.
|
|
273
262
|
await applyPendingRestore(dataDir);
|
|
274
|
-
// @camstack/system is imported DYNAMICALLY here — AFTER
|
|
275
|
-
//
|
|
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.
|
|
276
266
|
const { AddonInstaller, bootstrapSchema, detectWorkspacePackagesDir } = await Promise.resolve().then(() => __importStar(require('@camstack/system')));
|
|
277
267
|
// Install source resolution:
|
|
278
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
|
|
@@ -371,6 +371,38 @@ var require_dist = __commonJS({
|
|
|
371
371
|
__name(validateVersionDir, "validateVersionDir");
|
|
372
372
|
var fs3 = __toESM2(require("fs"));
|
|
373
373
|
var path3 = __toESM2(require("path"));
|
|
374
|
+
function parse(version) {
|
|
375
|
+
const dashIdx = version.indexOf("-");
|
|
376
|
+
const base = dashIdx === -1 ? version : version.slice(0, dashIdx);
|
|
377
|
+
const prerelease = dashIdx === -1 ? null : version.slice(dashIdx + 1);
|
|
378
|
+
const nums = base.split(".").map((seg) => {
|
|
379
|
+
const n = Number.parseInt(seg, 10);
|
|
380
|
+
return Number.isNaN(n) ? 0 : n;
|
|
381
|
+
});
|
|
382
|
+
return {
|
|
383
|
+
nums,
|
|
384
|
+
prerelease
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
__name(parse, "parse");
|
|
388
|
+
function compareSemver(a, b) {
|
|
389
|
+
const pa = parse(a);
|
|
390
|
+
const pb = parse(b);
|
|
391
|
+
const len = Math.max(pa.nums.length, pb.nums.length);
|
|
392
|
+
for (let i = 0; i < len; i++) {
|
|
393
|
+
const na = pa.nums[i] ?? 0;
|
|
394
|
+
const nb = pb.nums[i] ?? 0;
|
|
395
|
+
if (na < nb) return -1;
|
|
396
|
+
if (na > nb) return 1;
|
|
397
|
+
}
|
|
398
|
+
if (pa.prerelease === null && pb.prerelease === null) return 0;
|
|
399
|
+
if (pa.prerelease === null) return 1;
|
|
400
|
+
if (pb.prerelease === null) return -1;
|
|
401
|
+
if (pa.prerelease < pb.prerelease) return -1;
|
|
402
|
+
if (pa.prerelease > pb.prerelease) return 1;
|
|
403
|
+
return 0;
|
|
404
|
+
}
|
|
405
|
+
__name(compareSemver, "compareSemver");
|
|
374
406
|
var CURRENT_DIRNAME = "current";
|
|
375
407
|
var PENDING_ROOT_SWAP_FILE = ".pending-root-swap.json";
|
|
376
408
|
var LEGACY_FRAMEWORK_MARKERS = [
|
|
@@ -566,17 +598,43 @@ var require_dist = __commonJS({
|
|
|
566
598
|
};
|
|
567
599
|
}
|
|
568
600
|
__name(applyPendingRootSwap, "applyPendingRootSwap");
|
|
601
|
+
function readClosureVersion(closureDir, spec) {
|
|
602
|
+
try {
|
|
603
|
+
const raw = JSON.parse(fs3.readFileSync(path3.join(rootPackageDir2(closureDir, spec), "package.json"), "utf-8"));
|
|
604
|
+
if (typeof raw === "object" && raw !== null) {
|
|
605
|
+
const version = raw["version"];
|
|
606
|
+
if (typeof version === "string") return version;
|
|
607
|
+
}
|
|
608
|
+
} catch {
|
|
609
|
+
}
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
__name(readClosureVersion, "readClosureVersion");
|
|
613
|
+
function isAdoptablePendingClosure(rootDir, version, nodeMajor, spec) {
|
|
614
|
+
const dir = versionDir(rootDir, version);
|
|
615
|
+
return validateClosureDir(dir, nodeMajor, spec, version) === null && findMissingNativePrebuilds(dir).length === 0;
|
|
616
|
+
}
|
|
617
|
+
__name(isAdoptablePendingClosure, "isAdoptablePendingClosure");
|
|
569
618
|
function migrateToSingleCopy(rootDir, spec, nodeMajor, now, log) {
|
|
570
619
|
const dest = currentDir(rootDir);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
620
|
+
const legacyState = readServerRootState(rootDir);
|
|
621
|
+
const pendingVersion = legacyState?.pendingBoot?.version ?? null;
|
|
622
|
+
const pendingAdoptable = pendingVersion !== null && isAdoptablePendingClosure(rootDir, pendingVersion, nodeMajor, spec);
|
|
623
|
+
const currentValid = fs3.existsSync(dest) && validateClosureDir(dest, nodeMajor, spec) === null;
|
|
624
|
+
if (currentValid) {
|
|
625
|
+
const currentVersion = readClosureVersion(dest, spec);
|
|
626
|
+
const pendingSupersedes = pendingAdoptable && pendingVersion !== null && (currentVersion === null || compareSemver(pendingVersion, currentVersion) > 0);
|
|
627
|
+
if (!pendingSupersedes) {
|
|
628
|
+
return {
|
|
629
|
+
migrated: false,
|
|
630
|
+
version: null,
|
|
631
|
+
reason: "current already present"
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
log(`[single-copy] in-flight pending ${pendingVersion} supersedes older current ${currentVersion ?? "?"}`);
|
|
577
635
|
}
|
|
578
636
|
const candidates = [];
|
|
579
|
-
|
|
637
|
+
if (pendingAdoptable && pendingVersion !== null) candidates.push(pendingVersion);
|
|
580
638
|
if (legacyState !== null) {
|
|
581
639
|
for (const v of [
|
|
582
640
|
legacyState.currentVersion,
|
|
@@ -662,38 +720,6 @@ var require_dist = __commonJS({
|
|
|
662
720
|
};
|
|
663
721
|
}
|
|
664
722
|
__name(planBoot, "planBoot");
|
|
665
|
-
function parse(version) {
|
|
666
|
-
const dashIdx = version.indexOf("-");
|
|
667
|
-
const base = dashIdx === -1 ? version : version.slice(0, dashIdx);
|
|
668
|
-
const prerelease = dashIdx === -1 ? null : version.slice(dashIdx + 1);
|
|
669
|
-
const nums = base.split(".").map((seg) => {
|
|
670
|
-
const n = Number.parseInt(seg, 10);
|
|
671
|
-
return Number.isNaN(n) ? 0 : n;
|
|
672
|
-
});
|
|
673
|
-
return {
|
|
674
|
-
nums,
|
|
675
|
-
prerelease
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
__name(parse, "parse");
|
|
679
|
-
function compareSemver(a, b) {
|
|
680
|
-
const pa = parse(a);
|
|
681
|
-
const pb = parse(b);
|
|
682
|
-
const len = Math.max(pa.nums.length, pb.nums.length);
|
|
683
|
-
for (let i = 0; i < len; i++) {
|
|
684
|
-
const na = pa.nums[i] ?? 0;
|
|
685
|
-
const nb = pb.nums[i] ?? 0;
|
|
686
|
-
if (na < nb) return -1;
|
|
687
|
-
if (na > nb) return 1;
|
|
688
|
-
}
|
|
689
|
-
if (pa.prerelease === null && pb.prerelease === null) return 0;
|
|
690
|
-
if (pa.prerelease === null) return 1;
|
|
691
|
-
if (pb.prerelease === null) return -1;
|
|
692
|
-
if (pa.prerelease < pb.prerelease) return -1;
|
|
693
|
-
if (pa.prerelease > pb.prerelease) return 1;
|
|
694
|
-
return 0;
|
|
695
|
-
}
|
|
696
|
-
__name(compareSemver, "compareSemver");
|
|
697
723
|
var fs4 = __toESM2(require("fs"));
|
|
698
724
|
var path4 = __toESM2(require("path"));
|
|
699
725
|
function detectWorkspaceRoot(fromDir) {
|
|
@@ -756,7 +782,7 @@ var require_dist = __commonJS({
|
|
|
756
782
|
registerHooks(hooks);
|
|
757
783
|
}
|
|
758
784
|
__name(registerActiveRootResolver, "registerActiveRootResolver");
|
|
759
|
-
function
|
|
785
|
+
function readClosureVersion2(closureDir, spec) {
|
|
760
786
|
try {
|
|
761
787
|
const raw = JSON.parse(fs5.readFileSync(path5.join(rootPackageDir2(closureDir, spec), "package.json"), "utf-8"));
|
|
762
788
|
if (typeof raw === "object" && raw !== null) {
|
|
@@ -767,7 +793,7 @@ var require_dist = __commonJS({
|
|
|
767
793
|
}
|
|
768
794
|
return null;
|
|
769
795
|
}
|
|
770
|
-
__name(
|
|
796
|
+
__name(readClosureVersion2, "readClosureVersion2");
|
|
771
797
|
function runNodeStarter(options) {
|
|
772
798
|
const env = options.env ?? process.env;
|
|
773
799
|
const now = options.now ?? Date.now;
|
|
@@ -800,7 +826,7 @@ var require_dist = __commonJS({
|
|
|
800
826
|
}
|
|
801
827
|
const plan = planBoot(invalid === null);
|
|
802
828
|
if (plan.kind === "current") {
|
|
803
|
-
const version =
|
|
829
|
+
const version = readClosureVersion2(activeRootDir, options.spec);
|
|
804
830
|
const entry = currentEntryPath(rootDir, options.spec);
|
|
805
831
|
console.log(`[starter] booting ${options.spec.packageName}@${version ?? "?"} from ${activeRootDir}`);
|
|
806
832
|
env[options.envNames.bootMode] = "data-root";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.72",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
"test:watch": "vitest"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@camstack/addon-admin-ui": "1.1.
|
|
26
|
+
"@camstack/addon-admin-ui": "1.1.59",
|
|
27
27
|
"@camstack/addon-advanced-notifier": "1.1.30",
|
|
28
28
|
"@camstack/addon-auth": "1.1.15",
|
|
29
29
|
"@camstack/addon-decoder-nodeav": "1.1.18",
|
|
30
30
|
"@camstack/addon-notifiers": "1.1.30",
|
|
31
|
-
"@camstack/addon-pipeline": "1.1.
|
|
32
|
-
"@camstack/addon-pipeline-orchestrator": "1.1.
|
|
33
|
-
"@camstack/addon-post-analysis": "1.1.
|
|
31
|
+
"@camstack/addon-pipeline": "1.1.65",
|
|
32
|
+
"@camstack/addon-pipeline-orchestrator": "1.1.54",
|
|
33
|
+
"@camstack/addon-post-analysis": "1.1.37",
|
|
34
34
|
"@camstack/sdk": "1.1.30",
|
|
35
35
|
"@camstack/shm-ring": "1.0.29",
|
|
36
|
-
"@camstack/system": "1.1.
|
|
37
|
-
"@camstack/types": "1.1.
|
|
38
|
-
"@camstack/ui-library": "1.1.
|
|
36
|
+
"@camstack/system": "1.1.58",
|
|
37
|
+
"@camstack/types": "1.1.52",
|
|
38
|
+
"@camstack/ui-library": "1.1.43",
|
|
39
39
|
"@fastify/compress": "^9.0.0",
|
|
40
40
|
"@fastify/cookie": "^11.0.2",
|
|
41
41
|
"@fastify/multipart": "^10.0.0",
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Boot-time framework-swap job resume + health confirm.
|
|
4
|
-
*
|
|
5
|
-
* After a framework swap reboot, `post-boot.service.ts` calls this once the
|
|
6
|
-
* hub is healthy. It:
|
|
7
|
-
* 1. Reads `.framework-swap-confirm.json` (written by the launcher on apply).
|
|
8
|
-
* 2. Marks the journal task `applied` → `done`, then finalises the job →
|
|
9
|
-
* `completed`.
|
|
10
|
-
* 3. Calls `confirmFrameworkSwapHealthy` to delete the confirm marker +
|
|
11
|
-
* backup dirs (disarms the crash-loop rollback).
|
|
12
|
-
*
|
|
13
|
-
* Best-effort: a missing/corrupt journal is tolerated — `confirmFrameworkSwapHealthy`
|
|
14
|
-
* is still called so the rollback is always disarmed when the hub boots healthy.
|
|
15
|
-
*/
|
|
16
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
-
if (k2 === undefined) k2 = k;
|
|
18
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
-
}
|
|
22
|
-
Object.defineProperty(o, k2, desc);
|
|
23
|
-
}) : (function(o, m, k, k2) {
|
|
24
|
-
if (k2 === undefined) k2 = k;
|
|
25
|
-
o[k2] = m[k];
|
|
26
|
-
}));
|
|
27
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
-
}) : function(o, v) {
|
|
30
|
-
o["default"] = v;
|
|
31
|
-
});
|
|
32
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
-
var ownKeys = function(o) {
|
|
34
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
-
var ar = [];
|
|
36
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
-
return ar;
|
|
38
|
-
};
|
|
39
|
-
return ownKeys(o);
|
|
40
|
-
};
|
|
41
|
-
return function (mod) {
|
|
42
|
-
if (mod && mod.__esModule) return mod;
|
|
43
|
-
var result = {};
|
|
44
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
-
__setModuleDefault(result, mod);
|
|
46
|
-
return result;
|
|
47
|
-
};
|
|
48
|
-
})();
|
|
49
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
-
exports.resumeFrameworkSwapJob = resumeFrameworkSwapJob;
|
|
51
|
-
const fs = __importStar(require("node:fs"));
|
|
52
|
-
const path = __importStar(require("node:path"));
|
|
53
|
-
const types_1 = require("@camstack/types");
|
|
54
|
-
const system_1 = require("@camstack/system");
|
|
55
|
-
const launcher_framework_swap_js_1 = require("../launcher-framework-swap.js");
|
|
56
|
-
const lifecycle_journal_path_js_1 = require("../lifecycle-journal-path.js");
|
|
57
|
-
const SWAP_CONFIRM_FILE = '.framework-swap-confirm.json';
|
|
58
|
-
/**
|
|
59
|
-
* Resume a framework-swap journal job to `done`/`completed` and confirm the
|
|
60
|
-
* hub is healthy (deletes the confirm marker + backups).
|
|
61
|
-
*
|
|
62
|
-
* @returns `{ resumed: false }` when no confirm marker exists.
|
|
63
|
-
* `{ resumed: true, jobId }` when the marker was found and processed.
|
|
64
|
-
* Never throws — errors are swallowed to avoid crashing the post-boot path.
|
|
65
|
-
*/
|
|
66
|
-
async function resumeFrameworkSwapJob(dataDir) {
|
|
67
|
-
try {
|
|
68
|
-
const confirmMarker = readConfirmMarker(dataDir);
|
|
69
|
-
if (confirmMarker === null) {
|
|
70
|
-
return { resumed: false };
|
|
71
|
-
}
|
|
72
|
-
const { jobId, taskId } = confirmMarker;
|
|
73
|
-
let journalPatched = false;
|
|
74
|
-
try {
|
|
75
|
-
const journal = new system_1.JobJournal((0, lifecycle_journal_path_js_1.lifecycleJobsDir)(dataDir));
|
|
76
|
-
const job = journal.getJob(jobId);
|
|
77
|
-
if (job !== null) {
|
|
78
|
-
const task = job.tasks.find((t) => t.taskId === taskId);
|
|
79
|
-
if (task !== undefined && task.phase === 'applied') {
|
|
80
|
-
journal.patchTask(jobId, taskId, { phase: 'done', finishedAtMs: Date.now() });
|
|
81
|
-
// Single-task framework job: if all tasks are now terminal and none
|
|
82
|
-
// failed, mark the job completed (mirrors the engine's finalize logic).
|
|
83
|
-
const updatedJob = journal.getJob(jobId);
|
|
84
|
-
if (updatedJob !== null) {
|
|
85
|
-
const allTerminal = updatedJob.tasks.every((t) => t.phase === 'done' || t.phase === 'failed' || t.phase === 'skipped');
|
|
86
|
-
const anyFailed = updatedJob.tasks.some((t) => t.phase === 'failed');
|
|
87
|
-
if (allTerminal && !anyFailed) {
|
|
88
|
-
journal.setJobState(jobId, 'completed');
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
journalPatched = true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// Journal is missing or corrupt — still clean up the confirm marker so
|
|
97
|
-
// the rollback is disarmed on a healthy hub boot.
|
|
98
|
-
}
|
|
99
|
-
(0, launcher_framework_swap_js_1.confirmFrameworkSwapHealthy)(dataDir);
|
|
100
|
-
return journalPatched ? { resumed: true, jobId } : { resumed: false };
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
// Never crash the caller (post-boot service).
|
|
104
|
-
return { resumed: false };
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/** Read and shape-check the confirm marker. Returns null on any error. */
|
|
108
|
-
function readConfirmMarker(dataDir) {
|
|
109
|
-
try {
|
|
110
|
-
const raw = JSON.parse(fs.readFileSync(path.join(dataDir, SWAP_CONFIRM_FILE), 'utf-8'));
|
|
111
|
-
const parsed = types_1.frameworkSwapConfirmSchema.safeParse(raw);
|
|
112
|
-
if (!parsed.success)
|
|
113
|
-
return null;
|
|
114
|
-
return { jobId: parsed.data.jobId, taskId: parsed.data.taskId };
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
}
|