agentwheel 0.20.11 → 0.20.13
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/README.md +23 -6
- package/dist/index.js +73 -40
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +8 -1
- package/skills/agentwheel-discovery/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -414,12 +414,14 @@ label is not endpoint identity.
|
|
|
414
414
|
|
|
415
415
|
State created by an older Agentwheel release may be named with the full target fingerprint. For a
|
|
416
416
|
local target, Agentwheel adopts that state only when one canonical graph lock, its v2 install
|
|
417
|
-
manifest, the contribution owner, target details
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
the
|
|
417
|
+
manifest, the contribution owner, and target details agree. When a tracked graph lock has advanced
|
|
418
|
+
past the installed manifest, Agentwheel uses the manifest only as prior runtime state and resolves
|
|
419
|
+
the graph fresh; it never treats that newer lock as the previously installed graph. Mixed digests
|
|
420
|
+
inside the owned contribution still fail closed. A supported install or uninstall carries an
|
|
421
|
+
explicit migration and revalidates the old and new state under the shared apply lock before replacing
|
|
422
|
+
the legacy paths. An explicit `stateKey` remains the manifest and source-lock key while its legacy
|
|
423
|
+
graph-lock path moves. Status, dependency inspection, uninstall, and journal commands use the same
|
|
424
|
+
discovery rules.
|
|
423
425
|
|
|
424
426
|
Agentwheel refuses ambiguous candidates, a graph lock without its correlated manifest, or
|
|
425
427
|
disagreeing stable and legacy state, and preserves those files for manual reconciliation. A pending
|
|
@@ -443,6 +445,21 @@ agentwheel install --fleet example-fleet --agent lab-claude \
|
|
|
443
445
|
Once the stable manifest exists, normal status and planning use it while retaining the invalid
|
|
444
446
|
historical candidate for explicit later cleanup.
|
|
445
447
|
|
|
448
|
+
When stale ownership must be retired before the target-state migration can run, bind the retirement
|
|
449
|
+
to the exact already-installed Fleet manifest instead of asking target-state discovery to choose it:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
agentwheel ownership retire-stale \
|
|
453
|
+
--from-workspace-root <missing-worktree> \
|
|
454
|
+
--to-workspace-root <fleet-root> \
|
|
455
|
+
--source-state-key <stale-state-key> \
|
|
456
|
+
--destination-state-key <fleet-owned-state-key> \
|
|
457
|
+
--fleet <fleet-id> --agent <agent> --json
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
The dry-run still proves exact destination coverage and runtime bytes. Apply only its emitted command,
|
|
461
|
+
which includes the plan, source, destination, and manifest-inventory revisions.
|
|
462
|
+
|
|
446
463
|
## Core Ideas
|
|
447
464
|
|
|
448
465
|
**Three places, one direction:**
|
package/dist/index.js
CHANGED
|
@@ -8469,12 +8469,15 @@ async function discoverTargetApplyJournal(options) {
|
|
|
8469
8469
|
throw new Error(`Legacy apply journal graph-lock digest mismatch at ${item.path}.`);
|
|
8470
8470
|
}
|
|
8471
8471
|
const manifest = validateJournalManifestEnvelope(journal.manifest, options, journalStateKey, item.path);
|
|
8472
|
-
validateLegacyManifestCandidate(manifest, {
|
|
8472
|
+
const validatedManifest = validateLegacyManifestCandidate(manifest, {
|
|
8473
8473
|
...options,
|
|
8474
8474
|
candidateStateKey: journalStateKey,
|
|
8475
8475
|
graphLockDigest: journal.graphLockDigest,
|
|
8476
8476
|
graphLockPath: journal.graphLockPath
|
|
8477
8477
|
});
|
|
8478
|
+
if (!validatedManifest.graphLockCorrelated) {
|
|
8479
|
+
throw new Error(`Legacy apply journal install manifest graph-lock digest mismatch at ${item.path}.`);
|
|
8480
|
+
}
|
|
8478
8481
|
correlated.push({ path: item.path, stateKey: journalStateKey });
|
|
8479
8482
|
}
|
|
8480
8483
|
if (correlated.length > 1) {
|
|
@@ -8496,6 +8499,8 @@ function validateJournalManifestEnvelope(value, options, stateKey, journalPath)
|
|
|
8496
8499
|
}
|
|
8497
8500
|
return manifest;
|
|
8498
8501
|
}
|
|
8502
|
+
var LegacyContributionScopeMismatchError = class extends Error {
|
|
8503
|
+
};
|
|
8499
8504
|
async function resolvePriorTargetState(options) {
|
|
8500
8505
|
const candidates = await discoverLegacyStateCandidates({
|
|
8501
8506
|
...options,
|
|
@@ -8517,7 +8522,7 @@ async function resolvePriorTargetState(options) {
|
|
|
8517
8522
|
if (!options.stableLock && sameKeyCandidates.length === 1) {
|
|
8518
8523
|
const candidate = sameKeyCandidates[0];
|
|
8519
8524
|
return {
|
|
8520
|
-
graphLock: candidate.graphLock,
|
|
8525
|
+
graphLock: candidate.graphLockCorrelated ? candidate.graphLock : void 0,
|
|
8521
8526
|
manifest: options.stableManifest,
|
|
8522
8527
|
migration: migrationFromCandidate(candidate)
|
|
8523
8528
|
};
|
|
@@ -8535,12 +8540,20 @@ async function resolvePriorTargetState(options) {
|
|
|
8535
8540
|
if (correlated.length === 1) {
|
|
8536
8541
|
const candidate = correlated[0];
|
|
8537
8542
|
return {
|
|
8538
|
-
graphLock: candidate.graphLock,
|
|
8543
|
+
graphLock: candidate.graphLockCorrelated ? candidate.graphLock : void 0,
|
|
8539
8544
|
manifest: candidate.manifest,
|
|
8540
8545
|
migration: migrationFromCandidate(candidate)
|
|
8541
8546
|
};
|
|
8542
8547
|
}
|
|
8543
8548
|
if (options.stableLock) return { graphLock: options.stableLock };
|
|
8549
|
+
if (candidates.length > 0 && options.recoverLegacyState) {
|
|
8550
|
+
for (const candidate of candidates) {
|
|
8551
|
+
options.warn?.(
|
|
8552
|
+
`Legacy graph-only target state for ${options.adapter} at ${candidate.graphLockPath} has no correlated install manifest; runtime identity and ownership cannot be proven. Preserving it outside the recovered stable state.`
|
|
8553
|
+
);
|
|
8554
|
+
}
|
|
8555
|
+
return {};
|
|
8556
|
+
}
|
|
8544
8557
|
if (candidates.length > 1) {
|
|
8545
8558
|
throw new Error(`Ambiguous legacy target state for ${options.adapter}: multiple graph-only candidates were found.`);
|
|
8546
8559
|
}
|
|
@@ -8602,7 +8615,7 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
8602
8615
|
if (sourceLock) {
|
|
8603
8616
|
throw new Error(`Legacy source lock for ${graphLockPath} has no correlated install manifest.`);
|
|
8604
8617
|
}
|
|
8605
|
-
candidates.push({ stateKey: candidateStateKey, graphLockPath, graphLockDigest, graphLock });
|
|
8618
|
+
candidates.push({ stateKey: candidateStateKey, graphLockPath, graphLockDigest, graphLock, graphLockCorrelated: false });
|
|
8606
8619
|
continue;
|
|
8607
8620
|
}
|
|
8608
8621
|
const validated = validateLegacyManifestCandidate(manifest, {
|
|
@@ -8611,16 +8624,26 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
8611
8624
|
graphLockDigest,
|
|
8612
8625
|
graphLockPath
|
|
8613
8626
|
});
|
|
8627
|
+
if (!validated.graphLockCorrelated) {
|
|
8628
|
+
options.warn?.(
|
|
8629
|
+
`Legacy graph lock at ${graphLockPath} advanced beyond its installed manifest; using the manifest as prior runtime state and resolving the dependency graph fresh.`
|
|
8630
|
+
);
|
|
8631
|
+
}
|
|
8614
8632
|
candidates.push({
|
|
8615
8633
|
stateKey: candidateStateKey,
|
|
8616
8634
|
graphLockPath,
|
|
8617
8635
|
graphLockDigest,
|
|
8618
8636
|
graphLock,
|
|
8619
|
-
|
|
8637
|
+
graphLockCorrelated: validated.graphLockCorrelated,
|
|
8638
|
+
manifest: validated.manifest,
|
|
8620
8639
|
sourceLock
|
|
8621
8640
|
});
|
|
8622
8641
|
} catch (error) {
|
|
8623
8642
|
const message = `Legacy target state for ${graphLockPath} is invalid: ` + (error instanceof Error ? error.message : String(error));
|
|
8643
|
+
if (error instanceof LegacyContributionScopeMismatchError) {
|
|
8644
|
+
options.warn?.(`${message} Preserving it as foreign state for ownership validation.`);
|
|
8645
|
+
continue;
|
|
8646
|
+
}
|
|
8624
8647
|
if (!options.recoverLegacyState) throw new Error(message);
|
|
8625
8648
|
options.warn?.(`${message} Preserving it outside the recovered stable state.`);
|
|
8626
8649
|
}
|
|
@@ -8643,14 +8666,17 @@ function validateLegacyManifestCandidate(manifest, options) {
|
|
|
8643
8666
|
if (normalizeInstallRoot2(manifest.targetRoot, options.transport.kind) !== normalizeInstallRoot2(options.installRoot, options.transport.kind)) {
|
|
8644
8667
|
throw new Error(`Legacy target state target root mismatch at ${options.graphLockPath}.`);
|
|
8645
8668
|
}
|
|
8646
|
-
const
|
|
8647
|
-
if (
|
|
8648
|
-
throw new
|
|
8669
|
+
const ownedEntries = manifest.entries.filter((entry) => entry.workspaceOwner === options.workspaceOwner);
|
|
8670
|
+
if (ownedEntries.length === 0) {
|
|
8671
|
+
throw new LegacyContributionScopeMismatchError(
|
|
8672
|
+
`Legacy target state contribution scope does not match ${options.graphLockPath}.`
|
|
8673
|
+
);
|
|
8649
8674
|
}
|
|
8650
|
-
|
|
8651
|
-
|
|
8675
|
+
const manifestDigests = new Set(ownedEntries.map((entry) => entry.graphLockDigest));
|
|
8676
|
+
if (manifestDigests.size !== 1) {
|
|
8677
|
+
throw new Error(`Legacy install manifest has inconsistent graph-lock digests at ${options.graphLockPath}.`);
|
|
8652
8678
|
}
|
|
8653
|
-
return manifest;
|
|
8679
|
+
return { manifest, graphLockCorrelated: manifestDigests.has(options.graphLockDigest) };
|
|
8654
8680
|
}
|
|
8655
8681
|
function migrationFromCandidate(candidate) {
|
|
8656
8682
|
return {
|
|
@@ -11899,6 +11925,13 @@ async function planFleetNormalization(request) {
|
|
|
11899
11925
|
const sourcePackage = sourceByName.get(name);
|
|
11900
11926
|
const destinationPackage = destinationByName.get(name);
|
|
11901
11927
|
if (!sourcePackage || !destinationPackage) {
|
|
11928
|
+
if (legacySelfNormalization && normalizedRequest.orphanedOwnerRoots?.length && normalizedRequest.packages?.includes(name)) {
|
|
11929
|
+
packages.push({
|
|
11930
|
+
name,
|
|
11931
|
+
declarationDigest: sha2565(canonicalJson2({ name, evidence: "orphaned-installed-state" }))
|
|
11932
|
+
});
|
|
11933
|
+
continue;
|
|
11934
|
+
}
|
|
11902
11935
|
throw new Error(`Package '${name}' must be an existing duplicate in both source and destination fleets.`);
|
|
11903
11936
|
}
|
|
11904
11937
|
const sourceDeclaration = canonicalJson2(sourcePackage);
|
|
@@ -12368,11 +12401,12 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12368
12401
|
const legacyOwner = workspaceOwnerForRoot(fleet.root);
|
|
12369
12402
|
const fleetOwner = workspaceOwnerForRoot(fleet.root, fleet.fleetId);
|
|
12370
12403
|
const orphanedOwners = await orphanedWorkspaceOwners(orphanedOwnerRoots);
|
|
12404
|
+
const allowOrphanedRetiredPackages = orphanedOwners.size > 0;
|
|
12371
12405
|
const knownLegacyOwners = /* @__PURE__ */ new Set([legacyOwner, ...orphanedOwners]);
|
|
12372
12406
|
const transferableOwners = orphanedOwners.size > 0 ? orphanedOwners : /* @__PURE__ */ new Set([legacyOwner]);
|
|
12373
12407
|
let normalizedGraphCount = 0;
|
|
12374
12408
|
for (const graph of graphCandidates) {
|
|
12375
|
-
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId);
|
|
12409
|
+
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages);
|
|
12376
12410
|
if (resolve25(destinationState.graphLockPath) === resolve25(graph.path)) {
|
|
12377
12411
|
const destinationManifest = (await collectManifestPaths([destinationState.manifestPath]))[0];
|
|
12378
12412
|
if (destinationManifest?.manifest.entries.some((entry) => entry.workspaceOwner === fleetOwner && entryMatchesPackages(entry, selected))) {
|
|
@@ -12387,7 +12421,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12387
12421
|
continue;
|
|
12388
12422
|
}
|
|
12389
12423
|
}
|
|
12390
|
-
const legacyState = await legacyTargetStateForGraph(fleet, graph);
|
|
12424
|
+
const legacyState = await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages);
|
|
12391
12425
|
if (resolve25(legacyState.graphLockPath) !== resolve25(graph.path)) {
|
|
12392
12426
|
throw new Error(`Legacy graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12393
12427
|
}
|
|
@@ -12402,8 +12436,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12402
12436
|
}
|
|
12403
12437
|
const manifestPaths = /* @__PURE__ */ new Set();
|
|
12404
12438
|
for (const graph of graphs) {
|
|
12405
|
-
manifestPaths.add((await legacyTargetStateForGraph(fleet, graph)).manifestPath);
|
|
12406
|
-
manifestPaths.add((await targetStateForGraph(fleet, graph, fleet.fleetId)).manifestPath);
|
|
12439
|
+
manifestPaths.add((await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages)).manifestPath);
|
|
12440
|
+
manifestPaths.add((await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages)).manifestPath);
|
|
12407
12441
|
}
|
|
12408
12442
|
const manifests = await collectManifestPaths([...manifestPaths]);
|
|
12409
12443
|
const relevantEntries = manifests.flatMap((manifest) => manifest.manifest.entries.filter((entry) => graphs.some((graph) => entryMatchesGraphPackages(entry, graph.lock, selected)) && entryMatchesArtifacts(entry, selectedArtifacts)).map((entry) => ({ manifest, entry, renderedPath: renderedEntryPath(manifest.manifest, entry) })));
|
|
@@ -12440,8 +12474,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12440
12474
|
const renderedPaths = /* @__PURE__ */ new Set();
|
|
12441
12475
|
const orphanedUnmanagedPaths = /* @__PURE__ */ new Set();
|
|
12442
12476
|
for (const graph of graphs) {
|
|
12443
|
-
const legacyState = await legacyTargetStateForGraph(fleet, graph);
|
|
12444
|
-
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId);
|
|
12477
|
+
const legacyState = await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages);
|
|
12478
|
+
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages);
|
|
12445
12479
|
if (graph.path === destinationState.graphLockPath) {
|
|
12446
12480
|
throw new Error(`Fleet '${fleet.fleetId}' graph state is already normalized to fleet-qualified identity.`);
|
|
12447
12481
|
}
|
|
@@ -12725,7 +12759,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
|
|
|
12725
12759
|
});
|
|
12726
12760
|
return installManifestPath(installRoot, adapter.name, { installationType: agent.installationType, stateKey });
|
|
12727
12761
|
}
|
|
12728
|
-
async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null) {
|
|
12762
|
+
async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null, allowMissingNamedAgentPackages = false) {
|
|
12729
12763
|
const agent = scope.config.agents[graph.targetKey];
|
|
12730
12764
|
if (agent?.transport === "ssh") {
|
|
12731
12765
|
throw new Error(`Installed-state normalization cannot hand off SSH target '${graph.targetKey}' locally.`);
|
|
@@ -12734,10 +12768,10 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12734
12768
|
throw new Error(`Graph adapter '${graph.adapter}' does not match configured target adapter '${agent.adapter}'.`);
|
|
12735
12769
|
}
|
|
12736
12770
|
const graphPackages = graph.lock.canonical.roots.map((root) => scope.config.packages.find((pkg) => pkg.name === root.rootId));
|
|
12737
|
-
if (graphPackages.some((pkg) => !pkg)) {
|
|
12771
|
+
if (graphPackages.some((pkg) => !pkg) && !(allowMissingNamedAgentPackages && agent && agent.installationType)) {
|
|
12738
12772
|
throw new Error(`Graph target '${graph.targetKey}' contains a package absent from the fleet configuration.`);
|
|
12739
12773
|
}
|
|
12740
|
-
const packages = graphPackages;
|
|
12774
|
+
const packages = graphPackages.filter((pkg) => pkg !== void 0);
|
|
12741
12775
|
if (!agent) {
|
|
12742
12776
|
if (graph.targetKey !== graph.adapter) {
|
|
12743
12777
|
throw new Error(
|
|
@@ -12752,18 +12786,18 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12752
12786
|
throw new Error(`Graph adapter '${graph.adapter}' does not match direct package adapter '${packageAdapter}'.`);
|
|
12753
12787
|
}
|
|
12754
12788
|
}
|
|
12755
|
-
const installationType = oneEffectiveValue(
|
|
12789
|
+
const installationType = packages.length > 0 ? oneEffectiveValue(
|
|
12756
12790
|
packages.map((pkg) => pkg.installationType ?? agent?.installationType ?? "local"),
|
|
12757
12791
|
`installation type for target '${graph.targetKey}'`
|
|
12758
|
-
);
|
|
12759
|
-
const adapterConfig = oneEffectiveOptionalValue(
|
|
12792
|
+
) : agent.installationType;
|
|
12793
|
+
const adapterConfig = packages.length > 0 ? oneEffectiveOptionalValue(
|
|
12760
12794
|
packages.map((pkg) => agent?.adapterConfig ?? pkg.adapterConfig),
|
|
12761
12795
|
`adapter config for target '${graph.targetKey}'`
|
|
12762
|
-
);
|
|
12763
|
-
const adapterModule = oneEffectiveOptionalValue(
|
|
12796
|
+
) : agent?.adapterConfig;
|
|
12797
|
+
const adapterModule = packages.length > 0 ? oneEffectiveOptionalValue(
|
|
12764
12798
|
packages.map((pkg) => agent?.adapterModule ?? pkg.adapterModule),
|
|
12765
12799
|
`adapter module for target '${graph.targetKey}'`
|
|
12766
|
-
);
|
|
12800
|
+
) : agent?.adapterModule;
|
|
12767
12801
|
const adapter = await resolveAdapter({
|
|
12768
12802
|
adapter: agent?.adapter ?? graph.adapter,
|
|
12769
12803
|
adapterConfig,
|
|
@@ -12833,8 +12867,8 @@ async function installedTargetStateForGraph(scope, graph, manifests, owners, sel
|
|
|
12833
12867
|
assertCorrelatedGraphManifest(graph, state, manifest, owners, selected);
|
|
12834
12868
|
return state;
|
|
12835
12869
|
}
|
|
12836
|
-
async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null) {
|
|
12837
|
-
const current = await targetStateForGraph(scope, graph, identityFleetId);
|
|
12870
|
+
async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null, allowMissingNamedAgentPackages = false) {
|
|
12871
|
+
const current = await targetStateForGraph(scope, graph, identityFleetId, allowMissingNamedAgentPackages);
|
|
12838
12872
|
const targetFingerprint = graph.lock.canonical.targetFingerprint;
|
|
12839
12873
|
if (!targetFingerprint || !/^[a-f0-9]{64}$/u.test(targetFingerprint)) {
|
|
12840
12874
|
throw new Error(`Released graph lock has an invalid target fingerprint: ${graph.path}`);
|
|
@@ -12879,8 +12913,8 @@ function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected)
|
|
|
12879
12913
|
}
|
|
12880
12914
|
}
|
|
12881
12915
|
}
|
|
12882
|
-
async function legacyTargetStateForGraph(scope, graph) {
|
|
12883
|
-
return releasedTargetStateForGraph(scope, graph, null);
|
|
12916
|
+
async function legacyTargetStateForGraph(scope, graph, allowMissingNamedAgentPackages = false) {
|
|
12917
|
+
return releasedTargetStateForGraph(scope, graph, null, allowMissingNamedAgentPackages);
|
|
12884
12918
|
}
|
|
12885
12919
|
function assertGraphIsSubset(source, destination) {
|
|
12886
12920
|
const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId, canonicalJson2(root)]));
|
|
@@ -13992,7 +14026,7 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
|
|
|
13992
14026
|
await rm10(stage, { recursive: true, force: true });
|
|
13993
14027
|
}
|
|
13994
14028
|
});
|
|
13995
|
-
ownershipCommand.command("retire-stale").description("retire stale source-manifest ownership already covered by one Fleet manifest").requiredOption("--from-workspace-root <path>", "exact stale workspace owner root").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").requiredOption("--fleet <id>", "registered destination Fleet").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named destination Fleet agent").option("--profile <name>", "destination Fleet profile resolving to one target").option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--abandon-incomplete-merge-owner", "explicitly retire incomplete legacy merge metadata without changing runtime bytes", false).option("--apply", "apply the exact reviewed metadata-only retirement", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
|
|
14029
|
+
ownershipCommand.command("retire-stale").description("retire stale source-manifest ownership already covered by one Fleet manifest").requiredOption("--from-workspace-root <path>", "exact stale workspace owner root").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").option("--destination-state-key <key>", "exact Fleet-owned destination install-manifest state key (defaults to the stable target state)").requiredOption("--fleet <id>", "registered destination Fleet").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named destination Fleet agent").option("--profile <name>", "destination Fleet profile resolving to one target").option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--abandon-incomplete-merge-owner", "explicitly retire incomplete legacy merge metadata without changing runtime bytes", false).option("--apply", "apply the exact reviewed metadata-only retirement", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
|
|
13996
14030
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
13997
14031
|
const fleet = await showRegisteredFleet(options.fleet);
|
|
13998
14032
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
@@ -14007,19 +14041,16 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
14007
14041
|
if (target.fleetId !== options.fleet || resolve26(target.workspaceRoot) !== resolve26(fleet.root)) {
|
|
14008
14042
|
throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
|
|
14009
14043
|
}
|
|
14010
|
-
const
|
|
14011
|
-
const
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
"Migrate the target state before ownership retirement by running a full agentwheel install for this target."
|
|
14015
|
-
);
|
|
14016
|
-
}
|
|
14044
|
+
const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
|
|
14045
|
+
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
14046
|
+
const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter, void 0);
|
|
14047
|
+
const stableState = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
14017
14048
|
const request = {
|
|
14018
|
-
targetRoot:
|
|
14049
|
+
targetRoot: stableState.installRoot,
|
|
14019
14050
|
adapter: adapter.name,
|
|
14020
14051
|
installationType,
|
|
14021
14052
|
sourceStateKey: options.sourceStateKey,
|
|
14022
|
-
destinationStateKey:
|
|
14053
|
+
destinationStateKey: options.destinationStateKey ?? stableState.stateKey,
|
|
14023
14054
|
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
14024
14055
|
toWorkspaceRoot,
|
|
14025
14056
|
toFleetId: fleet.id,
|
|
@@ -14042,6 +14073,8 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
14042
14073
|
request.toWorkspaceRoot,
|
|
14043
14074
|
"--source-state-key",
|
|
14044
14075
|
request.sourceStateKey,
|
|
14076
|
+
"--destination-state-key",
|
|
14077
|
+
request.destinationStateKey,
|
|
14045
14078
|
"--fleet",
|
|
14046
14079
|
request.toFleetId,
|
|
14047
14080
|
...targetArgs,
|
package/openpack.json
CHANGED
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ allowed-tools: [Bash]
|
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
author: NestDevLab
|
|
8
|
-
version: "0.20.
|
|
8
|
+
version: "0.20.13"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# agentwheel
|
|
@@ -632,3 +632,10 @@ agentwheel plan --fleet <fleet-id> --agent <agent> \
|
|
|
632
632
|
planning base. `--force-conflict` adopts exact existing content; `--replace-conflict` permits the
|
|
633
633
|
reviewed plan to update differing unmanaged content. Apply only the same reviewed target and flags.
|
|
634
634
|
After the first stable install, rerun the ordinary plan without recovery flags and require a no-op.
|
|
635
|
+
|
|
636
|
+
An advanced tracked graph lock with an otherwise valid owned v2 manifest is an ordinary migration:
|
|
637
|
+
Agentwheel keeps the manifest as prior runtime state, resolves the graph fresh, and does not trust the
|
|
638
|
+
advanced lock as installed history. If stale foreign ownership blocks that migration, use
|
|
639
|
+
`agentwheel ownership retire-stale --destination-state-key <exact-fleet-state-key>` to produce a
|
|
640
|
+
metadata-only, revision-gated retirement plan against the already-installed Fleet manifest. Do not
|
|
641
|
+
guess the state key or apply without the exact command emitted by the dry-run.
|