plugnz 0.0.7 → 0.0.9
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/plugnz.mjs +937 -81
- package/package.json +1 -1
package/dist/plugnz.mjs
CHANGED
|
@@ -9339,10 +9339,23 @@ var dcode = {
|
|
|
9339
9339
|
continue;
|
|
9340
9340
|
const row = entry;
|
|
9341
9341
|
const path = typeof row.installPath === "string" ? row.installPath : typeof row.install_path === "string" ? row.install_path : undefined;
|
|
9342
|
+
let declaredVersion;
|
|
9343
|
+
if (path && existsSync8(path)) {
|
|
9344
|
+
for (const relative of ["plugin.json", ".claude-plugin/plugin.json", ".codex-plugin/plugin.json"]) {
|
|
9345
|
+
try {
|
|
9346
|
+
const manifest = JSON.parse(readFileSync6(join11(path, relative), "utf8"));
|
|
9347
|
+
if (typeof manifest.version === "string") {
|
|
9348
|
+
declaredVersion = manifest.version;
|
|
9349
|
+
break;
|
|
9350
|
+
}
|
|
9351
|
+
} catch {}
|
|
9352
|
+
}
|
|
9353
|
+
}
|
|
9342
9354
|
const at = id.indexOf("@");
|
|
9343
9355
|
const name = at < 0 ? id : id.slice(0, at);
|
|
9344
9356
|
const marketplace = at < 0 ? undefined : id.slice(at + 1);
|
|
9345
|
-
const
|
|
9357
|
+
const version = typeof row.version === "string" ? row.version : declaredVersion;
|
|
9358
|
+
const plugin = { id, name, ...marketplace ? { marketplace } : {}, ...path && existsSync8(path) ? { path } : {}, ...version === undefined ? {} : { version }, ...enabled.enabledPlugins[id] === true ? { enabled: true } : { enabled: false } };
|
|
9346
9359
|
result.push(plugin);
|
|
9347
9360
|
}
|
|
9348
9361
|
return result;
|
|
@@ -12622,12 +12635,18 @@ function assertOutcome(outcome, snapshots, dryRun, command) {
|
|
|
12622
12635
|
if (outcome["action"] === "not-attempted" && (outcome["route"] !== "none" || outcome["changed"] !== false || outcome["result"] === "succeeded" || outcome["result"] === "pending")) {
|
|
12623
12636
|
throw contradiction(`preflight refusal '${outcome["operationId"]}' must be a non-successful, non-mutating no-route action`);
|
|
12624
12637
|
}
|
|
12625
|
-
if (outcome["action"] === "not-attempted" && (outcome["resourceState"] !== "unknown" || outcome["activationState"] !== "unknown")) {
|
|
12638
|
+
if (!dryRun && outcome["action"] === "not-attempted" && (outcome["resourceState"] !== "unknown" || outcome["activationState"] !== "unknown")) {
|
|
12626
12639
|
throw contradiction(`preflight refusal '${outcome["operationId"]}' cannot claim a terminal resource or activation state`);
|
|
12627
12640
|
}
|
|
12628
12641
|
const containment = outcome["action"] === "retain-prior" || outcome["action"] === "disable-nonconforming";
|
|
12629
12642
|
const appliedDisablement = outcome["action"] === "disable-nonconforming" && outcome["changed"] === true;
|
|
12630
|
-
if (
|
|
12643
|
+
if (outcome["action"] === "disable-nonconforming" && outcome["result"] === "succeeded") {
|
|
12644
|
+
throw contradiction(`disablement '${outcome["operationId"]}' cannot claim Desired convergence`);
|
|
12645
|
+
}
|
|
12646
|
+
if (!dryRun && outcome["action"] === "disable-nonconforming" && outcome["resourceState"] === "retained" && outcome["activationState"] === "inactive" && outcome["changed"] !== true) {
|
|
12647
|
+
throw contradiction(`disablement '${outcome["operationId"]}' cannot claim inactive containment without a verified mutation`);
|
|
12648
|
+
}
|
|
12649
|
+
if (containment && !dryRun && (outcome["action"] === "retain-prior" || isObject2(reason) && reason["category"] === "capability")) {
|
|
12631
12650
|
const expectedResourceState = "retained";
|
|
12632
12651
|
const expectedActivationState = outcome["action"] === "retain-prior" ? "retained-prior" : appliedDisablement ? "inactive" : "active-nonconforming";
|
|
12633
12652
|
if (outcome["result"] !== "failed" || outcome["resourceState"] !== expectedResourceState || outcome["activationState"] !== expectedActivationState) {
|
|
@@ -12654,9 +12673,12 @@ function assertOutcome(outcome, snapshots, dryRun, command) {
|
|
|
12654
12673
|
throw contradiction(`successful Desired outcome '${outcome["operationId"]}' needs an exact native identity`);
|
|
12655
12674
|
}
|
|
12656
12675
|
const terminalStateMatches = outcome["coverage"] === "retirement" ? outcome["resourceState"] === "absent" && outcome["activationState"] === "inactive" : outcome["resourceState"] === "present" && outcome["activationState"] === "active-conforming";
|
|
12657
|
-
if (!terminalStateMatches)
|
|
12676
|
+
if (!dryRun && !terminalStateMatches)
|
|
12658
12677
|
throw contradiction(`successful outcome '${outcome["operationId"]}' contradicts its terminal resource or activation state`);
|
|
12659
12678
|
}
|
|
12679
|
+
if (dryRun && (outcome["resourceState"] === "potentially-changed" || outcome["resourceState"] === "absent" && outcome["activationState"] !== "inactive" || outcome["resourceState"] === "unknown" && outcome["activationState"] !== "unknown")) {
|
|
12680
|
+
throw contradiction(`dry-run outcome '${outcome["operationId"]}' contradicts its observed resource or activation state`);
|
|
12681
|
+
}
|
|
12660
12682
|
if (outcome["result"] === "not-attempted" && outcome["resourceState"] === "potentially-changed") {
|
|
12661
12683
|
throw contradiction(`not-attempted outcome '${outcome["operationId"]}' cannot be potentially changed`);
|
|
12662
12684
|
}
|
|
@@ -13543,7 +13565,8 @@ var dcode0183Managed = createCapabilityEvidenceProfile({
|
|
|
13543
13565
|
},
|
|
13544
13566
|
evidence: [
|
|
13545
13567
|
"docs/evidence/dcode-native-update-0.1.83-20261009.json",
|
|
13546
|
-
"docs/research/dcode-session-findings-2026-10-09.md"
|
|
13568
|
+
"docs/research/dcode-session-findings-2026-10-09.md",
|
|
13569
|
+
"docs/evidence/dcode-managed-lifecycle-20261011.md"
|
|
13547
13570
|
]
|
|
13548
13571
|
});
|
|
13549
13572
|
var capabilityEvidenceProfiles = Object.freeze([
|
|
@@ -14225,6 +14248,12 @@ function createLifecycleHostAdapter(definition) {
|
|
|
14225
14248
|
assertReadbackMatch(handle.prior, observation, "rollback");
|
|
14226
14249
|
return deepFreeze({ phase: "rollback-verified", handle, observation });
|
|
14227
14250
|
},
|
|
14251
|
+
async releaseRecovery(handle) {
|
|
14252
|
+
assertHandle(definition.id, handle);
|
|
14253
|
+
await invokePhase("cleanup", async () => {
|
|
14254
|
+
await definition.releaseRecovery?.(handle);
|
|
14255
|
+
});
|
|
14256
|
+
},
|
|
14228
14257
|
async cleanup(target, disposition) {
|
|
14229
14258
|
const reference = cleanupReference(definition.id, target);
|
|
14230
14259
|
if (isPreparationProjection(target) && disposition !== "aborted-preparation") {
|
|
@@ -20595,10 +20624,431 @@ function assertUnder(root, path) {
|
|
|
20595
20624
|
import { cpSync as cpSync8, existsSync as existsSync23, lstatSync as lstatSync16, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync9, readFileSync as readFileSync22, readdirSync as readdirSync17, realpathSync as realpathSync3, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync9 } from "node:fs";
|
|
20596
20625
|
import { basename as basename5, dirname as dirname11, join as join28, resolve as resolve17 } from "node:path";
|
|
20597
20626
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
20627
|
+
import { spawn as spawnChild } from "node:child_process";
|
|
20598
20628
|
var MARKER7 = ".plgnz-install.json";
|
|
20599
20629
|
var MANIFESTS = ["plugin.json", ".claude-plugin/plugin.json", ".codex-plugin/plugin.json"];
|
|
20600
20630
|
var READBACK_FAULT = ".plgnz-dcode-readback-fault";
|
|
20631
|
+
var NATIVE_LOCK_SCRIPT = `import sys
|
|
20632
|
+
from importlib.metadata import version
|
|
20633
|
+
from filelock import FileLock
|
|
20634
|
+
if version('deepagents-code') != '0.1.83':
|
|
20635
|
+
raise RuntimeError('unverified dcode lock runtime')
|
|
20636
|
+
with FileLock(sys.argv[1]).acquire(timeout=10):
|
|
20637
|
+
print('DCODE_LOCK_0.1.83', flush=True)
|
|
20638
|
+
sys.stdin.read()
|
|
20639
|
+
`;
|
|
20640
|
+
var nativeLocks = new Map;
|
|
20641
|
+
function nativePython() {
|
|
20642
|
+
const override = process.env["OPEN_PLUGIN_DCODE_PYTHON_BIN"];
|
|
20643
|
+
if (override) {
|
|
20644
|
+
assertIdentity2(basename5(override), "Python binary");
|
|
20645
|
+
return override;
|
|
20646
|
+
}
|
|
20647
|
+
const binary = dcodeBinary();
|
|
20648
|
+
if (binary === undefined)
|
|
20649
|
+
throw new Error("dcode native lock runtime is unavailable");
|
|
20650
|
+
const shebang = readFileSync22(binary, "utf8").split(/\r?\n/, 1)[0] ?? "";
|
|
20651
|
+
const match = /^#!(\/[^\s]+python(?:\d+(?:\.\d+)?)?)$/u.exec(shebang);
|
|
20652
|
+
if (match?.[1] === undefined || !existsSync23(match[1]))
|
|
20653
|
+
throw new Error("dcode native Python interpreter is unverified; set OPEN_PLUGIN_DCODE_PYTHON_BIN to its verified interpreter");
|
|
20654
|
+
return match[1];
|
|
20655
|
+
}
|
|
20656
|
+
function probeLifecycleVersion() {
|
|
20657
|
+
const observation = probeDcodeVersion();
|
|
20658
|
+
if (observation.kind !== "detected" || observation.version !== "0.1.83")
|
|
20659
|
+
return observation;
|
|
20660
|
+
const result = spawnSync([nativePython(), "-c", "from importlib.metadata import version; import filelock; assert version('deepagents-code') == '0.1.83'; print('DCODE_LOCK_0.1.83')"], { stdout: "pipe", stderr: "pipe", timeout: 1e4 });
|
|
20661
|
+
if (result.exitCode !== 0 || new TextDecoder().decode(result.stdout).trim() !== "DCODE_LOCK_0.1.83")
|
|
20662
|
+
throw new Error(`dcode native lock runtime is unverified: ${new TextDecoder().decode(result.stderr).trim()}`);
|
|
20663
|
+
return observation;
|
|
20664
|
+
}
|
|
20665
|
+
async function acquireNativeLock(attemptId, operationId) {
|
|
20666
|
+
const key = `${attemptId}:${operationId}`;
|
|
20667
|
+
const existing = nativeLocks.get(key);
|
|
20668
|
+
if (existing !== undefined) {
|
|
20669
|
+
if (!existing.isAlive())
|
|
20670
|
+
throw new Error("dcode native lock was lost before verification");
|
|
20671
|
+
return;
|
|
20672
|
+
}
|
|
20673
|
+
const file = join28(dcodeRoot(), "plugins", ".mutation.lock");
|
|
20674
|
+
assertManagedPath4(file);
|
|
20675
|
+
mkdirSync9(dirname11(file), { recursive: true });
|
|
20676
|
+
const child = spawnChild(nativePython(), ["-c", NATIVE_LOCK_SCRIPT, file], { env: { ...process.env, HOME: process.env["OPEN_PLUGIN_HOME"] || process.env["HOME"], DEEPAGENTS_HOME: dcodeRoot() }, stdio: ["pipe", "pipe", "pipe"] });
|
|
20677
|
+
let alive = true;
|
|
20678
|
+
const exited = new Promise((resolveExit) => child.on("exit", (code) => {
|
|
20679
|
+
alive = false;
|
|
20680
|
+
resolveExit(code);
|
|
20681
|
+
}));
|
|
20682
|
+
await new Promise((resolveLock, rejectLock) => {
|
|
20683
|
+
let output = "", diagnostic = "", settled = false;
|
|
20684
|
+
const finish = (error) => {
|
|
20685
|
+
if (settled)
|
|
20686
|
+
return;
|
|
20687
|
+
settled = true;
|
|
20688
|
+
clearTimeout(timer);
|
|
20689
|
+
if (error) {
|
|
20690
|
+
child.kill("SIGKILL");
|
|
20691
|
+
rejectLock(error);
|
|
20692
|
+
} else
|
|
20693
|
+
resolveLock();
|
|
20694
|
+
};
|
|
20695
|
+
const timer = setTimeout(() => finish(new Error("dcode native lock acquisition timed out")), 12000);
|
|
20696
|
+
child.stdout.on("data", (data) => {
|
|
20697
|
+
output += new TextDecoder().decode(data);
|
|
20698
|
+
if (output.includes(`DCODE_LOCK_0.1.83
|
|
20699
|
+
`))
|
|
20700
|
+
finish();
|
|
20701
|
+
});
|
|
20702
|
+
child.stderr.on("data", (data) => {
|
|
20703
|
+
diagnostic += new TextDecoder().decode(data);
|
|
20704
|
+
});
|
|
20705
|
+
child.on("error", (error) => finish(new Error(`dcode native lock failed: ${error.message}`)));
|
|
20706
|
+
child.on("exit", (code) => finish(new Error(`dcode native lock failed (${code}): ${diagnostic.trim()}`)));
|
|
20707
|
+
});
|
|
20708
|
+
nativeLocks.set(key, { child, exited, isAlive: () => alive });
|
|
20709
|
+
}
|
|
20710
|
+
async function releaseNativeLock(attemptId, operationId) {
|
|
20711
|
+
const key = `${attemptId}:${operationId}`, held = nativeLocks.get(key);
|
|
20712
|
+
if (held === undefined)
|
|
20713
|
+
return;
|
|
20714
|
+
nativeLocks.delete(key);
|
|
20715
|
+
held.child.stdin.end();
|
|
20716
|
+
let timer;
|
|
20717
|
+
try {
|
|
20718
|
+
const code = await Promise.race([held.exited, new Promise((_, reject) => {
|
|
20719
|
+
timer = setTimeout(() => {
|
|
20720
|
+
held.child.kill("SIGKILL");
|
|
20721
|
+
reject(new Error("dcode native lock release timed out"));
|
|
20722
|
+
}, 2000);
|
|
20723
|
+
})]);
|
|
20724
|
+
if (code !== 0)
|
|
20725
|
+
throw new Error(`dcode native lock process failed during mutation (${code})`);
|
|
20726
|
+
} finally {
|
|
20727
|
+
if (timer !== undefined)
|
|
20728
|
+
clearTimeout(timer);
|
|
20729
|
+
}
|
|
20730
|
+
}
|
|
20731
|
+
function lifecycleDirectory(attemptId, operationId) {
|
|
20732
|
+
const hash = new CryptoHasher("sha256");
|
|
20733
|
+
hash.update(`${attemptId}\x00${operationId}`);
|
|
20734
|
+
const dir = join28(dcodeCacheRoot(), "plgnz-operations", hash.digest("hex"));
|
|
20735
|
+
assertCachePath(dir);
|
|
20736
|
+
return dir;
|
|
20737
|
+
}
|
|
20738
|
+
function lifecycleSlot(nativeId, fingerprint) {
|
|
20739
|
+
return managedSlot(nativeId, fingerprint);
|
|
20740
|
+
}
|
|
20741
|
+
function observeDcodeTarget(target) {
|
|
20742
|
+
if (target.kind !== "dcode" || target.instance !== "default")
|
|
20743
|
+
throw new Error("unsupported dcode target identity");
|
|
20744
|
+
const registry = readRegistry3(dcodeRegistryFile());
|
|
20745
|
+
const enabled = boolObject(readEnablement(dcodeEnablementFile()).enabledPlugins, "enabledPlugins");
|
|
20746
|
+
return Object.entries(object3(registry.plugins, "registry plugins")).map(([nativeId, raw]) => {
|
|
20747
|
+
const rows = rowsFor(raw);
|
|
20748
|
+
if (rows.length !== 1)
|
|
20749
|
+
throw new Error(`dcode install record ${nativeId} has duplicate rows`);
|
|
20750
|
+
const path = rowInstall(rows[0], nativeId);
|
|
20751
|
+
assertCachePath(path);
|
|
20752
|
+
const present = existsSync23(path);
|
|
20753
|
+
const marker = present ? ownership3(path) : null;
|
|
20754
|
+
const body = present ? readDoc2(join28(path, MARKER7), {}, "ownership marker") : {};
|
|
20755
|
+
const owned = marker?.pluginId === nativeId;
|
|
20756
|
+
const fingerprint = present ? fingerprintTree(path) : null;
|
|
20757
|
+
return {
|
|
20758
|
+
nativeId,
|
|
20759
|
+
packageName: nativeId.split("@")[0],
|
|
20760
|
+
ownership: owned ? { kind: "owned", proof: body.proof === "adopted" ? "adopted" : "created", scopeId: typeof body.scopeId === "string" ? body.scopeId : "dcode:default", proofId: `dcode-marker:${nativeId}` } : { kind: "unmanaged" },
|
|
20761
|
+
presence: present ? "present" : "absent",
|
|
20762
|
+
enablement: enabled[nativeId] === true ? "enabled" : "disabled",
|
|
20763
|
+
activation: enabled[nativeId] === true && present ? "active" : "inactive",
|
|
20764
|
+
installedFingerprint: fingerprint,
|
|
20765
|
+
installedVersion: present ? declaredManifest(path, nativeId.split("@")[0]).version : null,
|
|
20766
|
+
source: owned ? { type: body.sourceType === "git" ? "git" : "local", immutableRevision: marker.kind === "current" ? marker.sourceRevision : marker.fingerprint, locator: body.sourceType === "git" ? marker.source : null } : null,
|
|
20767
|
+
contentRoots: fingerprint === null ? [] : [{ label: "managed", path, fingerprint }]
|
|
20768
|
+
};
|
|
20769
|
+
});
|
|
20770
|
+
}
|
|
20771
|
+
function dcodeLifecycleReadback(binding) {
|
|
20772
|
+
if (existsSync23(join28(dcodeStateDir(), READBACK_FAULT)))
|
|
20773
|
+
throw new Error(`dcode readback failed for ${binding.nativeId}`);
|
|
20774
|
+
const installations = observeDcodeTarget(binding.target);
|
|
20775
|
+
const row = installations.find((item) => item.nativeId === binding.nativeId);
|
|
20776
|
+
const present = row?.presence === "present";
|
|
20777
|
+
const enabled = row?.enablement === "enabled" && present;
|
|
20778
|
+
const dataRoot = join28(dcodeRoot(), "plugins", "data");
|
|
20779
|
+
assertManagedPath4(dataRoot);
|
|
20780
|
+
return {
|
|
20781
|
+
adapterId: binding.adapterId,
|
|
20782
|
+
target: binding.target,
|
|
20783
|
+
scopeId: binding.scopeId,
|
|
20784
|
+
packageName: binding.packageName,
|
|
20785
|
+
nativeId: binding.nativeId,
|
|
20786
|
+
route: binding.route ?? (present ? "managed" : "none"),
|
|
20787
|
+
presence: present ? "present" : "absent",
|
|
20788
|
+
enablement: enabled ? "enabled" : "disabled",
|
|
20789
|
+
activation: enabled ? "active" : "inactive",
|
|
20790
|
+
transition: { requirement: present ? "restart" : "none", status: "effective" },
|
|
20791
|
+
installedFingerprint: present ? row.installedFingerprint : null,
|
|
20792
|
+
contentRoots: present ? row.contentRoots : [],
|
|
20793
|
+
retention: { pluginData: existsSync23(dataRoot) ? { state: "present", fingerprint: fingerprintTree(dataRoot) } : { state: "absent", fingerprint: null }, inactiveMetadata: { state: "absent", fingerprint: null } }
|
|
20794
|
+
};
|
|
20795
|
+
}
|
|
20796
|
+
function captureDcodeRollback(binding, attemptId, operationId) {
|
|
20797
|
+
const dir = join28(lifecycleDirectory(attemptId, operationId), "rollback");
|
|
20798
|
+
mkdirSync9(dir, { recursive: true });
|
|
20799
|
+
const registry = readRegistry3(dcodeRegistryFile());
|
|
20800
|
+
const enabled = boolObject(readEnablement(dcodeEnablementFile()).enabledPlugins, "enabledPlugins");
|
|
20801
|
+
const row = object3(registry.plugins, "registry plugins")[binding.nativeId];
|
|
20802
|
+
const state = { row: row ?? null, enabled: enabled[binding.nativeId] ?? null, registryExisted: existsSync23(dcodeRegistryFile()), enablementExisted: existsSync23(dcodeEnablementFile()) };
|
|
20803
|
+
writeFileSync9(join28(dir, "state.json"), JSON.stringify(state));
|
|
20804
|
+
for (const [index, item] of rowsFor(row).entries()) {
|
|
20805
|
+
const path = rowInstall(item, binding.nativeId);
|
|
20806
|
+
assertCachePath(path);
|
|
20807
|
+
assertNoSymlinks6(path);
|
|
20808
|
+
if (existsSync23(path))
|
|
20809
|
+
cpSync8(path, join28(dir, `content-${index}`), { recursive: true });
|
|
20810
|
+
}
|
|
20811
|
+
return dir;
|
|
20812
|
+
}
|
|
20813
|
+
async function lockPreparation(binding, attemptId, operationId, expectedObservationId) {
|
|
20814
|
+
await acquireNativeLock(attemptId, operationId);
|
|
20815
|
+
const observed = createTargetInventoryObservation("dcode", { target: binding.target, installations: observeDcodeTarget(binding.target) });
|
|
20816
|
+
if (observed.observationId !== expectedObservationId)
|
|
20817
|
+
throw new Error("dcode native inventory changed before locked preparation; refusing stale rollback authority");
|
|
20818
|
+
}
|
|
20819
|
+
async function captureOwnedPreparation(binding, attemptId, operationId, expectedObservationId, capture) {
|
|
20820
|
+
try {
|
|
20821
|
+
await lockPreparation(binding, attemptId, operationId, expectedObservationId);
|
|
20822
|
+
return capture();
|
|
20823
|
+
} catch (error) {
|
|
20824
|
+
try {
|
|
20825
|
+
await releaseNativeLock(attemptId, operationId);
|
|
20826
|
+
} catch (releaseError) {
|
|
20827
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}; dcode preparation lock release failed: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`);
|
|
20828
|
+
}
|
|
20829
|
+
throw error;
|
|
20830
|
+
}
|
|
20831
|
+
}
|
|
20832
|
+
function preflightAdoption(request) {
|
|
20833
|
+
if (request.operation !== "install" && request.operation !== "update")
|
|
20834
|
+
return;
|
|
20835
|
+
const snapshot = request.snapshot;
|
|
20836
|
+
const installed = request.targetObservation.installations.find((item) => item.nativeId === snapshot.nativeId);
|
|
20837
|
+
if (installed?.ownership.kind !== "unmanaged" || installed.presence !== "present")
|
|
20838
|
+
return;
|
|
20839
|
+
const path = installed.contentRoots[0]?.path;
|
|
20840
|
+
if (path === undefined)
|
|
20841
|
+
throw new Error("dcode unmanaged adoption has no native root");
|
|
20842
|
+
const manifest = declaredManifest(snapshot.packageRoot, snapshot.packageName);
|
|
20843
|
+
assessPrior(object3(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")[snapshot.nativeId], snapshot.nativeId, snapshot.nativeGit?.locator ?? `local:${snapshot.scopeId}`, true, snapshot.packageName, manifest.version, snapshot.packageRoot);
|
|
20844
|
+
if (fingerprintTree(path) !== installed.installedFingerprint)
|
|
20845
|
+
throw new Error("dcode unmanaged adoption changed during preflight");
|
|
20846
|
+
}
|
|
20847
|
+
function setDcodeRow(nativeId, row, enabled) {
|
|
20848
|
+
assertManagedPath4(dcodeRegistryFile());
|
|
20849
|
+
assertManagedPath4(dcodeEnablementFile());
|
|
20850
|
+
const registry = readRegistry3(dcodeRegistryFile()), enablement = readEnablement(dcodeEnablementFile());
|
|
20851
|
+
const plugins = { ...object3(registry.plugins, "registry plugins") }, flags = { ...boolObject(enablement.enabledPlugins, "enabledPlugins") };
|
|
20852
|
+
if (row === null)
|
|
20853
|
+
delete plugins[nativeId];
|
|
20854
|
+
else
|
|
20855
|
+
plugins[nativeId] = row;
|
|
20856
|
+
if (enabled === null)
|
|
20857
|
+
delete flags[nativeId];
|
|
20858
|
+
else
|
|
20859
|
+
flags[nativeId] = enabled;
|
|
20860
|
+
writeDoc2(dcodeRegistryFile(), { ...registry, plugins });
|
|
20861
|
+
writeDoc2(dcodeEnablementFile(), { ...enablement, enabledPlugins: flags });
|
|
20862
|
+
}
|
|
20863
|
+
var dcodeDefinition = {
|
|
20864
|
+
id: "dcode",
|
|
20865
|
+
evidenceProfiles: capabilityEvidenceProfiles.filter((profile) => profile.host === "dcode").flatMap((profile) => [profile, createCapabilityEvidenceProfile({ ...profile, operations: ["disable"], semantics: { ...profile.semantics, "reversible-disable": "supported" }, evidence: [...profile.evidence, "docs/evidence/dcode-managed-lifecycle-20261011.md"] })]),
|
|
20866
|
+
probeVersion: async () => probeLifecycleVersion(),
|
|
20867
|
+
observeTarget: async (target) => ({ target, installations: observeDcodeTarget(target) }),
|
|
20868
|
+
observeNativeMutationScope: async () => ({ kind: "unavailable" }),
|
|
20869
|
+
observeNativeProjection: async (request) => {
|
|
20870
|
+
preflightAdoption(request);
|
|
20871
|
+
return { kind: "requires-managed", reasonId: "dcode-frozen-projection-and-updater-isolation" };
|
|
20872
|
+
},
|
|
20873
|
+
revalidateTargetPrecondition: async (handle) => {
|
|
20874
|
+
await acquireNativeLock(handle.attemptId, handle.operationId);
|
|
20875
|
+
return { version: probeDcodeVersion(), targetObservationId: createTargetInventoryObservation("dcode", { target: handle.target, installations: observeDcodeTarget(handle.target) }).observationId };
|
|
20876
|
+
},
|
|
20877
|
+
stageActivation: async (request) => {
|
|
20878
|
+
const dir = lifecycleDirectory(request.snapshot.attemptId, request.snapshot.operationId);
|
|
20879
|
+
const stagingRoot = join28(dir, "stage");
|
|
20880
|
+
if (existsSync23(stagingRoot))
|
|
20881
|
+
throw new Error("dcode preparation stage already exists");
|
|
20882
|
+
mkdirSync9(stagingRoot, { recursive: true });
|
|
20883
|
+
const manifest = declaredManifest(request.snapshot.packageRoot, request.snapshot.packageName);
|
|
20884
|
+
try {
|
|
20885
|
+
stagePlugin5(request.snapshot.packageRoot, stagingRoot, request.snapshot.packageName, manifest.version);
|
|
20886
|
+
} catch (error) {
|
|
20887
|
+
rmSync9(dir, { recursive: true, force: true });
|
|
20888
|
+
throw error;
|
|
20889
|
+
}
|
|
20890
|
+
return { stagingRoot, stagingId: `${request.snapshot.attemptId}:${request.snapshot.operationId}` };
|
|
20891
|
+
},
|
|
20892
|
+
applyLifecycleDirectives: async (projection) => {
|
|
20893
|
+
forceAutoUpdateOff(projection.stagingRoot);
|
|
20894
|
+
const prior = observeDcodeTarget(projection.target).find((item) => item.nativeId === projection.nativeId);
|
|
20895
|
+
writeFileSync9(join28(projection.stagingRoot, MARKER7), JSON.stringify({ source: projection.sourceLocator ?? `local:${projection.scopeId}`, pluginId: projection.nativeId, fingerprint: projection.sourceRevision, sourceRevision: projection.sourceRevision, projectedFingerprint: projectionDigest(projection.stagingRoot), scopeId: projection.scopeId, sourceType: projection.sourceType, proof: prior?.ownership.kind === "unmanaged" ? "adopted" : "created" }));
|
|
20896
|
+
return ["dcode.managed-unversioned-registry"];
|
|
20897
|
+
},
|
|
20898
|
+
applyPins: async (projection) => {
|
|
20899
|
+
const found = new Set;
|
|
20900
|
+
for (const candidate of dcodeMcpCandidates()) {
|
|
20901
|
+
const file = join28(projection.stagingRoot, candidate.kind === "spec" ? candidate.file : candidate.manifest);
|
|
20902
|
+
if (!existsSync23(file))
|
|
20903
|
+
continue;
|
|
20904
|
+
const body = readDoc2(file, {}, "MCP document");
|
|
20905
|
+
if (typeof body.mcpServers !== "object" || body.mcpServers === null || Array.isArray(body.mcpServers))
|
|
20906
|
+
continue;
|
|
20907
|
+
let changed = false;
|
|
20908
|
+
for (const pin of projection.pins) {
|
|
20909
|
+
const server = body.mcpServers[pin.server];
|
|
20910
|
+
if (typeof server !== "object" || server === null || Array.isArray(server))
|
|
20911
|
+
continue;
|
|
20912
|
+
server.command = pin.executable;
|
|
20913
|
+
found.add(pin.server);
|
|
20914
|
+
changed = true;
|
|
20915
|
+
}
|
|
20916
|
+
if (changed)
|
|
20917
|
+
writeFileSync9(file, JSON.stringify(body, null, 2));
|
|
20918
|
+
}
|
|
20919
|
+
if (projection.pins.some((pin) => !found.has(pin.server)))
|
|
20920
|
+
throw new Error("dcode resolved pin is absent from staged MCP configuration");
|
|
20921
|
+
const marker = readDoc2(join28(projection.stagingRoot, MARKER7), {}, "ownership marker");
|
|
20922
|
+
marker.projectedFingerprint = projectionDigest(projection.stagingRoot);
|
|
20923
|
+
writeFileSync9(join28(projection.stagingRoot, MARKER7), JSON.stringify(marker));
|
|
20924
|
+
return [...found];
|
|
20925
|
+
},
|
|
20926
|
+
captureActivationPreparation: async (projection, fingerprint) => {
|
|
20927
|
+
await lockPreparation(projection, projection.attemptId, projection.operationId, projection.targetObservationId);
|
|
20928
|
+
const prior = dcodeLifecycleReadback(projection);
|
|
20929
|
+
const target = lifecycleSlot(projection.nativeId, fingerprint);
|
|
20930
|
+
assertCachePath(target);
|
|
20931
|
+
const installed = observeDcodeTarget(projection.target).find((item) => item.nativeId === projection.nativeId);
|
|
20932
|
+
if (installed?.ownership.kind === "owned" && installed.ownership.scopeId !== projection.scopeId)
|
|
20933
|
+
throw new Error("dcode scope ownership conflict");
|
|
20934
|
+
if (installed?.ownership.kind === "unmanaged") {
|
|
20935
|
+
const path = installed.contentRoots[0]?.path;
|
|
20936
|
+
if (path === undefined || !sameTree7(projection.snapshot.packageRoot, path))
|
|
20937
|
+
throw new Error("dcode legacy content differs; refusing adoption");
|
|
20938
|
+
const manifest = declaredManifest(path, projection.packageName);
|
|
20939
|
+
if (manifest.version !== projection.packageVersion)
|
|
20940
|
+
throw new Error("dcode legacy version differs; refusing adoption");
|
|
20941
|
+
}
|
|
20942
|
+
if (existsSync23(target) && ownership3(target)?.pluginId !== projection.nativeId)
|
|
20943
|
+
throw new Error("dcode lifecycle target is not owned");
|
|
20944
|
+
const rollbackReference = captureDcodeRollback(projection, projection.attemptId, projection.operationId);
|
|
20945
|
+
writeFileSync9(join28(rollbackReference, "next-path.json"), JSON.stringify({ target }));
|
|
20946
|
+
return { prior, expected: { ...prior, route: "managed", presence: "present", enablement: "enabled", activation: "active", transition: { requirement: "restart", status: "effective" }, installedFingerprint: fingerprint, contentRoots: [{ label: "managed", path: target, fingerprint }] }, rollbackReference, rollbackCoverageOperationIds: projection.affectedOperationIds };
|
|
20947
|
+
},
|
|
20948
|
+
captureDisablePreparation: async (request) => {
|
|
20949
|
+
const binding = { adapterId: "dcode", ...request.activation };
|
|
20950
|
+
return captureOwnedPreparation(binding, request.attemptId, request.operationId, request.selection.targetObservationId, () => ({ prior: dcodeLifecycleReadback(binding), rollbackReference: captureDcodeRollback(binding, request.attemptId, request.operationId), rollbackCoverageOperationIds: request.selection.affectedOperationIds, transition: { requirement: "restart", status: "effective" } }));
|
|
20951
|
+
},
|
|
20952
|
+
captureRetirementPreparation: async (request) => {
|
|
20953
|
+
const binding = { adapterId: "dcode", ...request.activation };
|
|
20954
|
+
return captureOwnedPreparation(binding, request.attemptId, request.operationId, request.selection.targetObservationId, () => ({ prior: dcodeLifecycleReadback(binding), rollbackReference: captureDcodeRollback(binding, request.attemptId, request.operationId), rollbackCoverageOperationIds: request.selection.affectedOperationIds, transition: { requirement: "none", status: "effective" } }));
|
|
20955
|
+
},
|
|
20956
|
+
apply: async (prepared) => {
|
|
20957
|
+
await acquireNativeLock(prepared.handle.attemptId, prepared.handle.operationId);
|
|
20958
|
+
const target = lifecycleSlot(prepared.handle.nativeId, prepared.handle.projectedFingerprint);
|
|
20959
|
+
assertCachePath(target);
|
|
20960
|
+
mkdirSync9(dirname11(target), { recursive: true });
|
|
20961
|
+
if (existsSync23(target)) {
|
|
20962
|
+
const marker = ownership3(target);
|
|
20963
|
+
if (marker?.pluginId !== prepared.handle.nativeId)
|
|
20964
|
+
throw new Error("dcode lifecycle activation would overwrite foreign content");
|
|
20965
|
+
if (fingerprintTree(target) !== prepared.handle.projectedFingerprint)
|
|
20966
|
+
throw new Error("dcode immutable projection slot has diverged");
|
|
20967
|
+
} else {
|
|
20968
|
+
renameSync8(prepared.stagingRoot, target);
|
|
20969
|
+
}
|
|
20970
|
+
setDcodeRow(prepared.handle.nativeId, [{ installPath: target }], true);
|
|
20971
|
+
return { receiptId: `dcode-apply:${prepared.handle.operationId}`, changed: true };
|
|
20972
|
+
},
|
|
20973
|
+
disable: async (prepared) => {
|
|
20974
|
+
await acquireNativeLock(prepared.handle.attemptId, prepared.handle.operationId);
|
|
20975
|
+
const row = object3(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")[prepared.handle.nativeId];
|
|
20976
|
+
setDcodeRow(prepared.handle.nativeId, row ?? null, false);
|
|
20977
|
+
return { receiptId: `dcode-disable:${prepared.handle.operationId}`, changed: true };
|
|
20978
|
+
},
|
|
20979
|
+
retire: async (prepared) => {
|
|
20980
|
+
await acquireNativeLock(prepared.handle.attemptId, prepared.handle.operationId);
|
|
20981
|
+
const rows = rowsFor(object3(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")[prepared.handle.nativeId]);
|
|
20982
|
+
for (const row of rows) {
|
|
20983
|
+
const path = rowInstall(row, prepared.handle.nativeId);
|
|
20984
|
+
assertCachePath(path);
|
|
20985
|
+
if (ownership3(path)?.pluginId !== prepared.handle.nativeId)
|
|
20986
|
+
throw new Error("dcode retirement has no ownership proof");
|
|
20987
|
+
}
|
|
20988
|
+
setDcodeRow(prepared.handle.nativeId, null, null);
|
|
20989
|
+
for (const row of rows)
|
|
20990
|
+
rmSync9(rowInstall(row, prepared.handle.nativeId), { recursive: true, force: true });
|
|
20991
|
+
return { receiptId: `dcode-retire:${prepared.handle.operationId}`, changed: true };
|
|
20992
|
+
},
|
|
20993
|
+
readback: async (handle) => {
|
|
20994
|
+
const held = nativeLocks.get(`${handle.attemptId}:${handle.operationId}`);
|
|
20995
|
+
if (held !== undefined && !held.isAlive())
|
|
20996
|
+
throw new Error("dcode native lock was lost before readback");
|
|
20997
|
+
return dcodeLifecycleReadback(handle);
|
|
20998
|
+
},
|
|
20999
|
+
rollback: async (handle) => {
|
|
21000
|
+
await acquireNativeLock(handle.attemptId, handle.operationId);
|
|
21001
|
+
const target = handle.projectedFingerprint === null ? undefined : lifecycleSlot(handle.nativeId, handle.projectedFingerprint);
|
|
21002
|
+
if (target !== undefined)
|
|
21003
|
+
assertCachePath(target);
|
|
21004
|
+
if (target !== undefined && existsSync23(target) && !handle.prior.contentRoots.some((root) => resolve17(root.path) === resolve17(target))) {
|
|
21005
|
+
if (ownership3(target)?.pluginId !== handle.nativeId)
|
|
21006
|
+
throw new Error("dcode rollback target is not owned");
|
|
21007
|
+
rmSync9(target, { recursive: true });
|
|
21008
|
+
}
|
|
21009
|
+
const state = readDoc2(join28(handle.rollbackReference, "state.json"), {}, "rollback state");
|
|
21010
|
+
for (const [index, row] of rowsFor(state.row).entries()) {
|
|
21011
|
+
const path = rowInstall(row, handle.nativeId);
|
|
21012
|
+
assertCachePath(path);
|
|
21013
|
+
const backup = join28(handle.rollbackReference, `content-${index}`);
|
|
21014
|
+
if (existsSync23(backup)) {
|
|
21015
|
+
mkdirSync9(dirname11(path), { recursive: true });
|
|
21016
|
+
cpSync8(backup, path, { recursive: true });
|
|
21017
|
+
}
|
|
21018
|
+
}
|
|
21019
|
+
setDcodeRow(handle.nativeId, state.row ?? null, typeof state.enabled === "boolean" ? state.enabled : null);
|
|
21020
|
+
if (state.registryExisted === false && Object.keys(object3(readRegistry3(dcodeRegistryFile()).plugins, "registry plugins")).length === 0)
|
|
21021
|
+
rmSync9(dcodeRegistryFile());
|
|
21022
|
+
if (state.enablementExisted === false && Object.keys(boolObject(readEnablement(dcodeEnablementFile()).enabledPlugins, "enabledPlugins")).length === 0)
|
|
21023
|
+
rmSync9(dcodeEnablementFile());
|
|
21024
|
+
return { receiptId: `dcode-rollback:${handle.operationId}`, changed: true };
|
|
21025
|
+
},
|
|
21026
|
+
releaseRecovery: async (handle) => {
|
|
21027
|
+
await releaseNativeLock(handle.attemptId, handle.operationId);
|
|
21028
|
+
},
|
|
21029
|
+
cleanup: async (reference, disposition) => {
|
|
21030
|
+
try {
|
|
21031
|
+
const dir = lifecycleDirectory(reference.attemptId, reference.operationId);
|
|
21032
|
+
const next = readDoc2(join28(dir, "rollback", "next-path.json"), {}, "cleanup target");
|
|
21033
|
+
if (disposition === "verified-commit" && typeof next.target === "string") {
|
|
21034
|
+
const prior = readDoc2(join28(dir, "rollback", "state.json"), {}, "rollback state");
|
|
21035
|
+
for (const row of rowsFor(prior.row)) {
|
|
21036
|
+
const path = rowInstall(row, reference.operationId);
|
|
21037
|
+
assertCachePath(path);
|
|
21038
|
+
if (resolve17(path) !== resolve17(next.target))
|
|
21039
|
+
rmSync9(path, { recursive: true, force: true });
|
|
21040
|
+
}
|
|
21041
|
+
}
|
|
21042
|
+
rmSync9(dir, { recursive: true, force: true });
|
|
21043
|
+
} finally {
|
|
21044
|
+
await releaseNativeLock(reference.attemptId, reference.operationId);
|
|
21045
|
+
}
|
|
21046
|
+
return { cleanupId: `dcode-cleanup:${reference.operationId}`, completed: true };
|
|
21047
|
+
}
|
|
21048
|
+
};
|
|
21049
|
+
var dcodeLifecycle = createLifecycleHostAdapter(dcodeDefinition);
|
|
20601
21050
|
var dcodeWriter = {
|
|
21051
|
+
lifecyclePinAdapter: dcodeLifecycle,
|
|
20602
21052
|
...dcode,
|
|
20603
21053
|
supportsAdoption: true,
|
|
20604
21054
|
plannedNativeId: (plugin) => `${plugin.name}@${plugin.marketplace || "local"}`,
|
|
@@ -20661,7 +21111,7 @@ var dcodeWriter = {
|
|
|
20661
21111
|
throw new Error(`dcode cache target is unowned; refusing to replace it: ${target}`);
|
|
20662
21112
|
const former = prior.kind === "none" ? undefined : prior.path;
|
|
20663
21113
|
const unchanged = (former === undefined || resolve17(former) === resolve17(target)) && slot?.kind === "current" && slot.fingerprint === sourceFingerprint && slot.projectedFingerprint === projected && slot.sourceRevision === resolved.sha && sameTree7(stage, target);
|
|
20664
|
-
const nextRegistry = { ...registry, plugins: { ...plugins, [id]: [{ installPath: target
|
|
21114
|
+
const nextRegistry = { ...registry, plugins: { ...plugins, [id]: [{ installPath: target }] } };
|
|
20665
21115
|
const nextEnablement = { ...enablement, enabledPlugins: { ...enabled, [id]: true } };
|
|
20666
21116
|
if (opts?.dryRun) {
|
|
20667
21117
|
console.log(`[dcode] would activate directory: ${target}`);
|
|
@@ -20712,6 +21162,8 @@ var dcodeWriter = {
|
|
|
20712
21162
|
assertCachePath(plugin.path);
|
|
20713
21163
|
if (ownership3(plugin.path)?.pluginId !== plugin.id)
|
|
20714
21164
|
return { changes: [], refusals: [] };
|
|
21165
|
+
if (opts?.dryRun !== true)
|
|
21166
|
+
throw new Error("dcode pin requires the durable lifecycle pin workflow; direct in-place pinning is refused");
|
|
20715
21167
|
return pinPluginMcpFiles(plugin.path, dcodeMcpCandidates(), opts);
|
|
20716
21168
|
},
|
|
20717
21169
|
async remove(id) {
|
|
@@ -20861,7 +21313,9 @@ function assertReadback(id, registryFile, enablementFile, target, version, proje
|
|
|
20861
21313
|
const install = rowInstall(rows[0], id);
|
|
20862
21314
|
if (resolve17(install) !== resolve17(target))
|
|
20863
21315
|
throw new Error(`dcode readback path mismatch for ${id}`);
|
|
20864
|
-
if (rows[0].version !== version)
|
|
21316
|
+
if (rows[0].version !== undefined && rows[0].version !== null)
|
|
21317
|
+
throw new Error(`dcode managed install is eligible for native auto-update: ${id}`);
|
|
21318
|
+
if (declaredManifest(target, id.split("@")[0]).version !== version)
|
|
20865
21319
|
throw new Error(`dcode readback version mismatch for ${id}`);
|
|
20866
21320
|
if (boolObject(object3(enablement.enabledPlugins, "enabledPlugins"), "enabledPlugins")[id] !== true)
|
|
20867
21321
|
throw new Error(`dcode readback enablement mismatch for ${id}`);
|
|
@@ -25031,7 +25485,22 @@ async function planLifecycle(input) {
|
|
|
25031
25485
|
const result = await prepareEntry(entry, input.hosts, loaded.state, command, input.transportSource, transportCache);
|
|
25032
25486
|
if (result.kind === "defect")
|
|
25033
25487
|
return zeroWrite(command, input.dryRun, result.reason, result.terminalPhase);
|
|
25034
|
-
|
|
25488
|
+
if (input.removalSelection !== undefined) {
|
|
25489
|
+
if (command !== "remove" || result.kind !== "retire-source") {
|
|
25490
|
+
return zeroWrite(command, input.dryRun, createLifecycleReason("internal", "internal.invariant", "package retirement selection is valid only for manual removal"));
|
|
25491
|
+
}
|
|
25492
|
+
const selected = input.removalSelection.filter((row) => row.scopeId === result.scope.id);
|
|
25493
|
+
if (selected.length === 0 || selected.some((row) => !loaded.state.activations.some((activation) => activation.scopeId === row.scopeId && activation.packageId === row.packageId && activation.nativeId === row.nativeId))) {
|
|
25494
|
+
return zeroWrite(command, input.dryRun, createLifecycleReason("internal", "internal.invariant", "manual removal selection no longer matches recorded ownership"));
|
|
25495
|
+
}
|
|
25496
|
+
const retirementPackages = selected.map(({ packageId, nativeId }) => ({ packageId, nativeId })).sort((left, right) => left.packageId.localeCompare(right.packageId) || left.nativeId.localeCompare(right.nativeId));
|
|
25497
|
+
prepared.push({
|
|
25498
|
+
...result,
|
|
25499
|
+
retirementPackages,
|
|
25500
|
+
identity: ["remove-packages", result.scope.id, ...retirementPackages.flatMap((row) => [row.packageId, row.nativeId])]
|
|
25501
|
+
});
|
|
25502
|
+
} else
|
|
25503
|
+
prepared.push(result);
|
|
25035
25504
|
}
|
|
25036
25505
|
const attemptId = lifecycleAttemptId(loaded.state.stateGeneration, prepared.map((item) => item.identity));
|
|
25037
25506
|
const scopes = [];
|
|
@@ -25043,7 +25512,7 @@ async function planLifecycle(input) {
|
|
|
25043
25512
|
scopes.push(planned.scope);
|
|
25044
25513
|
operations.push(...planned.operations);
|
|
25045
25514
|
}
|
|
25046
|
-
return frozenPlan(input, command, attemptId, scopes, operations, prepared);
|
|
25515
|
+
return frozenPlan(input, command, attemptId, scopes, operations, prepared, loaded.state);
|
|
25047
25516
|
}
|
|
25048
25517
|
async function prepareEntry(entry, hosts, state, command, transportSource, transportCache) {
|
|
25049
25518
|
switch (entry.operation) {
|
|
@@ -25077,6 +25546,10 @@ async function prepareSync(entry, hosts, state, command, transportSource, transp
|
|
|
25077
25546
|
throw error;
|
|
25078
25547
|
}
|
|
25079
25548
|
const scope = deploymentScopeForSyncManifestEntry(entry);
|
|
25549
|
+
const recordedScope = state.scopes.find((row) => row.id === scope.id);
|
|
25550
|
+
if (recordedScope?.target.context !== undefined && JSON.stringify(canonicalTargetContext(recordedScope.target.context)) !== JSON.stringify(entry.target.context === undefined ? undefined : canonicalTargetContext(entry.target.context))) {
|
|
25551
|
+
return defect(createLifecycleReason("internal", "internal.invariant", `target context changed for recorded scope '${scope.id}'; use a distinct target instance or retire the recorded scope explicitly`));
|
|
25552
|
+
}
|
|
25080
25553
|
const pending = pendingDefect(state, scope.id, new Set(selected.map((item) => item.plugin.name)), command);
|
|
25081
25554
|
if (pending !== null)
|
|
25082
25555
|
return defect(pending);
|
|
@@ -25202,7 +25675,7 @@ async function planSync(item, attemptId, validatedAt, state, command) {
|
|
|
25202
25675
|
requiredCapabilities: [],
|
|
25203
25676
|
adoptionRequested: selected.adoptionRequested
|
|
25204
25677
|
});
|
|
25205
|
-
classified.push(uninventoriedUpdate(item, selected.plugin.name, nativeId));
|
|
25678
|
+
classified.push(uninventoriedUpdate(item, selected.plugin.name, nativeId, attemptId));
|
|
25206
25679
|
continue;
|
|
25207
25680
|
}
|
|
25208
25681
|
const nativeId = item.host === null ? selected.plugin.name : item.host.plannedNativeId(selected.plugin);
|
|
@@ -25213,7 +25686,7 @@ async function planSync(item, attemptId, validatedAt, state, command) {
|
|
|
25213
25686
|
requiredCapabilities: [...requiredSemanticsForOperation(inventory, "install")].sort(),
|
|
25214
25687
|
adoptionRequested: selected.adoptionRequested
|
|
25215
25688
|
});
|
|
25216
|
-
const row = await classifyDesired(item, selected, inventory, nativeId, attemptId, state, command === "
|
|
25689
|
+
const row = await classifyDesired(item, selected, inventory, nativeId, attemptId, state, command === "update");
|
|
25217
25690
|
if (row.kind === "defect")
|
|
25218
25691
|
return row;
|
|
25219
25692
|
classified.push(row);
|
|
@@ -25226,6 +25699,7 @@ async function planSync(item, attemptId, validatedAt, state, command) {
|
|
|
25226
25699
|
kind: "planned",
|
|
25227
25700
|
scope: {
|
|
25228
25701
|
scope: item.scope,
|
|
25702
|
+
target: item.observation?.target ?? item.entry.target,
|
|
25229
25703
|
selectorMode: item.entry.selectors === undefined ? "all" : "explicit",
|
|
25230
25704
|
desired: desiredGeneration(existing, item.frozen.snapshot.revision, item.frozen.snapshot.fingerprint, packages, validatedAt),
|
|
25231
25705
|
prune: blocks || retirements.operations.some((row) => row.operation.action !== "retire-orphan") ? "blocked" : "planned"
|
|
@@ -25233,8 +25707,8 @@ async function planSync(item, attemptId, validatedAt, state, command) {
|
|
|
25233
25707
|
operations: [...classified.map((row) => row.operation), ...retirements.operations]
|
|
25234
25708
|
};
|
|
25235
25709
|
}
|
|
25236
|
-
async function classifyDesired(item, selected, inventory, nativeId, attemptId, state,
|
|
25237
|
-
const operationId = operationIdentity("desired-pair", item.scope.id, selected.plugin.name, nativeId, item.frozen.snapshot.fingerprint);
|
|
25710
|
+
async function classifyDesired(item, selected, inventory, nativeId, attemptId, state, reapplyMatchingUpdate) {
|
|
25711
|
+
const operationId = operationIdentity("desired-pair", item.scope.id, selected.plugin.name, nativeId, `${item.frozen.snapshot.fingerprint}\x00${attemptId}`);
|
|
25238
25712
|
const snapshotId = sourceSnapshotId(item.frozen);
|
|
25239
25713
|
const activation = findActivation(state, item.scope.id, selected.plugin.name, nativeId);
|
|
25240
25714
|
if (item.host === null || item.observation === null) {
|
|
@@ -25274,13 +25748,12 @@ async function classifyDesired(item, selected, inventory, nativeId, attemptId, s
|
|
|
25274
25748
|
return classified(operationId, "desired-pair", item.scope, snapshotId, selected.plugin.name, nativeId, "not-attempted", "none", decision.reason);
|
|
25275
25749
|
}
|
|
25276
25750
|
const recordedRoute = activation?.route.kind === "managed" || activation?.route.kind === "native" ? activation.route.kind : null;
|
|
25277
|
-
const matchingBytes = sameBytes(activation, installation, selected.plugin);
|
|
25751
|
+
const matchingBytes = sameBytes(activation, installation, selected.plugin) && installation?.activation === "active" && installation.enablement === "enabled";
|
|
25278
25752
|
const action = chosenAction(replacing, recordedRoute, decision.route, matchingBytes, reapplyMatchingUpdate);
|
|
25279
|
-
|
|
25280
|
-
if (action === "route-migrate" || action === "update" && !applyUpdates) {
|
|
25753
|
+
if (action === "route-migrate") {
|
|
25281
25754
|
return classified(operationId, "desired-pair", item.scope, snapshotId, selected.plugin.name, nativeId, "retain-prior", recordedRoute ?? decision.route, createLifecycleReason("capability", "capability.unsupported", `${action} of '${selected.plugin.name}' is not applied by this execution slice`, action));
|
|
25282
25755
|
}
|
|
25283
|
-
return classified(
|
|
25756
|
+
return classified(operationId, "desired-pair", item.scope, snapshotId, selected.plugin.name, nativeId, action, decision.route, null);
|
|
25284
25757
|
}
|
|
25285
25758
|
async function planOmissions(item, attemptId, state, desired) {
|
|
25286
25759
|
const packageIds = new Set(desired.map((row) => row.packageId));
|
|
@@ -25306,7 +25779,7 @@ async function planOmissions(item, attemptId, state, desired) {
|
|
|
25306
25779
|
return { kind: "omissions", operations };
|
|
25307
25780
|
}
|
|
25308
25781
|
async function planRetire(item, attemptId, state) {
|
|
25309
|
-
const activations = state.activations.filter((activation) => activation.scopeId === item.recorded.id).sort((left, right) => left.packageId < right.packageId ? -1 : left.packageId > right.packageId ? 1 : 0);
|
|
25782
|
+
const activations = state.activations.filter((activation) => activation.scopeId === item.recorded.id && (item.retirementPackages === undefined || item.retirementPackages.some((row) => row.packageId === activation.packageId && row.nativeId === activation.nativeId))).sort((left, right) => left.packageId < right.packageId ? -1 : left.packageId > right.packageId ? 1 : 0);
|
|
25310
25783
|
const operations = [];
|
|
25311
25784
|
for (const activation of activations) {
|
|
25312
25785
|
const installation = item.observation?.installations.find((row) => row.nativeId === activation.nativeId);
|
|
@@ -25326,8 +25799,10 @@ async function planRetire(item, attemptId, state) {
|
|
|
25326
25799
|
kind: "planned",
|
|
25327
25800
|
scope: {
|
|
25328
25801
|
scope: item.scope,
|
|
25802
|
+
target: item.observation?.target ?? item.recorded.target,
|
|
25329
25803
|
selectorMode: "retired",
|
|
25330
25804
|
desired: null,
|
|
25805
|
+
...item.retirementPackages === undefined ? {} : { retirementPackages: item.retirementPackages },
|
|
25331
25806
|
prune: operations.length > 0 && operations.every((row) => row.operation.action === "retire-orphan") ? "planned" : "blocked"
|
|
25332
25807
|
},
|
|
25333
25808
|
operations
|
|
@@ -25417,7 +25892,7 @@ async function selectDisable(item, nativeId, operationId, attemptId, version, ac
|
|
|
25417
25892
|
return decision;
|
|
25418
25893
|
}
|
|
25419
25894
|
async function selectRetirement(host, observation, scope, activation, attemptId, source) {
|
|
25420
|
-
const operationId = operationIdentity("retirement", scope.id, activation.packageId, activation.nativeId, activation.sourceRevision ?? "unrecorded");
|
|
25895
|
+
const operationId = operationIdentity("retirement", scope.id, activation.packageId, activation.nativeId, `${activation.sourceRevision ?? "unrecorded"}\x00${attemptId}`);
|
|
25421
25896
|
const installation = observation.installations.find((row) => row.nativeId === activation.nativeId);
|
|
25422
25897
|
if (installation === undefined) {
|
|
25423
25898
|
return { kind: "planned", operation: notAttemptedRetirement(operationId, scope, activation, "ownership was not revalidated") };
|
|
@@ -25500,8 +25975,9 @@ async function decideRecordedRoute(host, observation, recorded, operationId, att
|
|
|
25500
25975
|
function recordedActivation(scope, source, activation, installation, target) {
|
|
25501
25976
|
return ownedActivation(scope, source, activation, installation, target);
|
|
25502
25977
|
}
|
|
25503
|
-
function frozenPlan(input, command, attemptId, scopes, operations, prepared) {
|
|
25978
|
+
function frozenPlan(input, command, attemptId, scopes, operations, prepared, state) {
|
|
25504
25979
|
const snapshots = [];
|
|
25980
|
+
const frozenSources = new Map;
|
|
25505
25981
|
const seen = new Set;
|
|
25506
25982
|
for (const item of prepared) {
|
|
25507
25983
|
if (item.kind !== "sync")
|
|
@@ -25511,8 +25987,12 @@ function frozenPlan(input, command, attemptId, scopes, operations, prepared) {
|
|
|
25511
25987
|
continue;
|
|
25512
25988
|
seen.add(id);
|
|
25513
25989
|
snapshots.push({ id, reference: item.frozen.snapshot });
|
|
25990
|
+
frozenSources.set(id, item.frozen);
|
|
25514
25991
|
}
|
|
25515
|
-
const outcomes = operations.map((row) =>
|
|
25992
|
+
const outcomes = operations.map((row) => {
|
|
25993
|
+
const outcome = outcomeFor(row);
|
|
25994
|
+
return input.dryRun ? { ...outcome, ...observedStates(row.operation, prepared, state) } : outcome;
|
|
25995
|
+
});
|
|
25516
25996
|
const failed = outcomes.filter((outcome) => outcome.result !== "succeeded");
|
|
25517
25997
|
const converged = failed.length === 0;
|
|
25518
25998
|
const failureCategory = converged ? null : failed[0]?.reason?.category ?? "internal";
|
|
@@ -25536,12 +26016,31 @@ function frozenPlan(input, command, attemptId, scopes, operations, prepared) {
|
|
|
25536
26016
|
kind: "frozen",
|
|
25537
26017
|
requestedDryRun: input.dryRun,
|
|
25538
26018
|
attemptId,
|
|
26019
|
+
frozenSources,
|
|
25539
26020
|
report,
|
|
25540
26021
|
scopes,
|
|
25541
26022
|
operations,
|
|
25542
26023
|
...input.transportSource === undefined ? {} : { transportSource: input.transportSource }
|
|
25543
26024
|
};
|
|
25544
26025
|
}
|
|
26026
|
+
function observedStates(operation, prepared, state) {
|
|
26027
|
+
const item = prepared.find((candidate) => candidate.scope.id === operation.scope.id);
|
|
26028
|
+
if (item?.observation === null || item === undefined)
|
|
26029
|
+
return { resourceState: "unknown", activationState: "unknown" };
|
|
26030
|
+
const installation = item.observation.installations.find((row) => row.nativeId === operation.nativeId);
|
|
26031
|
+
if (installation === undefined || installation.presence === "absent")
|
|
26032
|
+
return { resourceState: "absent", activationState: "inactive" };
|
|
26033
|
+
if (installation.enablement === "disabled" || installation.activation === "inactive")
|
|
26034
|
+
return { resourceState: "present", activationState: "inactive" };
|
|
26035
|
+
if (installation.enablement === "unknown" || installation.activation === "unknown")
|
|
26036
|
+
return { resourceState: "present", activationState: "unknown" };
|
|
26037
|
+
if (installation.activation === "nonconforming")
|
|
26038
|
+
return { resourceState: "present", activationState: "active-nonconforming" };
|
|
26039
|
+
const activation = findActivation(state, operation.scope.id, operation.package, operation.nativeId ?? "");
|
|
26040
|
+
const plugin = item.kind === "sync" ? item.selected.find((selected) => selected.plugin.name === operation.package)?.plugin : undefined;
|
|
26041
|
+
const conforming = operation.coverage === "retirement" ? activation?.fingerprints.installed === installation.installedFingerprint : plugin !== undefined && sameBytes(activation, installation, plugin);
|
|
26042
|
+
return { resourceState: "present", activationState: conforming ? "active-conforming" : "active-nonconforming" };
|
|
26043
|
+
}
|
|
25545
26044
|
function outcomeFor(row) {
|
|
25546
26045
|
const operation = row.operation;
|
|
25547
26046
|
switch (operation.action) {
|
|
@@ -25580,8 +26079,8 @@ function plannedNativeIdFor(item, plugin) {
|
|
|
25580
26079
|
return error instanceof Error ? error : new Error(unknownErrorDiagnostic(error));
|
|
25581
26080
|
}
|
|
25582
26081
|
}
|
|
25583
|
-
function uninventoriedUpdate(item, packageName, nativeId) {
|
|
25584
|
-
return classified(operationIdentity("desired-pair", item.scope.id, packageName, nativeId, item.frozen.snapshot.fingerprint), "desired-pair", item.scope, sourceSnapshotId(item.frozen), packageName, nativeId, "update", "managed", null);
|
|
26082
|
+
function uninventoriedUpdate(item, packageName, nativeId, attemptId) {
|
|
26083
|
+
return classified(operationIdentity("desired-pair", item.scope.id, packageName, nativeId, `${item.frozen.snapshot.fingerprint}\x00${attemptId}`), "desired-pair", item.scope, sourceSnapshotId(item.frozen), packageName, nativeId, "update", "managed", null);
|
|
25585
26084
|
}
|
|
25586
26085
|
function classified(operationId, coverage, scope, sourceSnapshotId, packageName, nativeId, action, route, reason) {
|
|
25587
26086
|
return {
|
|
@@ -25748,9 +26247,7 @@ function commandName(manifest) {
|
|
|
25748
26247
|
return manifest.entries.every((entry) => entry.operation === "retire-source") ? "retire-source" : "sync";
|
|
25749
26248
|
}
|
|
25750
26249
|
function resolvePlannedSource(binding, transportSource, cache) {
|
|
25751
|
-
const argument = transportArgument(binding, transportSource);
|
|
25752
|
-
if (argument === undefined)
|
|
25753
|
-
return resolveSource(sourceArgument(binding));
|
|
26250
|
+
const argument = transportArgument(binding, transportSource) ?? sourceArgument(binding);
|
|
25754
26251
|
const cached = cache.get(argument);
|
|
25755
26252
|
if (cached !== undefined)
|
|
25756
26253
|
return cached;
|
|
@@ -25806,7 +26303,7 @@ function lifecycleAttemptId(stateGeneration, identities) {
|
|
|
25806
26303
|
return `attempt-v1-${digest3([String(stateGeneration), ...identities.flat()])}`;
|
|
25807
26304
|
}
|
|
25808
26305
|
function lifecycleAttemptIdForPlannedScopes(stateGeneration, scopes) {
|
|
25809
|
-
return lifecycleAttemptId(stateGeneration, scopes.map((planned) => plannedAttemptIdentity(planned.scope.id, planned.desired?.sourceFingerprint, planned.desired?.packages.map((pkg) => pkg.packageId), planned.selectorMode === "retired" || planned.desired === null)));
|
|
26306
|
+
return lifecycleAttemptId(stateGeneration, scopes.map((planned) => planned.retirementPackages !== undefined ? ["remove-packages", planned.scope.id, ...planned.retirementPackages.flatMap((row) => [row.packageId, row.nativeId])] : plannedAttemptIdentity(planned.scope.id, planned.desired?.sourceFingerprint, planned.desired?.packages.map((pkg) => pkg.packageId), planned.selectorMode === "retired" || planned.desired === null)));
|
|
25810
26307
|
}
|
|
25811
26308
|
function plannedAttemptIdentity(scopeId, sourceFingerprint, packageIds, retired = false) {
|
|
25812
26309
|
if (retired || sourceFingerprint === undefined || packageIds === undefined)
|
|
@@ -26022,6 +26519,15 @@ async function executeFrozen(plan, hosts, now) {
|
|
|
26022
26519
|
const refusal = ledger.generationRefusal(plan);
|
|
26023
26520
|
if (refusal !== null)
|
|
26024
26521
|
return refusalReport(plan, refusal);
|
|
26522
|
+
try {
|
|
26523
|
+
for (const frozen of plan.frozenSources.values()) {
|
|
26524
|
+
if (fingerprintTree(frozen.snapshotDir) !== frozen.snapshot.fingerprint) {
|
|
26525
|
+
return refusalReport(plan, createLifecycleReason("internal", "internal.invariant", "retained frozen source bytes drifted before apply"));
|
|
26526
|
+
}
|
|
26527
|
+
}
|
|
26528
|
+
} catch (error) {
|
|
26529
|
+
return refusalReport(plan, thrownReason(error));
|
|
26530
|
+
}
|
|
26025
26531
|
const outcomes = [];
|
|
26026
26532
|
let stopped = null;
|
|
26027
26533
|
for (const row of plan.operations) {
|
|
@@ -26060,8 +26566,9 @@ async function runOperation(plan, operation, hosts, ledger) {
|
|
|
26060
26566
|
return succeeded(operation, false);
|
|
26061
26567
|
case "retire-orphan":
|
|
26062
26568
|
return runRetire(plan, operation, hosts, ledger);
|
|
26063
|
-
case "route-migrate":
|
|
26064
26569
|
case "disable-nonconforming":
|
|
26570
|
+
return runDisable(plan, operation, hosts, ledger);
|
|
26571
|
+
case "route-migrate":
|
|
26065
26572
|
case "retain-prior":
|
|
26066
26573
|
case "not-attempted":
|
|
26067
26574
|
return stop(operation, createLifecycleReason("internal", "internal.invariant", `operation '${operation.operationId}' is not applied by this execution slice`));
|
|
@@ -26124,6 +26631,12 @@ async function runActivation(plan, operation, hosts, ledger, action) {
|
|
|
26124
26631
|
verified = host.adapter.verify(receipt.handle, observation);
|
|
26125
26632
|
} catch (error) {
|
|
26126
26633
|
const reason = thrownReason(error);
|
|
26634
|
+
if (receipt === undefined && ledger.journalState(plan.attemptId, operation.operationId) === "applying" && error instanceof LifecycleHostPhaseError && !error.mutationStarted) {
|
|
26635
|
+
ledger.markNotApplied(plan, operation);
|
|
26636
|
+
if (preparation !== undefined)
|
|
26637
|
+
await abortPreparation(host, preparation);
|
|
26638
|
+
return stop(operation, reason);
|
|
26639
|
+
}
|
|
26127
26640
|
if (handle !== undefined && ledger.journalState(plan.attemptId, operation.operationId) === "applying") {
|
|
26128
26641
|
return recoverApplied(plan, operation, host, ledger, handle, reason);
|
|
26129
26642
|
}
|
|
@@ -26186,8 +26699,10 @@ async function prepareActivation(plan, operation, host, nativeId, action, record
|
|
|
26186
26699
|
if (context === undefined) {
|
|
26187
26700
|
return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' has no frozen source snapshot`));
|
|
26188
26701
|
}
|
|
26189
|
-
const frozen =
|
|
26190
|
-
if (frozen
|
|
26702
|
+
const frozen = operation.sourceSnapshotId === null ? undefined : plan.frozenSources.get(operation.sourceSnapshotId);
|
|
26703
|
+
if (frozen === undefined)
|
|
26704
|
+
return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' lost its retained frozen source`));
|
|
26705
|
+
if (frozen.snapshot.fingerprint !== context.reference.fingerprint || frozen.snapshot.revision !== context.reference.revision || fingerprintTree(frozen.snapshotDir) !== context.reference.fingerprint) {
|
|
26191
26706
|
return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' source bytes drifted from the frozen snapshot`));
|
|
26192
26707
|
}
|
|
26193
26708
|
const plugin = frozen.plugins.find((candidate) => candidate.name === operation.package);
|
|
@@ -26195,12 +26710,15 @@ async function prepareActivation(plan, operation, host, nativeId, action, record
|
|
|
26195
26710
|
if (plugin === undefined || packageFingerprint === undefined) {
|
|
26196
26711
|
return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' is missing from the frozen source`));
|
|
26197
26712
|
}
|
|
26198
|
-
const target = lifecycleTarget2(operation);
|
|
26713
|
+
const target = lifecycleTarget2(plan, operation);
|
|
26199
26714
|
const observation = await host.adapter.observeTarget(target);
|
|
26200
26715
|
const version = await host.adapter.probeVersion(observation.target);
|
|
26201
26716
|
if (version.kind !== "detected") {
|
|
26202
26717
|
return refused(createLifecycleReason("runtime", "runtime.operation-failed", `${action} '${operation.package}' lost its detected target version`));
|
|
26203
26718
|
}
|
|
26719
|
+
if (fingerprintTree(frozen.snapshotDir) !== context.reference.fingerprint) {
|
|
26720
|
+
return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' retained snapshot changed during target observation`));
|
|
26721
|
+
}
|
|
26204
26722
|
const inventory = inventoryPackageSemantics(plugin);
|
|
26205
26723
|
const resolvedPins = resolveRecordedPinExecutables(plugin.dir, recordedPins);
|
|
26206
26724
|
if (resolvedPins.kind === "refused")
|
|
@@ -26276,6 +26794,50 @@ function sealActivationSnapshot(operation, attemptId, nativeId, frozen, plugin,
|
|
|
26276
26794
|
throw new Error(`frozen snapshot action '${snapshot.action}' is not ${action}`);
|
|
26277
26795
|
return snapshot;
|
|
26278
26796
|
}
|
|
26797
|
+
async function runDisable(plan, operation, hosts, ledger) {
|
|
26798
|
+
const host = hosts.find((candidate) => candidate.kinds.includes(operation.scope.target.kind));
|
|
26799
|
+
const reason = plan.report.outcomes.find((row) => row.operationId === operation.operationId)?.reason;
|
|
26800
|
+
if (host === undefined || operation.nativeId === null || reason === undefined || reason === null) {
|
|
26801
|
+
return stop(operation, createLifecycleReason("internal", "internal.invariant", "disable containment lost its adapter, identity or desired failure reason"));
|
|
26802
|
+
}
|
|
26803
|
+
let preparation;
|
|
26804
|
+
let receipt;
|
|
26805
|
+
try {
|
|
26806
|
+
const prepared = await prepareOwnedMutation(plan, operation, host, operation.nativeId, ledger, "disable");
|
|
26807
|
+
if (prepared.kind === "refused")
|
|
26808
|
+
return stop(operation, prepared.reason);
|
|
26809
|
+
if (prepared.selection.operation !== "disable")
|
|
26810
|
+
throw new Error("disable containment selected the wrong operation");
|
|
26811
|
+
ledger.acceptJournal(plan, operation, "disable-nonconforming");
|
|
26812
|
+
preparation = await host.adapter.prepareDisable({ operationId: operation.operationId, attemptId: plan.attemptId, selection: { ...prepared.selection, operation: "disable" }, activation: prepared.activation });
|
|
26813
|
+
ledger.markApplying(plan, operation);
|
|
26814
|
+
receipt = await host.adapter.disable(preparation);
|
|
26815
|
+
const observation = await host.adapter.readback(receipt.handle);
|
|
26816
|
+
const verified = host.adapter.verify(receipt.handle, observation);
|
|
26817
|
+
ledger.confirmDisable(plan, operation);
|
|
26818
|
+
try {
|
|
26819
|
+
await host.adapter.cleanup(verified.handle, "verified-commit");
|
|
26820
|
+
} catch {
|
|
26821
|
+
ledger.markCleanupPending(plan, operation, "disable-nonconforming");
|
|
26822
|
+
return pendingCleanup(operation);
|
|
26823
|
+
}
|
|
26824
|
+
ledger.completeContainment(plan, operation);
|
|
26825
|
+
return { stop: true, reason, outcome: { ...operation, result: "failed", resourceState: "retained", activationState: "inactive", changed: receipt.changed, reason } };
|
|
26826
|
+
} catch (error) {
|
|
26827
|
+
const failure = thrownReason(error);
|
|
26828
|
+
if (receipt === undefined && error instanceof LifecycleHostPhaseError && !error.mutationStarted) {
|
|
26829
|
+
if (ledger.journalState(plan.attemptId, operation.operationId) !== undefined)
|
|
26830
|
+
ledger.markNotApplied(plan, operation);
|
|
26831
|
+
if (preparation !== undefined)
|
|
26832
|
+
await abortPreparation(host, preparation);
|
|
26833
|
+
return stop(operation, failure);
|
|
26834
|
+
}
|
|
26835
|
+
if (preparation !== undefined && (receipt !== undefined || ledger.journalState(plan.attemptId, operation.operationId) === "applying")) {
|
|
26836
|
+
return recoverApplied(plan, operation, host, ledger, preparation.handle, failure);
|
|
26837
|
+
}
|
|
26838
|
+
return stop(operation, failure);
|
|
26839
|
+
}
|
|
26840
|
+
}
|
|
26279
26841
|
async function runRetire(plan, operation, hosts, ledger) {
|
|
26280
26842
|
if (ledger.journalState(plan.attemptId, operation.operationId) === "completed")
|
|
26281
26843
|
return retired(operation, false);
|
|
@@ -26293,17 +26855,20 @@ async function runRetire(plan, operation, hosts, ledger) {
|
|
|
26293
26855
|
}
|
|
26294
26856
|
let prepared;
|
|
26295
26857
|
try {
|
|
26296
|
-
prepared = await
|
|
26858
|
+
prepared = await prepareOwnedMutation(plan, operation, host, nativeId, ledger);
|
|
26297
26859
|
} catch (error) {
|
|
26298
26860
|
return stop(operation, thrownReason(error));
|
|
26299
26861
|
}
|
|
26300
26862
|
if (prepared.kind === "refused")
|
|
26301
26863
|
return stop(operation, prepared.reason);
|
|
26864
|
+
if (prepared.selection.operation !== "retire")
|
|
26865
|
+
return stop(operation, createLifecycleReason("internal", "internal.invariant", "retirement selected the wrong operation"));
|
|
26302
26866
|
try {
|
|
26303
26867
|
ledger.acceptJournal(plan, operation, "retire-orphan");
|
|
26304
26868
|
} catch (error) {
|
|
26305
26869
|
return stop(operation, thrownReason(error));
|
|
26306
26870
|
}
|
|
26871
|
+
let preparation;
|
|
26307
26872
|
let receipt;
|
|
26308
26873
|
let verified;
|
|
26309
26874
|
try {
|
|
@@ -26311,17 +26876,27 @@ async function runRetire(plan, operation, hosts, ledger) {
|
|
|
26311
26876
|
operationId: operation.operationId,
|
|
26312
26877
|
attemptId: plan.attemptId,
|
|
26313
26878
|
action: "retire-orphan",
|
|
26314
|
-
selection: prepared.selection,
|
|
26879
|
+
selection: { ...prepared.selection, operation: "retire" },
|
|
26315
26880
|
activation: prepared.activation
|
|
26316
26881
|
});
|
|
26882
|
+
preparation = sealed;
|
|
26317
26883
|
ledger.markPruning(plan, operation);
|
|
26318
26884
|
receipt = await host.adapter.retire(sealed);
|
|
26319
26885
|
const observation = await host.adapter.readback(receipt.handle);
|
|
26320
26886
|
verified = host.adapter.verify(receipt.handle, observation);
|
|
26321
26887
|
} catch (error) {
|
|
26322
26888
|
const reason = thrownReason(error);
|
|
26889
|
+
if (receipt === undefined && ledger.journalState(plan.attemptId, operation.operationId) === "applying" && error instanceof LifecycleHostPhaseError && !error.mutationStarted) {
|
|
26890
|
+
ledger.markNotApplied(plan, operation);
|
|
26891
|
+
if (preparation !== undefined)
|
|
26892
|
+
await abortPreparation(host, preparation);
|
|
26893
|
+
return stop(operation, reason);
|
|
26894
|
+
}
|
|
26323
26895
|
if (receipt !== undefined)
|
|
26324
26896
|
return recoverApplied(plan, operation, host, ledger, receipt.handle, reason);
|
|
26897
|
+
if (preparation !== undefined && ledger.journalState(plan.attemptId, operation.operationId) === "applying") {
|
|
26898
|
+
return recoverApplied(plan, operation, host, ledger, preparation.handle, reason);
|
|
26899
|
+
}
|
|
26325
26900
|
return stop(operation, reason);
|
|
26326
26901
|
}
|
|
26327
26902
|
if (receipt === undefined || verified === undefined) {
|
|
@@ -26341,12 +26916,13 @@ async function runRetire(plan, operation, hosts, ledger) {
|
|
|
26341
26916
|
ledger.markCompleted(plan, operation);
|
|
26342
26917
|
return retired(operation, receipt.changed);
|
|
26343
26918
|
}
|
|
26344
|
-
async function
|
|
26919
|
+
async function prepareOwnedMutation(plan, operation, host, nativeId, ledger, capabilityOperation = "retire") {
|
|
26920
|
+
const operationLabel = capabilityOperation === "disable" ? "disablement" : "retirement";
|
|
26345
26921
|
const recorded = ledger.activation(operation.scope.id, operation.package, nativeId);
|
|
26346
26922
|
if (recorded === undefined || recorded.route.kind === "legacy-unverified" || recorded.ownership.kind === "legacy-claim" || recorded.sourceRevision === undefined) {
|
|
26347
26923
|
return refused(createLifecycleReason("internal", "internal.invariant", `ownership of '${operation.package}' was not revalidated`));
|
|
26348
26924
|
}
|
|
26349
|
-
const target = lifecycleTarget2(operation);
|
|
26925
|
+
const target = lifecycleTarget2(plan, operation);
|
|
26350
26926
|
const observation = await host.adapter.observeTarget(target);
|
|
26351
26927
|
const installation = observation.installations.find((row) => row.nativeId === nativeId);
|
|
26352
26928
|
if (installation === undefined || installation.presence !== "present" || installation.ownership.kind !== "owned" || installation.ownership.scopeId !== operation.scope.id) {
|
|
@@ -26354,10 +26930,10 @@ async function prepareRetirement(plan, operation, host, nativeId, ledger) {
|
|
|
26354
26930
|
}
|
|
26355
26931
|
const version = await host.adapter.probeVersion(observation.target);
|
|
26356
26932
|
if (version.kind !== "detected") {
|
|
26357
|
-
return refused(createLifecycleReason("runtime", "runtime.operation-failed",
|
|
26933
|
+
return refused(createLifecycleReason("runtime", "runtime.operation-failed", `${operationLabel} '${operation.package}' lost its detected target version`));
|
|
26358
26934
|
}
|
|
26359
26935
|
if (recorded.fingerprints.installed !== installation.installedFingerprint) {
|
|
26360
|
-
return refused(createLifecycleReason("internal", "internal.invariant",
|
|
26936
|
+
return refused(createLifecycleReason("internal", "internal.invariant", `${operationLabel} '${operation.package}' fingerprint does not match the owned install`));
|
|
26361
26937
|
}
|
|
26362
26938
|
const owned = ownedActivation(operation.scope, operation.scope.source, recorded, installation, observation.target);
|
|
26363
26939
|
if (owned === null) {
|
|
@@ -26366,27 +26942,27 @@ async function prepareRetirement(plan, operation, host, nativeId, ledger) {
|
|
|
26366
26942
|
const planCoverage = createLifecyclePlanCoverage(observation, [{
|
|
26367
26943
|
nativeId,
|
|
26368
26944
|
operationId: operation.operationId,
|
|
26369
|
-
operation:
|
|
26945
|
+
operation: capabilityOperation,
|
|
26370
26946
|
mutationGroupId: operation.operationId,
|
|
26371
26947
|
authorization: "observed-owned"
|
|
26372
26948
|
}]);
|
|
26373
26949
|
const nativeScope = await host.adapter.observeNativeMutationScope({
|
|
26374
26950
|
targetObservation: observation,
|
|
26375
|
-
operation:
|
|
26951
|
+
operation: capabilityOperation,
|
|
26376
26952
|
packageName: owned.packageName,
|
|
26377
26953
|
nativeId,
|
|
26378
26954
|
sourceType: owned.sourceType
|
|
26379
26955
|
});
|
|
26380
26956
|
const nativeProjection = await host.adapter.observeNativeProjection({
|
|
26381
26957
|
targetObservation: observation,
|
|
26382
|
-
operation:
|
|
26958
|
+
operation: capabilityOperation,
|
|
26383
26959
|
operationId: operation.operationId,
|
|
26384
26960
|
attemptId: plan.attemptId,
|
|
26385
26961
|
activation: owned
|
|
26386
26962
|
});
|
|
26387
26963
|
const decision = host.adapter.decideRoute({
|
|
26388
26964
|
target: observation.target,
|
|
26389
|
-
operation:
|
|
26965
|
+
operation: capabilityOperation,
|
|
26390
26966
|
operationId: operation.operationId,
|
|
26391
26967
|
attemptId: plan.attemptId,
|
|
26392
26968
|
scopeId: operation.scope.id,
|
|
@@ -26400,13 +26976,13 @@ async function prepareRetirement(plan, operation, host, nativeId, ledger) {
|
|
|
26400
26976
|
planCoverage,
|
|
26401
26977
|
activation: owned
|
|
26402
26978
|
});
|
|
26403
|
-
if (decision.kind !== "selected" || decision.operation !==
|
|
26404
|
-
return refused(createLifecycleReason("runtime", "runtime.operation-failed",
|
|
26979
|
+
if (decision.kind !== "selected" || decision.operation !== capabilityOperation || decision.route !== operation.route) {
|
|
26980
|
+
return refused(createLifecycleReason("runtime", "runtime.operation-failed", `${operationLabel} '${operation.package}' kept frozen route '${operation.route}'`));
|
|
26405
26981
|
}
|
|
26406
26982
|
return { kind: "ready", selection: decision, activation: owned, recorded };
|
|
26407
26983
|
}
|
|
26408
|
-
function lifecycleTarget2(operation) {
|
|
26409
|
-
return { kind: operation.scope.target.kind, instance: operation.scope.target.instance };
|
|
26984
|
+
function lifecycleTarget2(plan, operation) {
|
|
26985
|
+
return plan.scopes.find((row) => row.scope.id === operation.scope.id)?.target ?? { kind: operation.scope.target.kind, instance: operation.scope.target.instance };
|
|
26410
26986
|
}
|
|
26411
26987
|
|
|
26412
26988
|
class Ledger {
|
|
@@ -26467,8 +27043,18 @@ class Ledger {
|
|
|
26467
27043
|
return this.state.activations.find((row) => row.scopeId === scopeId && row.packageId === packageId && row.nativeId === nativeId);
|
|
26468
27044
|
}
|
|
26469
27045
|
markApplying(plan, operation) {
|
|
27046
|
+
if (operation.action === "disable-nonconforming")
|
|
27047
|
+
this.disablePending(plan, operation, "applying");
|
|
26470
27048
|
this.replaceAttempt(plan, (attempt) => this.journaled(attempt, operation.operationId, "applying", "applying", true));
|
|
26471
27049
|
}
|
|
27050
|
+
markNotApplied(plan, operation) {
|
|
27051
|
+
if (operation.action === "disable-nonconforming")
|
|
27052
|
+
this.clearDisablePending(operation);
|
|
27053
|
+
this.replaceAttempt(plan, (attempt) => {
|
|
27054
|
+
const next = this.journaled(attempt, operation.operationId, "not-attempted", "failed", false);
|
|
27055
|
+
return { ...next, mutationStarted: next.journal.some((row) => mutationBegan(row.state)) };
|
|
27056
|
+
});
|
|
27057
|
+
}
|
|
26472
27058
|
markPruning(plan, operation) {
|
|
26473
27059
|
this.replaceAttempt(plan, (attempt) => this.journaled(attempt, operation.operationId, "applying", "pruning", true));
|
|
26474
27060
|
}
|
|
@@ -26532,13 +27118,36 @@ class Ledger {
|
|
|
26532
27118
|
}
|
|
26533
27119
|
markRolledBack(plan, operation) {
|
|
26534
27120
|
this.adoptMovedGeneration();
|
|
27121
|
+
if (operation.action === "disable-nonconforming")
|
|
27122
|
+
this.clearDisablePending(operation, true);
|
|
26535
27123
|
this.replaceAttempt(plan, (attempt) => this.journaled(attempt, operation.operationId, "rolled-back", "failed", true));
|
|
26536
27124
|
}
|
|
27125
|
+
confirmDisable(plan, operation) {
|
|
27126
|
+
this.clearDisablePending(operation);
|
|
27127
|
+
this.state = { ...this.state, activations: this.state.activations.map((row) => row.scopeId === operation.scope.id && row.packageId === operation.package ? { ...row, activationState: "inactive", readbackState: "verified", readbackAt: this.now, updatedAt: this.now } : row) };
|
|
27128
|
+
this.replaceAttempt(plan, (attempt) => this.journaled(attempt, operation.operationId, "readback-verified", "readback", true));
|
|
27129
|
+
}
|
|
27130
|
+
completeContainment(plan, operation) {
|
|
27131
|
+
this.replaceAttempt(plan, (attempt) => this.journaled(attempt, operation.operationId, "completed", "failed", true));
|
|
27132
|
+
}
|
|
26537
27133
|
markRollbackRequired(plan, operation) {
|
|
26538
27134
|
this.adoptMovedGeneration();
|
|
26539
27135
|
this.retargetBareAlias(plan, operation);
|
|
27136
|
+
if (operation.action === "disable-nonconforming")
|
|
27137
|
+
this.disablePending(plan, operation, "rollback");
|
|
26540
27138
|
this.replaceAttempt(plan, (attempt) => this.journaled(attempt, operation.operationId, "rollback", "recovery-required", true));
|
|
26541
27139
|
}
|
|
27140
|
+
disablePending(plan, operation, phase) {
|
|
27141
|
+
this.state = { ...this.state, activations: this.state.activations.map((row) => row.scopeId === operation.scope.id && row.packageId === operation.package ? { ...row, pending: { operation: "disable-nonconforming", phase, attemptId: plan.attemptId, startedAt: this.now } } : row) };
|
|
27142
|
+
}
|
|
27143
|
+
clearDisablePending(operation, rolledBack = false) {
|
|
27144
|
+
this.state = { ...this.state, activations: this.state.activations.map((row) => {
|
|
27145
|
+
if (row.scopeId !== operation.scope.id || row.packageId !== operation.package)
|
|
27146
|
+
return row;
|
|
27147
|
+
const { pending: _pending, ...prior } = row;
|
|
27148
|
+
return rolledBack ? { ...prior, activationState: "nonconforming", readbackState: "unverified" } : prior;
|
|
27149
|
+
}) };
|
|
27150
|
+
}
|
|
26542
27151
|
markCompleted(plan, operation) {
|
|
26543
27152
|
const desiredPairs = plan.operations.filter((row) => row.operation.scope.id === operation.scope.id && row.operation.coverage === "desired-pair");
|
|
26544
27153
|
const converged = desiredPairs.every((row) => row.operation.operationId === operation.operationId || row.operation.action === "unchanged" || this.journalState(plan.attemptId, row.operation.operationId) === "completed");
|
|
@@ -26557,7 +27166,13 @@ class Ledger {
|
|
|
26557
27166
|
if (scope.authority !== "authoritative")
|
|
26558
27167
|
return scope;
|
|
26559
27168
|
const planned = plan.scopes.find((row) => row.scope.id === scope.id);
|
|
27169
|
+
if (planned?.retirementPackages !== undefined)
|
|
27170
|
+
return scope;
|
|
26560
27171
|
if (planned?.selectorMode === "retired") {
|
|
27172
|
+
if (this.state.activations.some((row) => row.scopeId === scope.id))
|
|
27173
|
+
return scope;
|
|
27174
|
+
if (plan.operations.some((row) => row.operation.scope.id === scope.id && row.operation.operationId !== operation.operationId && this.journalState(plan.attemptId, row.operation.operationId) !== "completed"))
|
|
27175
|
+
return scope;
|
|
26561
27176
|
const { desired: _desired, lastConverged: _converged, ...kept } = scope;
|
|
26562
27177
|
return {
|
|
26563
27178
|
...kept,
|
|
@@ -26625,7 +27240,7 @@ class Ledger {
|
|
|
26625
27240
|
if (existing !== undefined && existing.authority !== "authoritative")
|
|
26626
27241
|
return existing;
|
|
26627
27242
|
const selectorMode = planned.selectorMode;
|
|
26628
|
-
const target = { kind: planned.scope.target.kind, instance: planned.scope.target.instance };
|
|
27243
|
+
const target = planned.target ?? { kind: planned.scope.target.kind, instance: planned.scope.target.instance };
|
|
26629
27244
|
if (existing === undefined) {
|
|
26630
27245
|
return {
|
|
26631
27246
|
id: planned.scope.id,
|
|
@@ -26791,19 +27406,40 @@ function succeeded(operation, changed, notices = []) {
|
|
|
26791
27406
|
};
|
|
26792
27407
|
}
|
|
26793
27408
|
async function recoverApplied(plan, operation, host, ledger, handle, reason) {
|
|
27409
|
+
let verified = false;
|
|
27410
|
+
let diagnostic = "";
|
|
26794
27411
|
try {
|
|
26795
|
-
|
|
26796
|
-
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
|
|
27412
|
+
try {
|
|
27413
|
+
await host.adapter.rollback(handle);
|
|
27414
|
+
const observation = await host.adapter.readback(handle);
|
|
27415
|
+
host.adapter.verifyRollback(handle, observation);
|
|
27416
|
+
verified = true;
|
|
27417
|
+
} catch (error) {
|
|
27418
|
+
diagnostic = `; rollback verification failed: ${unknownErrorDiagnostic(error)}`;
|
|
27419
|
+
}
|
|
27420
|
+
try {
|
|
27421
|
+
if (verified)
|
|
27422
|
+
ledger.markRolledBack(plan, operation);
|
|
27423
|
+
else
|
|
27424
|
+
ledger.markRollbackRequired(plan, operation);
|
|
27425
|
+
} catch (error) {
|
|
27426
|
+
verified = false;
|
|
27427
|
+
diagnostic += `; recovery ledger could not be persisted: ${unknownErrorDiagnostic(error)}`;
|
|
27428
|
+
}
|
|
27429
|
+
if (verified) {
|
|
27430
|
+
try {
|
|
27431
|
+
await host.adapter.cleanup(handle, "verified-rollback");
|
|
27432
|
+
} catch {}
|
|
27433
|
+
}
|
|
27434
|
+
} finally {
|
|
27435
|
+
try {
|
|
27436
|
+
await host.adapter.releaseRecovery(handle);
|
|
27437
|
+
} catch (error) {
|
|
27438
|
+
verified = false;
|
|
27439
|
+
diagnostic += `; recovery resources could not be released: ${unknownErrorDiagnostic(error)}`;
|
|
27440
|
+
}
|
|
26801
27441
|
}
|
|
26802
|
-
|
|
26803
|
-
try {
|
|
26804
|
-
await host.adapter.cleanup(handle, "verified-rollback");
|
|
26805
|
-
} catch {}
|
|
26806
|
-
return reportedRollback(operation, reason);
|
|
27442
|
+
return verified ? reportedRollback(operation, reason) : pendingRecovery(operation, `; original failure: ${reason.diagnostic}${diagnostic}`);
|
|
26807
27443
|
}
|
|
26808
27444
|
async function abortPreparation(host, preparation) {
|
|
26809
27445
|
try {
|
|
@@ -26831,8 +27467,8 @@ function pendingReadback(operation, reason) {
|
|
|
26831
27467
|
}
|
|
26832
27468
|
};
|
|
26833
27469
|
}
|
|
26834
|
-
function pendingRecovery(operation) {
|
|
26835
|
-
const reason = createLifecycleReason("recovery", "recovery.required", `rollback for '${operation.operationId}' did not verify`);
|
|
27470
|
+
function pendingRecovery(operation, diagnostic = "") {
|
|
27471
|
+
const reason = createLifecycleReason("recovery", "recovery.required", `rollback for '${operation.operationId}' did not verify${diagnostic}`);
|
|
26836
27472
|
return {
|
|
26837
27473
|
stop: true,
|
|
26838
27474
|
reason,
|
|
@@ -26868,8 +27504,8 @@ function pendingCleanup(operation) {
|
|
|
26868
27504
|
outcome: {
|
|
26869
27505
|
...operation,
|
|
26870
27506
|
result: "pending",
|
|
26871
|
-
resourceState: "present",
|
|
26872
|
-
activationState: "active-conforming",
|
|
27507
|
+
resourceState: operation.action === "disable-nonconforming" ? "retained" : "present",
|
|
27508
|
+
activationState: operation.action === "disable-nonconforming" ? "inactive" : "active-conforming",
|
|
26873
27509
|
changed: true,
|
|
26874
27510
|
reason
|
|
26875
27511
|
}
|
|
@@ -26948,7 +27584,7 @@ function summaryFor(outcomes) {
|
|
|
26948
27584
|
const failed = outcomes.find((outcome) => outcome.result !== "succeeded");
|
|
26949
27585
|
const changed = outcomes.some((outcome) => outcome.changed);
|
|
26950
27586
|
const pending = outcomes.find((outcome) => outcome.result === "pending");
|
|
26951
|
-
const readback = outcomes.find((outcome) => outcome.reason?.category === "readback");
|
|
27587
|
+
const readback = outcomes.find((outcome) => outcome.reason?.category === "readback" || outcome.action === "disable-nonconforming" && outcome.changed);
|
|
26952
27588
|
const reapplied = outcomes.some((outcome) => outcome.result === "succeeded" && outcome.action === "update");
|
|
26953
27589
|
const mutationStarted = changed || pending !== undefined || reapplied;
|
|
26954
27590
|
if (failed === undefined) {
|
|
@@ -27042,7 +27678,8 @@ async function runFrozenLifecycle(command) {
|
|
|
27042
27678
|
validatedAt: command.now,
|
|
27043
27679
|
hosts: command.hosts,
|
|
27044
27680
|
...command.command === undefined ? {} : { command: command.command },
|
|
27045
|
-
...command.transportSource === undefined ? {} : { transportSource: command.transportSource }
|
|
27681
|
+
...command.transportSource === undefined ? {} : { transportSource: command.transportSource },
|
|
27682
|
+
...command.removalSelection === undefined ? {} : { removalSelection: command.removalSelection }
|
|
27046
27683
|
});
|
|
27047
27684
|
const executed = await executeLifecycle({
|
|
27048
27685
|
plan,
|
|
@@ -27065,6 +27702,7 @@ function plannerHost(kind, adapter, plannedNativeId, announceDryRun) {
|
|
|
27065
27702
|
var lifecyclePlannerHostKinds = [
|
|
27066
27703
|
"claude-code",
|
|
27067
27704
|
"codex",
|
|
27705
|
+
"dcode",
|
|
27068
27706
|
"kimi",
|
|
27069
27707
|
"cursor",
|
|
27070
27708
|
"omp",
|
|
@@ -27076,6 +27714,7 @@ function createLifecyclePlannerHosts() {
|
|
|
27076
27714
|
return [
|
|
27077
27715
|
plannerHost("claude-code", createClaudeCodeLifecycleHost(), claudeCodeWriter.plannedNativeId),
|
|
27078
27716
|
plannerHost("codex", codexLifecycle, codexWriter.plannedNativeId),
|
|
27717
|
+
plannerHost("dcode", dcodeLifecycle, dcodeWriter.plannedNativeId),
|
|
27079
27718
|
plannerHost("kimi", createLifecycleHostAdapter(kimiLifecycle), kimiWriter.plannedNativeId),
|
|
27080
27719
|
plannerHost("cursor", cursorManagedLifecycle, cursorWriter.plannedNativeId, announceCursorDryRun),
|
|
27081
27720
|
plannerHost("omp", ompLifecycle, ompWriter.plannedNativeId),
|
|
@@ -27085,6 +27724,161 @@ function createLifecyclePlannerHosts() {
|
|
|
27085
27724
|
];
|
|
27086
27725
|
}
|
|
27087
27726
|
|
|
27727
|
+
// src/pin-lifecycle.ts
|
|
27728
|
+
import { cpSync as cpSync13, mkdtempSync as mkdtempSync14, rmSync as rmSync15 } from "node:fs";
|
|
27729
|
+
import { tmpdir as tmpdir12 } from "node:os";
|
|
27730
|
+
import { basename as basename9, join as join35, relative as relative8 } from "node:path";
|
|
27731
|
+
async function runOfflineLifecyclePin(host, plugin, dryRun) {
|
|
27732
|
+
const adapter = host.lifecyclePinAdapter;
|
|
27733
|
+
if (adapter === undefined || plugin.path === undefined)
|
|
27734
|
+
throw new Error("offline lifecycle pin has no adapter or active root");
|
|
27735
|
+
let state = readLifecycleState().state;
|
|
27736
|
+
const matches = state.activations.filter((row) => row.nativeId === plugin.id && state.scopes.some((scope) => scope.id === row.scopeId && scope.target.kind === host.id));
|
|
27737
|
+
if (matches.length !== 1)
|
|
27738
|
+
throw new Error("offline lifecycle pin requires one recorded owned activation");
|
|
27739
|
+
const activation = matches[0];
|
|
27740
|
+
const scope = state.scopes.find((row) => row.id === activation.scopeId);
|
|
27741
|
+
if (activation.ownership.kind === "legacy-claim" || activation.route.kind === "legacy-unverified" || activation.sourceRevision === undefined || activation.pending !== undefined)
|
|
27742
|
+
throw new Error("offline lifecycle pin requires verified ownership with no pending recovery");
|
|
27743
|
+
if (activation.activationState !== "active" || activation.readbackState !== "verified")
|
|
27744
|
+
throw new Error("offline lifecycle pin requires a verified active installation");
|
|
27745
|
+
const preview = await host.pin(plugin, { dryRun: true });
|
|
27746
|
+
if (preview.refusals.length !== 0)
|
|
27747
|
+
return { changes: [], refusals: preview.refusals };
|
|
27748
|
+
if (preview.changes.length === 0)
|
|
27749
|
+
return preview;
|
|
27750
|
+
const now = new Date().toISOString();
|
|
27751
|
+
const frozenRoot = mkdtempSync14(join35(tmpdir12(), "plgnz-offline-pin-"));
|
|
27752
|
+
const attemptId = `pin-${basename9(frozenRoot)}`;
|
|
27753
|
+
const operationId = `${attemptId}:${plugin.id}`;
|
|
27754
|
+
let handle;
|
|
27755
|
+
let preparation;
|
|
27756
|
+
let accepted = false, applying = false, committed = false;
|
|
27757
|
+
const attempt = { id: attemptId, command: "update", phase: "accepted", mutationStarted: false, scopeIds: [scope.id], journal: [{ operationId, scopeId: scope.id, packageId: activation.packageId, nativeId: activation.nativeId, action: "update", state: "pending", route: activation.route, startedAt: now, updatedAt: now }], startedAt: now, updatedAt: now };
|
|
27758
|
+
const persist = (nextActivation) => {
|
|
27759
|
+
if (readLifecycleState().state.stateGeneration !== state.stateGeneration)
|
|
27760
|
+
throw new Error("pin ledger changed concurrently; refusing stale commit");
|
|
27761
|
+
const next = { ...state, stateGeneration: state.stateGeneration + 1, activations: state.activations.map((row) => row.scopeId === activation.scopeId && row.nativeId === activation.nativeId ? nextActivation : row), attempts: [...state.attempts.filter((row) => row.id !== attemptId), { ...attempt, journal: attempt.journal.map((row) => ({ ...row })) }] };
|
|
27762
|
+
writeLifecycleState(next, { globalPreflight: "succeeded" });
|
|
27763
|
+
state = next;
|
|
27764
|
+
};
|
|
27765
|
+
const pending = (phase) => persist({ ...activation, pending: { operation: "update", phase, attemptId, startedAt: now }, readbackState: "pending" });
|
|
27766
|
+
try {
|
|
27767
|
+
cpSync13(plugin.path, frozenRoot, { recursive: true });
|
|
27768
|
+
const packageFingerprint = fingerprintTree(frozenRoot);
|
|
27769
|
+
if (packageFingerprint !== activation.fingerprints.installed)
|
|
27770
|
+
throw new Error("pin active bytes differ from recorded readback");
|
|
27771
|
+
const target = { kind: scope.target.kind, instance: scope.target.instance };
|
|
27772
|
+
const observed = await adapter.observeTarget(target);
|
|
27773
|
+
const installed = observed.installations.find((row) => row.nativeId === plugin.id);
|
|
27774
|
+
if (installed?.ownership.kind !== "owned" || installed.ownership.scopeId !== scope.id || installed.installedFingerprint !== packageFingerprint || installed.enablement !== "enabled")
|
|
27775
|
+
throw new Error("pin native identity or bytes differ from the recorded owned activation");
|
|
27776
|
+
const version = await adapter.probeVersion(target);
|
|
27777
|
+
const sourceType = scope.source.kind;
|
|
27778
|
+
const inventory = inventoryPackageSemantics({ dir: frozenRoot, name: activation.packageId, ...plugin.version === undefined ? {} : { version: plugin.version }, contentFingerprint: packageFingerprint });
|
|
27779
|
+
const snapshot = createFrozenPackageSnapshot({ operationId, attemptId, scopeId: scope.id, target, action: "update", packageName: activation.packageId, nativeId: plugin.id, sourceType, immutableRevision: activation.sourceRevision, snapshotRoot: frozenRoot, packageRoot: frozenRoot, relativePackagePath: ".", snapshotFingerprint: packageFingerprint, packageFingerprint, ...sourceType === "git" ? { nativeGit: { locator: scope.source.locator, resolvedRevision: activation.sourceRevision } } : {}, inventory });
|
|
27780
|
+
const pins = createResolvedLifecyclePins(preview.changes.map((change) => ({ server: change.server, executable: change.to })).sort((left, right) => left.server < right.server ? -1 : left.server > right.server ? 1 : 0));
|
|
27781
|
+
const nativeScope = await adapter.observeNativeMutationScope({ targetObservation: observed, operation: "update", packageName: activation.packageId, nativeId: plugin.id, sourceType });
|
|
27782
|
+
const nativeProjection = await adapter.observeNativeProjection({ targetObservation: observed, operation: "update", snapshot, pins });
|
|
27783
|
+
const selection = adapter.decideRoute({ target, operationId, attemptId, scopeId: scope.id, packageName: activation.packageId, nativeId: plugin.id, version, sourceType, targetObservation: observed, nativeScope, nativeProjection, planCoverage: createLifecyclePlanCoverage(observed, [{ nativeId: plugin.id, operationId, operation: "update", mutationGroupId: operationId, authorization: "observed-owned" }]), operation: "update", snapshot, pins });
|
|
27784
|
+
if (selection.kind !== "selected")
|
|
27785
|
+
throw new Error(`pin capability ${selection.status}: ${selection.gaps.map((gap) => gap.capabilityId).join(", ")}`);
|
|
27786
|
+
if (selection.route !== "managed")
|
|
27787
|
+
throw new Error("offline lifecycle pin requires a frozen Managed projection");
|
|
27788
|
+
if (dryRun)
|
|
27789
|
+
return preview;
|
|
27790
|
+
pending("accepted");
|
|
27791
|
+
accepted = true;
|
|
27792
|
+
preparation = await adapter.stageActivation({ selection, snapshot, pins });
|
|
27793
|
+
preparation = await adapter.applyLifecycleDirectives(preparation);
|
|
27794
|
+
preparation = await adapter.applyPins(preparation);
|
|
27795
|
+
const sealed = await adapter.sealActivation(preparation);
|
|
27796
|
+
preparation = sealed;
|
|
27797
|
+
handle = sealed.handle;
|
|
27798
|
+
attempt.phase = "applying";
|
|
27799
|
+
attempt.mutationStarted = true;
|
|
27800
|
+
attempt.journal[0].state = "applying";
|
|
27801
|
+
pending("applying");
|
|
27802
|
+
applying = true;
|
|
27803
|
+
await adapter.apply(sealed);
|
|
27804
|
+
const observation = await adapter.readback(handle);
|
|
27805
|
+
adapter.verify(handle, observation);
|
|
27806
|
+
if (observation.installedFingerprint === null || handle.projectedFingerprint === null)
|
|
27807
|
+
throw new Error("pin readback lacks exact installed/projected fingerprint");
|
|
27808
|
+
attempt.phase = "finalizing";
|
|
27809
|
+
attempt.journal[0].state = "readback-verified";
|
|
27810
|
+
const pinnedActivation = { ...activation, route: { kind: handle.route, evidenceKey: { kind: "capability-profile", key: handle.evidenceId } }, fingerprints: { ...activation.fingerprints, projected: handle.projectedFingerprint, installed: observation.installedFingerprint }, pins: [...new Set([...activation.pins, ...pins.map((pin) => pin.server)])].sort(), readbackState: "verified", readbackAt: now, updatedAt: now };
|
|
27811
|
+
persist(pinnedActivation);
|
|
27812
|
+
committed = true;
|
|
27813
|
+
try {
|
|
27814
|
+
await adapter.cleanup(handle, "verified-commit");
|
|
27815
|
+
} catch (error) {
|
|
27816
|
+
attempt.journal[0].state = "cleanup-pending";
|
|
27817
|
+
persist({ ...pinnedActivation, pending: { operation: "update", phase: "cleanup", attemptId, startedAt: now } });
|
|
27818
|
+
await adapter.releaseRecovery(handle);
|
|
27819
|
+
throw error;
|
|
27820
|
+
}
|
|
27821
|
+
attempt.phase = "completed";
|
|
27822
|
+
attempt.journal[0].state = "completed";
|
|
27823
|
+
attempt.completedAt = now;
|
|
27824
|
+
persist(pinnedActivation);
|
|
27825
|
+
const activatedRoot = observation.contentRoots[0]?.path;
|
|
27826
|
+
return { ...preview, changes: preview.changes.map((change) => ({ ...change, file: activatedRoot === undefined ? change.file : join35(activatedRoot, relative8(plugin.path, change.file)) })) };
|
|
27827
|
+
} catch (error) {
|
|
27828
|
+
if (!committed && handle !== undefined && applying) {
|
|
27829
|
+
if (!(error instanceof LifecycleHostPhaseError && error.mutationStarted === false)) {
|
|
27830
|
+
attempt.phase = "recovery-required";
|
|
27831
|
+
attempt.journal[0].state = "rollback";
|
|
27832
|
+
try {
|
|
27833
|
+
let journalError;
|
|
27834
|
+
try {
|
|
27835
|
+
pending("rollback");
|
|
27836
|
+
} catch (writeError) {
|
|
27837
|
+
journalError = writeError;
|
|
27838
|
+
}
|
|
27839
|
+
await adapter.rollback(handle);
|
|
27840
|
+
adapter.verifyRollback(handle, await adapter.readback(handle));
|
|
27841
|
+
if (journalError !== undefined)
|
|
27842
|
+
throw new Error(`rollback verified but its durable receipt is blocked: ${journalError.message}`);
|
|
27843
|
+
attempt.phase = "finalizing";
|
|
27844
|
+
attempt.journal[0].state = "cleanup-pending";
|
|
27845
|
+
persist({ ...activation, pending: { operation: "update", phase: "cleanup", attemptId, startedAt: now } });
|
|
27846
|
+
await adapter.cleanup(handle, "verified-rollback");
|
|
27847
|
+
attempt.phase = "failed";
|
|
27848
|
+
attempt.journal[0].state = "rolled-back";
|
|
27849
|
+
persist(activation);
|
|
27850
|
+
} catch (recoveryError) {
|
|
27851
|
+
await adapter.releaseRecovery(handle);
|
|
27852
|
+
throw new Error(`pin failed: ${error.message}; recovery remains pending: ${recoveryError.message}`);
|
|
27853
|
+
}
|
|
27854
|
+
} else {
|
|
27855
|
+
attempt.phase = "failed";
|
|
27856
|
+
attempt.journal[0].state = "not-attempted";
|
|
27857
|
+
attempt.mutationStarted = false;
|
|
27858
|
+
persist(activation);
|
|
27859
|
+
await adapter.cleanup(handle, "aborted-preparation");
|
|
27860
|
+
}
|
|
27861
|
+
} else if (!committed && accepted) {
|
|
27862
|
+
try {
|
|
27863
|
+
if (preparation !== undefined)
|
|
27864
|
+
await adapter.cleanup(preparation, "aborted-preparation");
|
|
27865
|
+
} finally {
|
|
27866
|
+
attempt.phase = "failed";
|
|
27867
|
+
attempt.journal[0].state = "not-attempted";
|
|
27868
|
+
persist(activation);
|
|
27869
|
+
}
|
|
27870
|
+
}
|
|
27871
|
+
throw error;
|
|
27872
|
+
} finally {
|
|
27873
|
+
try {
|
|
27874
|
+
if (handle !== undefined)
|
|
27875
|
+
await adapter.releaseRecovery(handle);
|
|
27876
|
+
} finally {
|
|
27877
|
+
rmSync15(frozenRoot, { recursive: true, force: true });
|
|
27878
|
+
}
|
|
27879
|
+
}
|
|
27880
|
+
}
|
|
27881
|
+
|
|
27088
27882
|
// src/pin.ts
|
|
27089
27883
|
function defaultPinTargets(all = writers) {
|
|
27090
27884
|
return all.filter((w) => w.gui);
|
|
@@ -27095,13 +27889,20 @@ async function runPin(options = {}) {
|
|
|
27095
27889
|
const selected = selectTargets(candidates, options);
|
|
27096
27890
|
const findings = [];
|
|
27097
27891
|
let ledgerChanged = false;
|
|
27892
|
+
const changedRecords = new Set;
|
|
27098
27893
|
for (const host of selected) {
|
|
27099
27894
|
if (!host.detect())
|
|
27100
27895
|
continue;
|
|
27101
27896
|
for (const plugin of host.listInstalled()) {
|
|
27102
27897
|
if (plugin.path === undefined)
|
|
27103
27898
|
continue;
|
|
27104
|
-
|
|
27899
|
+
let outcome;
|
|
27900
|
+
try {
|
|
27901
|
+
outcome = host.lifecyclePinAdapter === undefined ? await host.pin(plugin, { dryRun: options.dryRun }) : await runOfflineLifecyclePin(host, plugin, options.dryRun === true);
|
|
27902
|
+
} catch (error) {
|
|
27903
|
+
findings.push({ host: host.id, mark: "✗", message: `plugin '${plugin.id}' pin failed — ${error.message}` });
|
|
27904
|
+
continue;
|
|
27905
|
+
}
|
|
27105
27906
|
const prefix = options.dryRun === true ? "[dry-run] " : "";
|
|
27106
27907
|
for (const change of outcome.changes) {
|
|
27107
27908
|
findings.push({
|
|
@@ -27119,6 +27920,8 @@ async function runPin(options = {}) {
|
|
|
27119
27920
|
}
|
|
27120
27921
|
if (outcome.changes.length === 0)
|
|
27121
27922
|
continue;
|
|
27923
|
+
if (host.lifecyclePinAdapter !== undefined)
|
|
27924
|
+
continue;
|
|
27122
27925
|
const record = findRecord(state, host.id, plugin.id);
|
|
27123
27926
|
if (record === undefined) {
|
|
27124
27927
|
findings.push({
|
|
@@ -27130,6 +27933,7 @@ async function runPin(options = {}) {
|
|
|
27130
27933
|
}
|
|
27131
27934
|
if (options.dryRun === true)
|
|
27132
27935
|
continue;
|
|
27936
|
+
changedRecords.add(record);
|
|
27133
27937
|
const pins = new Set(record.pins ?? []);
|
|
27134
27938
|
for (const change of outcome.changes)
|
|
27135
27939
|
pins.add(change.server);
|
|
@@ -27147,10 +27951,10 @@ async function runPin(options = {}) {
|
|
|
27147
27951
|
}
|
|
27148
27952
|
}
|
|
27149
27953
|
if (ledgerChanged)
|
|
27150
|
-
persistRecordedPins(state, options.state !== undefined);
|
|
27954
|
+
persistRecordedPins(state, options.state !== undefined, changedRecords);
|
|
27151
27955
|
return { findings, exitCode: findings.some((f) => f.mark === "✗") ? 1 : 0 };
|
|
27152
27956
|
}
|
|
27153
|
-
function persistRecordedPins(records, legacyLedger) {
|
|
27957
|
+
function persistRecordedPins(records, legacyLedger, changedRecords) {
|
|
27154
27958
|
const loaded = legacyLedger ? null : readLifecycleState();
|
|
27155
27959
|
if (loaded === null || loaded.sourceVersion !== 2) {
|
|
27156
27960
|
writeState(records);
|
|
@@ -27162,7 +27966,7 @@ function persistRecordedPins(records, legacyLedger) {
|
|
|
27162
27966
|
stateGeneration: loaded.state.stateGeneration + 1,
|
|
27163
27967
|
activations: loaded.state.activations.map((activation) => {
|
|
27164
27968
|
const scope = scopes.get(activation.scopeId);
|
|
27165
|
-
const record = records.find((candidate) => candidate.host === scope?.target.kind && candidate.id === activation.nativeId);
|
|
27969
|
+
const record = records.find((candidate) => changedRecords.has(candidate) && candidate.host === scope?.target.kind && candidate.id === activation.nativeId);
|
|
27166
27970
|
if (record === undefined)
|
|
27167
27971
|
return activation;
|
|
27168
27972
|
return {
|
|
@@ -27634,7 +28438,7 @@ function legacyCommandAction(command) {
|
|
|
27634
28438
|
// package.json
|
|
27635
28439
|
var package_default = {
|
|
27636
28440
|
name: "plugnz",
|
|
27637
|
-
version: "0.0.
|
|
28441
|
+
version: "0.0.9",
|
|
27638
28442
|
description: "Install, diagnose and update agent plugins (Agent Plugins spec) and MCP configs across Claude Code, Codex, Cursor, Kimi and more",
|
|
27639
28443
|
license: "MIT",
|
|
27640
28444
|
type: "module",
|
|
@@ -27877,10 +28681,17 @@ function withoutRetiredActivation(state, scopeId, packageId, nativeId, retiredAt
|
|
|
27877
28681
|
if (activation === undefined)
|
|
27878
28682
|
throw new Error(`activation '${packageId}' is not recorded for manual removal`);
|
|
27879
28683
|
const dropped = state.activations.filter((row) => row !== activation);
|
|
28684
|
+
const attempts = state.attempts.map((attempt) => attempt.id !== activation.pending?.attemptId ? attempt : {
|
|
28685
|
+
...attempt,
|
|
28686
|
+
phase: "completed",
|
|
28687
|
+
completedAt: retiredAt,
|
|
28688
|
+
updatedAt: retiredAt,
|
|
28689
|
+
journal: attempt.journal.map((entry) => ({ ...entry, state: "completed", updatedAt: retiredAt }))
|
|
28690
|
+
});
|
|
27880
28691
|
const tombstone = retirementTombstone(activation, retiredAt);
|
|
27881
28692
|
if (tombstone === null) {
|
|
27882
28693
|
if (activation.ownership.kind === "legacy-claim" && activation.route.kind === "legacy-unverified" && (activation.ownership.prior === "plgnz" || activation.ownership.prior === "unrecorded")) {
|
|
27883
|
-
return { ...state, stateGeneration: state.stateGeneration + 1, activations: dropped };
|
|
28694
|
+
return { ...state, stateGeneration: state.stateGeneration + 1, activations: dropped, attempts };
|
|
27884
28695
|
}
|
|
27885
28696
|
throw new Error(`activation '${packageId}' cannot retain a tombstone`);
|
|
27886
28697
|
}
|
|
@@ -27888,9 +28699,47 @@ function withoutRetiredActivation(state, scopeId, packageId, nativeId, retiredAt
|
|
|
27888
28699
|
...state,
|
|
27889
28700
|
stateGeneration: state.stateGeneration + 1,
|
|
27890
28701
|
activations: dropped,
|
|
28702
|
+
attempts,
|
|
27891
28703
|
tombstones: state.tombstones.some((row) => row.id === tombstone.id) ? state.tombstones : [...state.tombstones, tombstone]
|
|
27892
28704
|
};
|
|
27893
28705
|
}
|
|
28706
|
+
function withPendingRemoval(state, operation, now) {
|
|
28707
|
+
const activation = state.activations.find((row) => row.scopeId === operation.scope.id && row.nativeId === operation.nativeId && (row.packageId === operation.package || row.packageId === operation.nativeId));
|
|
28708
|
+
if (activation === undefined)
|
|
28709
|
+
throw new Error(`activation '${operation.package}' is not recorded for manual removal`);
|
|
28710
|
+
if (activation.pending?.operation === "remove")
|
|
28711
|
+
return { ...state, stateGeneration: state.stateGeneration + 1 };
|
|
28712
|
+
const attemptId = `remove-attempt-${state.stateGeneration}-${operation.operationId}`;
|
|
28713
|
+
return {
|
|
28714
|
+
...state,
|
|
28715
|
+
stateGeneration: state.stateGeneration + 1,
|
|
28716
|
+
scopes: state.scopes.map((scope) => scope.id === operation.scope.id ? { ...scope, lastAttemptId: attemptId } : scope),
|
|
28717
|
+
activations: state.activations.map((row) => row !== activation ? row : {
|
|
28718
|
+
...row,
|
|
28719
|
+
pending: { operation: "remove", phase: "applying", attemptId, startedAt: now }
|
|
28720
|
+
}),
|
|
28721
|
+
attempts: [...state.attempts, {
|
|
28722
|
+
id: attemptId,
|
|
28723
|
+
command: "remove",
|
|
28724
|
+
phase: "pruning",
|
|
28725
|
+
mutationStarted: true,
|
|
28726
|
+
scopeIds: [operation.scope.id],
|
|
28727
|
+
startedAt: now,
|
|
28728
|
+
updatedAt: now,
|
|
28729
|
+
journal: [{
|
|
28730
|
+
operationId: operation.operationId,
|
|
28731
|
+
scopeId: operation.scope.id,
|
|
28732
|
+
packageId: activation.packageId,
|
|
28733
|
+
nativeId: activation.nativeId,
|
|
28734
|
+
action: "remove",
|
|
28735
|
+
state: "applying",
|
|
28736
|
+
route: activation.route,
|
|
28737
|
+
startedAt: now,
|
|
28738
|
+
updatedAt: now
|
|
28739
|
+
}]
|
|
28740
|
+
}]
|
|
28741
|
+
};
|
|
28742
|
+
}
|
|
27894
28743
|
function reportFor(command, dryRun, plan, outcomes, options = {}) {
|
|
27895
28744
|
const failure = options.reason ?? outcomes.find((candidate) => candidate.result !== "succeeded")?.reason ?? null;
|
|
27896
28745
|
const result = options.result ?? (failure === null && outcomes.every((candidate) => candidate.result === "succeeded") ? "converged" : "incomplete");
|
|
@@ -28598,19 +29447,13 @@ async function runRecordedLifecycleRemove(flags, target, state, selected) {
|
|
|
28598
29447
|
const alias = writer?.persistedNativeIdMayAlias?.(activation.nativeId, target) === true;
|
|
28599
29448
|
if (target !== activation.packageId && target !== activation.nativeId && !alias)
|
|
28600
29449
|
return [];
|
|
28601
|
-
return [{ scope }];
|
|
29450
|
+
return [{ scope, activation }];
|
|
28602
29451
|
});
|
|
28603
29452
|
if (matches.length === 0 || matches.some((match) => !plannerKinds.has(match.scope.target.kind)))
|
|
28604
29453
|
return null;
|
|
28605
29454
|
const byScope = new Map;
|
|
28606
29455
|
for (const match of matches)
|
|
28607
29456
|
byScope.set(match.scope.id, match.scope);
|
|
28608
|
-
for (const scopeId of byScope.keys()) {
|
|
28609
|
-
const inScope = state.activations.filter((activation) => activation.scopeId === scopeId).length;
|
|
28610
|
-
const selectedInScope = matches.filter((match) => match.scope.id === scopeId).length;
|
|
28611
|
-
if (inScope !== selectedInScope)
|
|
28612
|
-
return null;
|
|
28613
|
-
}
|
|
28614
29457
|
const perHost = new Map;
|
|
28615
29458
|
for (const match of matches) {
|
|
28616
29459
|
const count = (perHost.get(match.scope.target.kind) ?? 0) + 1;
|
|
@@ -28643,7 +29486,12 @@ async function runRecordedLifecycleRemove(flags, target, state, selected) {
|
|
|
28643
29486
|
dryRun: flags.dryRun,
|
|
28644
29487
|
hosts: createLifecyclePlannerHosts(),
|
|
28645
29488
|
now: new Date().toISOString(),
|
|
28646
|
-
command: "remove"
|
|
29489
|
+
command: "remove",
|
|
29490
|
+
removalSelection: matches.map(({ activation }) => ({
|
|
29491
|
+
scopeId: activation.scopeId,
|
|
29492
|
+
packageId: activation.packageId,
|
|
29493
|
+
nativeId: activation.nativeId
|
|
29494
|
+
}))
|
|
28647
29495
|
});
|
|
28648
29496
|
return executed.report;
|
|
28649
29497
|
}
|
|
@@ -29175,20 +30023,22 @@ async function main(argv) {
|
|
|
29175
30023
|
preflightFailure = createLifecycleReason("internal", "internal.invariant", "install is pending; refusing removal");
|
|
29176
30024
|
break;
|
|
29177
30025
|
}
|
|
30026
|
+
const pendingOperationId = claimed?.pending?.operation === "remove" ? lifecycleState.attempts.find((attempt) => attempt.id === claimed.pending.attemptId)?.journal.find((entry) => entry.scopeId === claimed.scopeId && entry.packageId === claimed.packageId && entry.nativeId === claimed.nativeId)?.operationId : undefined;
|
|
29178
30027
|
pairs.push({
|
|
29179
30028
|
writer,
|
|
29180
30029
|
record,
|
|
29181
30030
|
nativeId: identity.nativeId,
|
|
29182
30031
|
legacyNativeIds: identity.legacyNativeIds,
|
|
29183
30032
|
failure: null,
|
|
29184
|
-
operation: planOperation2({
|
|
30033
|
+
operation: { ...planOperation2({
|
|
29185
30034
|
command: "remove",
|
|
29186
30035
|
scope: selected.scope,
|
|
29187
30036
|
package: identity.package,
|
|
29188
30037
|
nativeId: identity.nativeId,
|
|
29189
30038
|
action: "retire-orphan",
|
|
29190
|
-
route: "managed"
|
|
29191
|
-
|
|
30039
|
+
route: "managed",
|
|
30040
|
+
discriminator: lifecycleVersion === 2 ? String(lifecycleState.stateGeneration) : undefined
|
|
30041
|
+
}), ...pendingOperationId === undefined ? {} : { operationId: pendingOperationId } }
|
|
29192
30042
|
});
|
|
29193
30043
|
}
|
|
29194
30044
|
if (preflightFailure !== null) {
|
|
@@ -29244,7 +30094,7 @@ async function main(argv) {
|
|
|
29244
30094
|
throw new LifecycleCommandError(createLifecycleReason("internal", "internal.invariant", `state generation moved before removal of '${nativeId}'`));
|
|
29245
30095
|
}
|
|
29246
30096
|
try {
|
|
29247
|
-
lifecycleState =
|
|
30097
|
+
lifecycleState = withPendingRemoval(fresh.state, operation, new Date().toISOString());
|
|
29248
30098
|
} catch (error) {
|
|
29249
30099
|
throw new LifecycleCommandError(createLifecycleReason("internal", "internal.invariant", unknownErrorDiagnostic(error)));
|
|
29250
30100
|
}
|
|
@@ -29276,6 +30126,12 @@ async function main(argv) {
|
|
|
29276
30126
|
}
|
|
29277
30127
|
pairTerminalPhase = "finalize";
|
|
29278
30128
|
if (lifecycleVersion === 2) {
|
|
30129
|
+
const fresh = readLifecycleState();
|
|
30130
|
+
if (fresh.sourceVersion !== 2 || fresh.state.stateGeneration !== lifecycleState.stateGeneration) {
|
|
30131
|
+
throw new LifecycleCommandError(createLifecycleReason("internal", "internal.invariant", `state generation moved before retirement finalization of '${nativeId}'`));
|
|
30132
|
+
}
|
|
30133
|
+
lifecycleState = withoutRetiredActivation(fresh.state, operation.scope.id, operation.package, nativeId, new Date().toISOString());
|
|
30134
|
+
writeLifecycleState(lifecycleState, { globalPreflight: "succeeded" });
|
|
29279
30135
|
state = readState();
|
|
29280
30136
|
} else {
|
|
29281
30137
|
const finalized = state.filter((candidate) => candidate !== pending);
|