@getmikie/cli 1.1.103 → 1.1.104
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/bin/mikie.mjs +39 -1
- package/lib/launcher.mjs +460 -23
- package/lib/update.mjs +217 -22
- package/package.json +5 -5
- package/scripts/reconcile-install.mjs +477 -1
package/lib/update.mjs
CHANGED
|
@@ -66,6 +66,8 @@ if (
|
|
|
66
66
|
descriptor.transaction_root !== transactionRoot ||
|
|
67
67
|
descriptor.token !== journal.token ||
|
|
68
68
|
journal.recovery?.descriptor_sha256 !== expectedDescriptorSha256 ||
|
|
69
|
+
descriptor.final_launcher !== journal.prepared_candidate?.final_launcher ||
|
|
70
|
+
descriptor.final_launcher_sha256 !== journal.prepared_candidate?.final_launcher_sha256 ||
|
|
69
71
|
![descriptor.runtime, descriptor.launcher].every(
|
|
70
72
|
(artifactPath) => typeof artifactPath === "string" && inside(transactionRoot, artifactPath),
|
|
71
73
|
)
|
|
@@ -719,7 +721,9 @@ export async function runUpdateTransaction({
|
|
|
719
721
|
if (versionOrder < 0) {
|
|
720
722
|
throw new Error("registry resolved an older Mikie version");
|
|
721
723
|
}
|
|
722
|
-
|
|
724
|
+
const reconcileCurrentInstallation =
|
|
725
|
+
versionOrder === 0 && files.requiresFinalLauncherSettlement?.() === true;
|
|
726
|
+
if (versionOrder === 0 && !reconcileCurrentInstallation) {
|
|
723
727
|
output.writeLine(`Mikie is already current at ${installedCurrentVersion}.`);
|
|
724
728
|
return { status: "current", version: installedCurrentVersion };
|
|
725
729
|
}
|
|
@@ -757,7 +761,7 @@ export async function runUpdateTransaction({
|
|
|
757
761
|
if (wrapperVersion !== resolved.version || candidatePlatformVersion !== resolved.version) {
|
|
758
762
|
throw new Error("candidate package version verification failed");
|
|
759
763
|
}
|
|
760
|
-
finalLauncher = files.finalLauncher();
|
|
764
|
+
finalLauncher = await files.finalLauncher(candidate, resolved.version);
|
|
761
765
|
const externalRecovery = await files.recordPreparedCandidate(
|
|
762
766
|
snapshot,
|
|
763
767
|
candidate,
|
|
@@ -775,6 +779,7 @@ export async function runUpdateTransaction({
|
|
|
775
779
|
MIKIE_DISTRIBUTION_MODE: "npm",
|
|
776
780
|
MIKIE_NPM_UPDATE_TOPOLOGY: UpdateTopology.NpmGlobal,
|
|
777
781
|
MIKIE_NPM_UPDATE_REPAIR_PHASE: "prepare",
|
|
782
|
+
MIKIE_UPDATE_REPAIR_LAUNCHER_STATE: "prior",
|
|
778
783
|
MIKIE_UPDATE_REPAIR_CANDIDATE: candidate.binary,
|
|
779
784
|
MIKIE_UPDATE_REPAIR_LAUNCHER: finalLauncher,
|
|
780
785
|
},
|
|
@@ -810,8 +815,21 @@ export async function runUpdateTransaction({
|
|
|
810
815
|
await files.verifyActivatedCandidate(candidate, activatedCandidate);
|
|
811
816
|
await files.activate(activatedCandidate);
|
|
812
817
|
await files.checkpointCandidate(snapshot, { provider: true });
|
|
813
|
-
const settlementRequired =
|
|
814
|
-
|
|
818
|
+
const settlementRequired =
|
|
819
|
+
repairResult.receipt.action === "migrated" ||
|
|
820
|
+
files.requiresFinalLauncherSettlement?.() === true;
|
|
821
|
+
// A skipped receipt does not prove that no client still references a
|
|
822
|
+
// historical launcher. In particular, Claude can report unavailable
|
|
823
|
+
// when hooks/ownership are absent while an MCP-only registration remains.
|
|
824
|
+
// Retain until a later authenticated reconciliation proves both surfaces
|
|
825
|
+
// absent/current instead of deleting a possibly referenced runtime.
|
|
826
|
+
const retainHistoricalRuntimes = ["foreign_conflict", "unavailable"].includes(
|
|
827
|
+
repairResult.receipt.before,
|
|
828
|
+
);
|
|
829
|
+
await files.decideCommit(snapshot, {
|
|
830
|
+
settlementRequired,
|
|
831
|
+
...(retainHistoricalRuntimes ? { retainHistoricalRuntimes: true } : {}),
|
|
832
|
+
});
|
|
815
833
|
commitDecided = true;
|
|
816
834
|
await files.reconcileTrajectoryStore(snapshot);
|
|
817
835
|
if (settlementRequired) {
|
|
@@ -822,6 +840,7 @@ export async function runUpdateTransaction({
|
|
|
822
840
|
MIKIE_DISTRIBUTION_MODE: "npm",
|
|
823
841
|
MIKIE_NPM_UPDATE_TOPOLOGY: UpdateTopology.NpmGlobal,
|
|
824
842
|
MIKIE_NPM_UPDATE_REPAIR_PHASE: "commit",
|
|
843
|
+
MIKIE_UPDATE_REPAIR_LAUNCHER_STATE: "activated",
|
|
825
844
|
MIKIE_UPDATE_REPAIR_CANDIDATE: candidate.binary,
|
|
826
845
|
MIKIE_UPDATE_REPAIR_LAUNCHER: finalLauncher,
|
|
827
846
|
},
|
|
@@ -830,9 +849,12 @@ export async function runUpdateTransaction({
|
|
|
830
849
|
throw new Error("candidate owned-adapter settlement failed");
|
|
831
850
|
}
|
|
832
851
|
}
|
|
833
|
-
await files.retire(
|
|
852
|
+
await files.retire(
|
|
853
|
+
snapshot,
|
|
854
|
+
retainHistoricalRuntimes ? { retainHistoricalRuntimes: true } : undefined,
|
|
855
|
+
);
|
|
834
856
|
return {
|
|
835
|
-
status: "updated",
|
|
857
|
+
status: reconcileCurrentInstallation ? "reconciled" : "updated",
|
|
836
858
|
version: resolved.version,
|
|
837
859
|
providerState: repairResult.receipt.after,
|
|
838
860
|
adapterAction: repairResult.skipped
|
|
@@ -853,6 +875,8 @@ export async function runUpdateTransaction({
|
|
|
853
875
|
MIKIE_DISTRIBUTION_MODE: "npm",
|
|
854
876
|
MIKIE_NPM_UPDATE_TOPOLOGY: UpdateTopology.NpmGlobal,
|
|
855
877
|
MIKIE_NPM_UPDATE_REPAIR_PHASE: "rollback",
|
|
878
|
+
MIKIE_UPDATE_REPAIR_LAUNCHER_STATE:
|
|
879
|
+
files.preparedLauncherState?.(snapshot) ?? "prior",
|
|
856
880
|
MIKIE_UPDATE_REPAIR_CANDIDATE: candidate.binary,
|
|
857
881
|
MIKIE_UPDATE_REPAIR_LAUNCHER: finalLauncher,
|
|
858
882
|
},
|
|
@@ -868,6 +892,9 @@ export async function runUpdateTransaction({
|
|
|
868
892
|
if (!commitDecided && !restoreFailure) {
|
|
869
893
|
try {
|
|
870
894
|
await files.restore(snapshot);
|
|
895
|
+
if (snapshot.preparedCandidate) {
|
|
896
|
+
await files.abandonPreparedLauncher?.(snapshot);
|
|
897
|
+
}
|
|
871
898
|
} catch (restoreError) {
|
|
872
899
|
restoreFailure = restoreError;
|
|
873
900
|
}
|
|
@@ -1135,6 +1162,33 @@ function bytesFingerprint(existed, bytes) {
|
|
|
1135
1162
|
return existed ? `present:${createHash("sha256").update(bytes).digest("hex")}` : "absent";
|
|
1136
1163
|
}
|
|
1137
1164
|
|
|
1165
|
+
async function executableDigest(filePath) {
|
|
1166
|
+
if (typeof filePath !== "string" || !path.isAbsolute(filePath)) {
|
|
1167
|
+
throw new Error("prepared final launcher authority is invalid");
|
|
1168
|
+
}
|
|
1169
|
+
await assertNoLinkedAncestors(filePath);
|
|
1170
|
+
const before = await lstat(filePath);
|
|
1171
|
+
if (
|
|
1172
|
+
!before.isFile() ||
|
|
1173
|
+
before.isSymbolicLink() ||
|
|
1174
|
+
before.size <= 0 ||
|
|
1175
|
+
before.size > MAX_TRANSACTION_TREE_BYTES
|
|
1176
|
+
) {
|
|
1177
|
+
throw new Error("prepared final launcher authority is invalid");
|
|
1178
|
+
}
|
|
1179
|
+
const bytes = await readFile(filePath);
|
|
1180
|
+
const after = await lstat(filePath);
|
|
1181
|
+
if (
|
|
1182
|
+
!after.isFile() ||
|
|
1183
|
+
after.isSymbolicLink() ||
|
|
1184
|
+
bytes.length !== before.size ||
|
|
1185
|
+
after.size !== before.size
|
|
1186
|
+
) {
|
|
1187
|
+
throw new Error("prepared final launcher changed");
|
|
1188
|
+
}
|
|
1189
|
+
return sha256Bytes(bytes);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1138
1192
|
async function directoryFingerprint(root) {
|
|
1139
1193
|
const digest = createHash("sha256");
|
|
1140
1194
|
let entries = 0;
|
|
@@ -1263,6 +1317,9 @@ export function createFilesystemAdapter({
|
|
|
1263
1317
|
settleProviderRepair,
|
|
1264
1318
|
reconcileTrajectoryStore,
|
|
1265
1319
|
activationBoundary,
|
|
1320
|
+
prepareFinalLauncher,
|
|
1321
|
+
retireFinalLauncher,
|
|
1322
|
+
abandonFinalLauncher,
|
|
1266
1323
|
}) {
|
|
1267
1324
|
const currentRoots = validateInstallationLayout(currentLayout);
|
|
1268
1325
|
const currentShims = currentRoots.shims;
|
|
@@ -1276,7 +1333,10 @@ export function createFilesystemAdapter({
|
|
|
1276
1333
|
!path.isAbsolute(transactionRoot) ||
|
|
1277
1334
|
(settleProviderRepair !== undefined && typeof settleProviderRepair !== "function") ||
|
|
1278
1335
|
(reconcileTrajectoryStore !== undefined && typeof reconcileTrajectoryStore !== "function") ||
|
|
1279
|
-
(activationBoundary !== undefined && typeof activationBoundary !== "function")
|
|
1336
|
+
(activationBoundary !== undefined && typeof activationBoundary !== "function") ||
|
|
1337
|
+
(prepareFinalLauncher !== undefined && typeof prepareFinalLauncher !== "function") ||
|
|
1338
|
+
(retireFinalLauncher !== undefined && typeof retireFinalLauncher !== "function") ||
|
|
1339
|
+
(abandonFinalLauncher !== undefined && typeof abandonFinalLauncher !== "function")
|
|
1280
1340
|
) {
|
|
1281
1341
|
throw new Error("unsafe Mikie update filesystem path");
|
|
1282
1342
|
}
|
|
@@ -1300,6 +1360,7 @@ export function createFilesystemAdapter({
|
|
|
1300
1360
|
}
|
|
1301
1361
|
const lockPath = `${transactionRoot}.lock`;
|
|
1302
1362
|
const journalPath = path.join(transactionRoot, "journal.json");
|
|
1363
|
+
let preparedFinalLauncherAuthority = null;
|
|
1303
1364
|
|
|
1304
1365
|
const readLock = async () => {
|
|
1305
1366
|
try {
|
|
@@ -1348,6 +1409,7 @@ export function createFilesystemAdapter({
|
|
|
1348
1409
|
platform_fingerprint: snapshot.preparedCandidate.platformFingerprint,
|
|
1349
1410
|
shims: snapshot.preparedCandidate.shimFingerprints,
|
|
1350
1411
|
final_launcher: snapshot.preparedCandidate.finalLauncher,
|
|
1412
|
+
final_launcher_sha256: snapshot.preparedCandidate.finalLauncherSha256,
|
|
1351
1413
|
}
|
|
1352
1414
|
: null,
|
|
1353
1415
|
recovery: snapshot.recovery
|
|
@@ -1436,7 +1498,10 @@ export function createFilesystemAdapter({
|
|
|
1436
1498
|
"provider_rolled_back",
|
|
1437
1499
|
"commit_decided",
|
|
1438
1500
|
"commit_decided_no_repair",
|
|
1501
|
+
"commit_decided_retain_runtime",
|
|
1502
|
+
"commit_decided_no_repair_retain_runtime",
|
|
1439
1503
|
"committed",
|
|
1504
|
+
"committed_retain_runtime",
|
|
1440
1505
|
].includes(value.phase) ||
|
|
1441
1506
|
!Array.isArray(value.installation) ||
|
|
1442
1507
|
!Array.isArray(value.shims) ||
|
|
@@ -1599,6 +1664,7 @@ export function createFilesystemAdapter({
|
|
|
1599
1664
|
"platform_fingerprint",
|
|
1600
1665
|
"shims",
|
|
1601
1666
|
"final_launcher",
|
|
1667
|
+
"final_launcher_sha256",
|
|
1602
1668
|
]) ||
|
|
1603
1669
|
![prepared.wrapper_package_json, prepared.platform_package_json, prepared.binary].every(
|
|
1604
1670
|
(candidatePath) =>
|
|
@@ -1619,7 +1685,9 @@ export function createFilesystemAdapter({
|
|
|
1619
1685
|
) ||
|
|
1620
1686
|
new Set(prepared.shims.map((shim) => path.resolve(shim.path))).size !==
|
|
1621
1687
|
prepared.shims.length ||
|
|
1622
|
-
|
|
1688
|
+
typeof prepared.final_launcher !== "string" ||
|
|
1689
|
+
!path.isAbsolute(prepared.final_launcher) ||
|
|
1690
|
+
!/^[a-f0-9]{64}$/.test(prepared.final_launcher_sha256)
|
|
1623
1691
|
) {
|
|
1624
1692
|
throw new Error("invalid Mikie update transaction journal");
|
|
1625
1693
|
}
|
|
@@ -1632,6 +1700,7 @@ export function createFilesystemAdapter({
|
|
|
1632
1700
|
shims: prepared.shims.map((shim) => shim.path),
|
|
1633
1701
|
shimFingerprints: prepared.shims,
|
|
1634
1702
|
finalLauncher: prepared.final_launcher,
|
|
1703
|
+
finalLauncherSha256: prepared.final_launcher_sha256,
|
|
1635
1704
|
};
|
|
1636
1705
|
const preparedRoots = validateInstallationLayout(preparedCandidate);
|
|
1637
1706
|
if (
|
|
@@ -1648,6 +1717,13 @@ export function createFilesystemAdapter({
|
|
|
1648
1717
|
throw new Error("corrupt Mikie staged candidate");
|
|
1649
1718
|
}
|
|
1650
1719
|
}
|
|
1720
|
+
if (
|
|
1721
|
+
!sameExecutablePath(preparedCandidate.finalLauncher, currentLayout.binary) &&
|
|
1722
|
+
(await executableDigest(preparedCandidate.finalLauncher)) !==
|
|
1723
|
+
preparedCandidate.finalLauncherSha256
|
|
1724
|
+
) {
|
|
1725
|
+
throw new Error("prepared final launcher changed");
|
|
1726
|
+
}
|
|
1651
1727
|
}
|
|
1652
1728
|
let recovery = null;
|
|
1653
1729
|
if (value.recovery !== null) {
|
|
@@ -1715,7 +1791,10 @@ export function createFilesystemAdapter({
|
|
|
1715
1791
|
"candidate_packages",
|
|
1716
1792
|
"commit_decided",
|
|
1717
1793
|
"commit_decided_no_repair",
|
|
1794
|
+
"commit_decided_retain_runtime",
|
|
1795
|
+
"commit_decided_no_repair_retain_runtime",
|
|
1718
1796
|
"committed",
|
|
1797
|
+
"committed_retain_runtime",
|
|
1719
1798
|
].includes(value.phase) &&
|
|
1720
1799
|
(!preparedCandidate || !recovery)
|
|
1721
1800
|
) {
|
|
@@ -2223,6 +2302,45 @@ export function createFilesystemAdapter({
|
|
|
2223
2302
|
}
|
|
2224
2303
|
};
|
|
2225
2304
|
|
|
2305
|
+
const retirePreparedLauncher = async (snapshot) => {
|
|
2306
|
+
if (!retireFinalLauncher) return;
|
|
2307
|
+
const authority = snapshot?.preparedCandidate;
|
|
2308
|
+
if (
|
|
2309
|
+
!authority ||
|
|
2310
|
+
(await executableDigest(authority.finalLauncher)) !== authority.finalLauncherSha256
|
|
2311
|
+
) {
|
|
2312
|
+
throw new Error("prepared final launcher changed before retirement");
|
|
2313
|
+
}
|
|
2314
|
+
await retireFinalLauncher({
|
|
2315
|
+
path: authority.finalLauncher,
|
|
2316
|
+
sha256: authority.finalLauncherSha256,
|
|
2317
|
+
});
|
|
2318
|
+
};
|
|
2319
|
+
|
|
2320
|
+
const abandonPreparedLauncher = async (snapshot) => {
|
|
2321
|
+
if (!abandonFinalLauncher || !snapshot?.preparedCandidate) return;
|
|
2322
|
+
const authority = snapshot.preparedCandidate;
|
|
2323
|
+
if ((await executableDigest(authority.finalLauncher)) !== authority.finalLauncherSha256) {
|
|
2324
|
+
throw new Error("prepared final launcher changed before abandonment");
|
|
2325
|
+
}
|
|
2326
|
+
await abandonFinalLauncher({
|
|
2327
|
+
path: authority.finalLauncher,
|
|
2328
|
+
sha256: authority.finalLauncherSha256,
|
|
2329
|
+
});
|
|
2330
|
+
};
|
|
2331
|
+
|
|
2332
|
+
const preparedLauncherState = (snapshot) => {
|
|
2333
|
+
const authority = snapshot?.preparedCandidate;
|
|
2334
|
+
if (!authority) throw new Error("prepared final launcher authority is unavailable");
|
|
2335
|
+
if (!sameExecutablePath(authority.finalLauncher, currentLayout.binary)) return "activated";
|
|
2336
|
+
const owner = snapshot.installation.find(
|
|
2337
|
+
(entry) =>
|
|
2338
|
+
sameExecutablePath(entry.root, currentRoots.platformRoot) ||
|
|
2339
|
+
isPathInside(entry.root, currentLayout.binary),
|
|
2340
|
+
);
|
|
2341
|
+
return owner?.activationState === "activated" ? "activated" : "prior";
|
|
2342
|
+
};
|
|
2343
|
+
|
|
2226
2344
|
const recoverInterrupted = async (lease, expectedJournalToken) => {
|
|
2227
2345
|
try {
|
|
2228
2346
|
const metadata = await lstat(transactionRoot);
|
|
@@ -2234,7 +2352,8 @@ export function createFilesystemAdapter({
|
|
|
2234
2352
|
throw error;
|
|
2235
2353
|
}
|
|
2236
2354
|
const snapshot = await hydrateJournal(lease, expectedJournalToken);
|
|
2237
|
-
if (snapshot.phase
|
|
2355
|
+
if (["commit_decided", "commit_decided_retain_runtime"].includes(snapshot.phase)) {
|
|
2356
|
+
const retainHistoricalRuntimes = snapshot.phase === "commit_decided_retain_runtime";
|
|
2238
2357
|
if (!reconcileTrajectoryStore) {
|
|
2239
2358
|
throw new Error("trajectory-store reconciliation is unavailable");
|
|
2240
2359
|
}
|
|
@@ -2242,25 +2361,39 @@ export function createFilesystemAdapter({
|
|
|
2242
2361
|
if (!settleProviderRepair) {
|
|
2243
2362
|
throw new Error("candidate owned-adapter settlement is unavailable");
|
|
2244
2363
|
}
|
|
2245
|
-
await settleProviderRepair("commit", snapshot);
|
|
2246
|
-
snapshot.phase = "committed";
|
|
2364
|
+
await settleProviderRepair("commit", snapshot, "activated");
|
|
2365
|
+
snapshot.phase = retainHistoricalRuntimes ? "committed_retain_runtime" : "committed";
|
|
2247
2366
|
await writeJournal(snapshot);
|
|
2367
|
+
if (!retainHistoricalRuntimes) await retirePreparedLauncher(snapshot);
|
|
2248
2368
|
await cleanupCommittedActivation(snapshot);
|
|
2249
2369
|
await rm(transactionRoot, { recursive: true, force: true });
|
|
2250
2370
|
return;
|
|
2251
2371
|
}
|
|
2252
|
-
if (
|
|
2372
|
+
if (
|
|
2373
|
+
["commit_decided_no_repair", "commit_decided_no_repair_retain_runtime"].includes(
|
|
2374
|
+
snapshot.phase,
|
|
2375
|
+
)
|
|
2376
|
+
) {
|
|
2377
|
+
const retainHistoricalRuntimes =
|
|
2378
|
+
snapshot.phase === "commit_decided_no_repair_retain_runtime";
|
|
2253
2379
|
if (!reconcileTrajectoryStore) {
|
|
2254
2380
|
throw new Error("trajectory-store reconciliation is unavailable");
|
|
2255
2381
|
}
|
|
2256
2382
|
await reconcileTrajectoryStore(snapshot.preparedCandidate.finalLauncher, snapshot);
|
|
2257
|
-
snapshot.phase = "committed";
|
|
2383
|
+
snapshot.phase = retainHistoricalRuntimes ? "committed_retain_runtime" : "committed";
|
|
2258
2384
|
await writeJournal(snapshot);
|
|
2385
|
+
if (!retainHistoricalRuntimes) await retirePreparedLauncher(snapshot);
|
|
2259
2386
|
await cleanupCommittedActivation(snapshot);
|
|
2260
2387
|
await rm(transactionRoot, { recursive: true, force: true });
|
|
2261
2388
|
return;
|
|
2262
2389
|
}
|
|
2263
2390
|
if (snapshot.phase === "committed") {
|
|
2391
|
+
await retirePreparedLauncher(snapshot);
|
|
2392
|
+
await cleanupCommittedActivation(snapshot);
|
|
2393
|
+
await rm(transactionRoot, { recursive: true, force: true });
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2396
|
+
if (snapshot.phase === "committed_retain_runtime") {
|
|
2264
2397
|
await cleanupCommittedActivation(snapshot);
|
|
2265
2398
|
await rm(transactionRoot, { recursive: true, force: true });
|
|
2266
2399
|
return;
|
|
@@ -2269,11 +2402,12 @@ export function createFilesystemAdapter({
|
|
|
2269
2402
|
if (!settleProviderRepair) {
|
|
2270
2403
|
throw new Error("candidate owned-adapter settlement is unavailable");
|
|
2271
2404
|
}
|
|
2272
|
-
await settleProviderRepair("rollback", snapshot);
|
|
2405
|
+
await settleProviderRepair("rollback", snapshot, preparedLauncherState(snapshot));
|
|
2273
2406
|
snapshot.phase = "provider_rolled_back";
|
|
2274
2407
|
await writeJournal(snapshot);
|
|
2275
2408
|
}
|
|
2276
2409
|
await restoreSnapshot(snapshot);
|
|
2410
|
+
await abandonPreparedLauncher(snapshot);
|
|
2277
2411
|
};
|
|
2278
2412
|
|
|
2279
2413
|
const beginTransaction = async () => {
|
|
@@ -2440,11 +2574,45 @@ export function createFilesystemAdapter({
|
|
|
2440
2574
|
}
|
|
2441
2575
|
return snapshot.stagingPrefix;
|
|
2442
2576
|
},
|
|
2443
|
-
|
|
2444
|
-
return
|
|
2577
|
+
requiresFinalLauncherSettlement() {
|
|
2578
|
+
return prepareFinalLauncher !== undefined;
|
|
2579
|
+
},
|
|
2580
|
+
preparedLauncherState(snapshot) {
|
|
2581
|
+
return preparedLauncherState(snapshot);
|
|
2582
|
+
},
|
|
2583
|
+
async finalLauncher(candidate, version) {
|
|
2584
|
+
const candidateRoots = validateInstallationLayout(candidate);
|
|
2585
|
+
if (
|
|
2586
|
+
!exactPublicVersion(version) ||
|
|
2587
|
+
!isPathInside(path.join(transactionRoot, "candidate-prefix"), candidateRoots.wrapperRoot) ||
|
|
2588
|
+
!isPathInside(path.join(transactionRoot, "candidate-prefix"), candidateRoots.platformRoot)
|
|
2589
|
+
) {
|
|
2590
|
+
throw new Error("prepared final launcher authority is invalid");
|
|
2591
|
+
}
|
|
2592
|
+
const finalLauncher = prepareFinalLauncher
|
|
2593
|
+
? await prepareFinalLauncher(candidate, version)
|
|
2594
|
+
: currentLayout.binary;
|
|
2595
|
+
const candidateSha256 = await executableDigest(candidate.binary);
|
|
2596
|
+
if (
|
|
2597
|
+
typeof finalLauncher !== "string" ||
|
|
2598
|
+
!path.isAbsolute(finalLauncher) ||
|
|
2599
|
+
isPathInside(transactionRoot, finalLauncher) ||
|
|
2600
|
+
(prepareFinalLauncher && (await executableDigest(finalLauncher)) !== candidateSha256)
|
|
2601
|
+
) {
|
|
2602
|
+
throw new Error("prepared final launcher authority is invalid");
|
|
2603
|
+
}
|
|
2604
|
+
preparedFinalLauncherAuthority = {
|
|
2605
|
+
path: finalLauncher,
|
|
2606
|
+
sha256: candidateSha256,
|
|
2607
|
+
};
|
|
2608
|
+
return finalLauncher;
|
|
2445
2609
|
},
|
|
2446
2610
|
async recordPreparedCandidate(snapshot, candidate, finalLauncher) {
|
|
2447
2611
|
const candidateRoots = validateInstallationLayout(candidate);
|
|
2612
|
+
const authority = preparedFinalLauncherAuthority ?? {
|
|
2613
|
+
path: currentLayout.binary,
|
|
2614
|
+
sha256: await executableDigest(candidate.binary),
|
|
2615
|
+
};
|
|
2448
2616
|
if (
|
|
2449
2617
|
!isPathInside(snapshot.stagingPrefix, candidateRoots.wrapperRoot) ||
|
|
2450
2618
|
!isPathInside(snapshot.stagingPrefix, candidateRoots.platformRoot) ||
|
|
@@ -2455,7 +2623,9 @@ export function createFilesystemAdapter({
|
|
|
2455
2623
|
!isPathInside(snapshot.stagingPrefix, shim) ||
|
|
2456
2624
|
path.basename(shim).toLowerCase() !== path.basename(currentShims[index]).toLowerCase(),
|
|
2457
2625
|
) ||
|
|
2458
|
-
!sameExecutablePath(finalLauncher,
|
|
2626
|
+
!sameExecutablePath(finalLauncher, authority.path) ||
|
|
2627
|
+
(prepareFinalLauncher && (await executableDigest(finalLauncher)) !== authority.sha256) ||
|
|
2628
|
+
(await executableDigest(candidate.binary)) !== authority.sha256
|
|
2459
2629
|
) {
|
|
2460
2630
|
throw new Error("unsafe Mikie staged candidate");
|
|
2461
2631
|
}
|
|
@@ -2482,6 +2652,7 @@ export function createFilesystemAdapter({
|
|
|
2482
2652
|
shims: candidateRoots.shims,
|
|
2483
2653
|
shimFingerprints,
|
|
2484
2654
|
finalLauncher,
|
|
2655
|
+
finalLauncherSha256: authority.sha256,
|
|
2485
2656
|
};
|
|
2486
2657
|
const runtime = path.join(transactionRoot, "recovery-runtime.mjs");
|
|
2487
2658
|
const launcher = path.join(transactionRoot, "recover.mjs");
|
|
@@ -2506,6 +2677,7 @@ export function createFilesystemAdapter({
|
|
|
2506
2677
|
node: process.execPath,
|
|
2507
2678
|
candidate_binary: candidate.binary,
|
|
2508
2679
|
final_launcher: finalLauncher,
|
|
2680
|
+
final_launcher_sha256: authority.sha256,
|
|
2509
2681
|
};
|
|
2510
2682
|
const descriptorBytes = Buffer.from(`${JSON.stringify(recoveryDescriptor)}\n`);
|
|
2511
2683
|
await atomicWritePrivate(descriptor, descriptorBytes);
|
|
@@ -2749,8 +2921,17 @@ export function createFilesystemAdapter({
|
|
|
2749
2921
|
await writeJournal(snapshot);
|
|
2750
2922
|
}
|
|
2751
2923
|
},
|
|
2752
|
-
async decideCommit(
|
|
2753
|
-
snapshot
|
|
2924
|
+
async decideCommit(
|
|
2925
|
+
snapshot,
|
|
2926
|
+
{ settlementRequired = true, retainHistoricalRuntimes = false } = {},
|
|
2927
|
+
) {
|
|
2928
|
+
snapshot.phase = retainHistoricalRuntimes
|
|
2929
|
+
? settlementRequired
|
|
2930
|
+
? "commit_decided_retain_runtime"
|
|
2931
|
+
: "commit_decided_no_repair_retain_runtime"
|
|
2932
|
+
: settlementRequired
|
|
2933
|
+
? "commit_decided"
|
|
2934
|
+
: "commit_decided_no_repair";
|
|
2754
2935
|
await writeJournal(snapshot);
|
|
2755
2936
|
},
|
|
2756
2937
|
async reconcileTrajectoryStore(snapshot) {
|
|
@@ -2865,9 +3046,13 @@ export function createFilesystemAdapter({
|
|
|
2865
3046
|
await restoreSnapshot(snapshot);
|
|
2866
3047
|
if (snapshot.autoLease) await releaseLease(snapshot.lease);
|
|
2867
3048
|
},
|
|
2868
|
-
async
|
|
2869
|
-
snapshot
|
|
3049
|
+
async abandonPreparedLauncher(snapshot) {
|
|
3050
|
+
await abandonPreparedLauncher(snapshot);
|
|
3051
|
+
},
|
|
3052
|
+
async retire(snapshot, { retainHistoricalRuntimes = false } = {}) {
|
|
3053
|
+
snapshot.phase = retainHistoricalRuntimes ? "committed_retain_runtime" : "committed";
|
|
2870
3054
|
await writeJournal(snapshot);
|
|
3055
|
+
if (!retainHistoricalRuntimes) await retirePreparedLauncher(snapshot);
|
|
2871
3056
|
await cleanupCommittedActivation(snapshot);
|
|
2872
3057
|
await rm(snapshot.backupRoot, { recursive: true, force: true });
|
|
2873
3058
|
if (snapshot.autoLease) await releaseLease(snapshot.lease);
|
|
@@ -2908,6 +3093,7 @@ export async function recoverExternalNpmUpdate(transactionRoot, expectedAuthorit
|
|
|
2908
3093
|
"node",
|
|
2909
3094
|
"candidate_binary",
|
|
2910
3095
|
"final_launcher",
|
|
3096
|
+
"final_launcher_sha256",
|
|
2911
3097
|
]) ||
|
|
2912
3098
|
descriptor.schema_version !== RECOVERY_SCHEMA ||
|
|
2913
3099
|
descriptor.transaction_root !== transactionRoot ||
|
|
@@ -2916,7 +3102,10 @@ export async function recoverExternalNpmUpdate(transactionRoot, expectedAuthorit
|
|
|
2916
3102
|
(artifactPath) =>
|
|
2917
3103
|
typeof artifactPath === "string" && isPathInside(transactionRoot, artifactPath),
|
|
2918
3104
|
) ||
|
|
2919
|
-
descriptor.final_launcher !==
|
|
3105
|
+
typeof descriptor.final_launcher !== "string" ||
|
|
3106
|
+
!path.isAbsolute(descriptor.final_launcher) ||
|
|
3107
|
+
!/^[a-f0-9]{64}$/.test(descriptor.final_launcher_sha256) ||
|
|
3108
|
+
typeof descriptor.current_layout?.binary !== "string" ||
|
|
2920
3109
|
descriptor.node !== process.execPath ||
|
|
2921
3110
|
!/^[a-f0-9]{64}$/.test(descriptor.runtime_sha256) ||
|
|
2922
3111
|
!/^[a-f0-9]{64}$/.test(descriptor.launcher_sha256) ||
|
|
@@ -2947,6 +3136,12 @@ export async function recoverExternalNpmUpdate(transactionRoot, expectedAuthorit
|
|
|
2947
3136
|
) {
|
|
2948
3137
|
throw new Error("tampered Mikie update recovery authority");
|
|
2949
3138
|
}
|
|
3139
|
+
if (
|
|
3140
|
+
!sameExecutablePath(descriptor.final_launcher, descriptor.current_layout.binary) &&
|
|
3141
|
+
(await executableDigest(descriptor.final_launcher)) !== descriptor.final_launcher_sha256
|
|
3142
|
+
) {
|
|
3143
|
+
throw new Error("prepared final launcher changed");
|
|
3144
|
+
}
|
|
2950
3145
|
const journal = parseJsonNoDuplicateKeys(journalBytes);
|
|
2951
3146
|
if (journal?.token !== descriptor.token) {
|
|
2952
3147
|
throw new Error("Mikie update recovery authority does not match the journal");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getmikie/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.104",
|
|
4
4
|
"description": "Native Mikie relay launcher for MCP clients",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"node": ">=22"
|
|
25
25
|
},
|
|
26
26
|
"optionalDependencies": {
|
|
27
|
-
"@getmikie/darwin-arm64": "1.1.
|
|
28
|
-
"@getmikie/darwin-x64": "1.1.
|
|
29
|
-
"@getmikie/linux-x64-gnu": "1.1.
|
|
30
|
-
"@getmikie/win32-x64": "1.1.
|
|
27
|
+
"@getmikie/darwin-arm64": "1.1.104",
|
|
28
|
+
"@getmikie/darwin-x64": "1.1.104",
|
|
29
|
+
"@getmikie/linux-x64-gnu": "1.1.104",
|
|
30
|
+
"@getmikie/win32-x64": "1.1.104"
|
|
31
31
|
},
|
|
32
32
|
"publishConfig": {
|
|
33
33
|
"access": "public",
|