agentwheel 0.20.12 → 0.20.14
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 +75 -42
- 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,
|
|
@@ -8503,21 +8508,29 @@ async function resolvePriorTargetState(options) {
|
|
|
8503
8508
|
});
|
|
8504
8509
|
const correlated = candidates.filter((candidate) => candidate.manifest);
|
|
8505
8510
|
if (options.stableManifest) {
|
|
8511
|
+
const compatible = [];
|
|
8506
8512
|
for (const candidate of correlated) {
|
|
8507
|
-
if (
|
|
8513
|
+
if (sameManifestContributions(options.stableManifest, candidate.manifest)) {
|
|
8514
|
+
compatible.push(candidate);
|
|
8515
|
+
continue;
|
|
8516
|
+
}
|
|
8517
|
+
if (!options.recoverLegacyState) {
|
|
8508
8518
|
throw new Error(
|
|
8509
8519
|
`Stable and legacy target states coexist with disagreeing contributions for ${options.adapter}; reconcile them explicitly.`
|
|
8510
8520
|
);
|
|
8511
8521
|
}
|
|
8522
|
+
options.warn?.(
|
|
8523
|
+
`Legacy target state for ${options.adapter} at ${candidate.graphLockPath} disagrees with the stable manifest. Preserving it outside the explicitly recovered stable state.`
|
|
8524
|
+
);
|
|
8512
8525
|
}
|
|
8513
|
-
const sameKeyCandidates =
|
|
8526
|
+
const sameKeyCandidates = compatible.filter((candidate) => candidate.stateKey === options.stateKey);
|
|
8514
8527
|
if (!options.stableLock && sameKeyCandidates.length > 1) {
|
|
8515
8528
|
throw new Error(`Ambiguous legacy target state for ${options.adapter}: multiple correlated graph locks use the explicit state key.`);
|
|
8516
8529
|
}
|
|
8517
8530
|
if (!options.stableLock && sameKeyCandidates.length === 1) {
|
|
8518
8531
|
const candidate = sameKeyCandidates[0];
|
|
8519
8532
|
return {
|
|
8520
|
-
graphLock: candidate.graphLock,
|
|
8533
|
+
graphLock: candidate.graphLockCorrelated ? candidate.graphLock : void 0,
|
|
8521
8534
|
manifest: options.stableManifest,
|
|
8522
8535
|
migration: migrationFromCandidate(candidate)
|
|
8523
8536
|
};
|
|
@@ -8535,7 +8548,7 @@ async function resolvePriorTargetState(options) {
|
|
|
8535
8548
|
if (correlated.length === 1) {
|
|
8536
8549
|
const candidate = correlated[0];
|
|
8537
8550
|
return {
|
|
8538
|
-
graphLock: candidate.graphLock,
|
|
8551
|
+
graphLock: candidate.graphLockCorrelated ? candidate.graphLock : void 0,
|
|
8539
8552
|
manifest: candidate.manifest,
|
|
8540
8553
|
migration: migrationFromCandidate(candidate)
|
|
8541
8554
|
};
|
|
@@ -8610,7 +8623,7 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
8610
8623
|
if (sourceLock) {
|
|
8611
8624
|
throw new Error(`Legacy source lock for ${graphLockPath} has no correlated install manifest.`);
|
|
8612
8625
|
}
|
|
8613
|
-
candidates.push({ stateKey: candidateStateKey, graphLockPath, graphLockDigest, graphLock });
|
|
8626
|
+
candidates.push({ stateKey: candidateStateKey, graphLockPath, graphLockDigest, graphLock, graphLockCorrelated: false });
|
|
8614
8627
|
continue;
|
|
8615
8628
|
}
|
|
8616
8629
|
const validated = validateLegacyManifestCandidate(manifest, {
|
|
@@ -8619,16 +8632,26 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
8619
8632
|
graphLockDigest,
|
|
8620
8633
|
graphLockPath
|
|
8621
8634
|
});
|
|
8635
|
+
if (!validated.graphLockCorrelated) {
|
|
8636
|
+
options.warn?.(
|
|
8637
|
+
`Legacy graph lock at ${graphLockPath} advanced beyond its installed manifest; using the manifest as prior runtime state and resolving the dependency graph fresh.`
|
|
8638
|
+
);
|
|
8639
|
+
}
|
|
8622
8640
|
candidates.push({
|
|
8623
8641
|
stateKey: candidateStateKey,
|
|
8624
8642
|
graphLockPath,
|
|
8625
8643
|
graphLockDigest,
|
|
8626
8644
|
graphLock,
|
|
8627
|
-
|
|
8645
|
+
graphLockCorrelated: validated.graphLockCorrelated,
|
|
8646
|
+
manifest: validated.manifest,
|
|
8628
8647
|
sourceLock
|
|
8629
8648
|
});
|
|
8630
8649
|
} catch (error) {
|
|
8631
8650
|
const message = `Legacy target state for ${graphLockPath} is invalid: ` + (error instanceof Error ? error.message : String(error));
|
|
8651
|
+
if (error instanceof LegacyContributionScopeMismatchError) {
|
|
8652
|
+
options.warn?.(`${message} Preserving it as foreign state for ownership validation.`);
|
|
8653
|
+
continue;
|
|
8654
|
+
}
|
|
8632
8655
|
if (!options.recoverLegacyState) throw new Error(message);
|
|
8633
8656
|
options.warn?.(`${message} Preserving it outside the recovered stable state.`);
|
|
8634
8657
|
}
|
|
@@ -8651,14 +8674,17 @@ function validateLegacyManifestCandidate(manifest, options) {
|
|
|
8651
8674
|
if (normalizeInstallRoot2(manifest.targetRoot, options.transport.kind) !== normalizeInstallRoot2(options.installRoot, options.transport.kind)) {
|
|
8652
8675
|
throw new Error(`Legacy target state target root mismatch at ${options.graphLockPath}.`);
|
|
8653
8676
|
}
|
|
8654
|
-
const
|
|
8655
|
-
if (
|
|
8656
|
-
throw new
|
|
8677
|
+
const ownedEntries = manifest.entries.filter((entry) => entry.workspaceOwner === options.workspaceOwner);
|
|
8678
|
+
if (ownedEntries.length === 0) {
|
|
8679
|
+
throw new LegacyContributionScopeMismatchError(
|
|
8680
|
+
`Legacy target state contribution scope does not match ${options.graphLockPath}.`
|
|
8681
|
+
);
|
|
8657
8682
|
}
|
|
8658
|
-
|
|
8659
|
-
|
|
8683
|
+
const manifestDigests = new Set(ownedEntries.map((entry) => entry.graphLockDigest));
|
|
8684
|
+
if (manifestDigests.size !== 1) {
|
|
8685
|
+
throw new Error(`Legacy install manifest has inconsistent graph-lock digests at ${options.graphLockPath}.`);
|
|
8660
8686
|
}
|
|
8661
|
-
return manifest;
|
|
8687
|
+
return { manifest, graphLockCorrelated: manifestDigests.has(options.graphLockDigest) };
|
|
8662
8688
|
}
|
|
8663
8689
|
function migrationFromCandidate(candidate) {
|
|
8664
8690
|
return {
|
|
@@ -11907,6 +11933,13 @@ async function planFleetNormalization(request) {
|
|
|
11907
11933
|
const sourcePackage = sourceByName.get(name);
|
|
11908
11934
|
const destinationPackage = destinationByName.get(name);
|
|
11909
11935
|
if (!sourcePackage || !destinationPackage) {
|
|
11936
|
+
if (legacySelfNormalization && normalizedRequest.orphanedOwnerRoots?.length && normalizedRequest.packages?.includes(name)) {
|
|
11937
|
+
packages.push({
|
|
11938
|
+
name,
|
|
11939
|
+
declarationDigest: sha2565(canonicalJson2({ name, evidence: "orphaned-installed-state" }))
|
|
11940
|
+
});
|
|
11941
|
+
continue;
|
|
11942
|
+
}
|
|
11910
11943
|
throw new Error(`Package '${name}' must be an existing duplicate in both source and destination fleets.`);
|
|
11911
11944
|
}
|
|
11912
11945
|
const sourceDeclaration = canonicalJson2(sourcePackage);
|
|
@@ -12376,11 +12409,12 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12376
12409
|
const legacyOwner = workspaceOwnerForRoot(fleet.root);
|
|
12377
12410
|
const fleetOwner = workspaceOwnerForRoot(fleet.root, fleet.fleetId);
|
|
12378
12411
|
const orphanedOwners = await orphanedWorkspaceOwners(orphanedOwnerRoots);
|
|
12412
|
+
const allowOrphanedRetiredPackages = orphanedOwners.size > 0;
|
|
12379
12413
|
const knownLegacyOwners = /* @__PURE__ */ new Set([legacyOwner, ...orphanedOwners]);
|
|
12380
12414
|
const transferableOwners = orphanedOwners.size > 0 ? orphanedOwners : /* @__PURE__ */ new Set([legacyOwner]);
|
|
12381
12415
|
let normalizedGraphCount = 0;
|
|
12382
12416
|
for (const graph of graphCandidates) {
|
|
12383
|
-
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId);
|
|
12417
|
+
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages);
|
|
12384
12418
|
if (resolve25(destinationState.graphLockPath) === resolve25(graph.path)) {
|
|
12385
12419
|
const destinationManifest = (await collectManifestPaths([destinationState.manifestPath]))[0];
|
|
12386
12420
|
if (destinationManifest?.manifest.entries.some((entry) => entry.workspaceOwner === fleetOwner && entryMatchesPackages(entry, selected))) {
|
|
@@ -12395,7 +12429,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12395
12429
|
continue;
|
|
12396
12430
|
}
|
|
12397
12431
|
}
|
|
12398
|
-
const legacyState = await legacyTargetStateForGraph(fleet, graph);
|
|
12432
|
+
const legacyState = await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages);
|
|
12399
12433
|
if (resolve25(legacyState.graphLockPath) !== resolve25(graph.path)) {
|
|
12400
12434
|
throw new Error(`Legacy graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12401
12435
|
}
|
|
@@ -12410,8 +12444,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12410
12444
|
}
|
|
12411
12445
|
const manifestPaths = /* @__PURE__ */ new Set();
|
|
12412
12446
|
for (const graph of graphs) {
|
|
12413
|
-
manifestPaths.add((await legacyTargetStateForGraph(fleet, graph)).manifestPath);
|
|
12414
|
-
manifestPaths.add((await targetStateForGraph(fleet, graph, fleet.fleetId)).manifestPath);
|
|
12447
|
+
manifestPaths.add((await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages)).manifestPath);
|
|
12448
|
+
manifestPaths.add((await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages)).manifestPath);
|
|
12415
12449
|
}
|
|
12416
12450
|
const manifests = await collectManifestPaths([...manifestPaths]);
|
|
12417
12451
|
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) })));
|
|
@@ -12448,8 +12482,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12448
12482
|
const renderedPaths = /* @__PURE__ */ new Set();
|
|
12449
12483
|
const orphanedUnmanagedPaths = /* @__PURE__ */ new Set();
|
|
12450
12484
|
for (const graph of graphs) {
|
|
12451
|
-
const legacyState = await legacyTargetStateForGraph(fleet, graph);
|
|
12452
|
-
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId);
|
|
12485
|
+
const legacyState = await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages);
|
|
12486
|
+
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages);
|
|
12453
12487
|
if (graph.path === destinationState.graphLockPath) {
|
|
12454
12488
|
throw new Error(`Fleet '${fleet.fleetId}' graph state is already normalized to fleet-qualified identity.`);
|
|
12455
12489
|
}
|
|
@@ -12733,7 +12767,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
|
|
|
12733
12767
|
});
|
|
12734
12768
|
return installManifestPath(installRoot, adapter.name, { installationType: agent.installationType, stateKey });
|
|
12735
12769
|
}
|
|
12736
|
-
async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null) {
|
|
12770
|
+
async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null, allowMissingNamedAgentPackages = false) {
|
|
12737
12771
|
const agent = scope.config.agents[graph.targetKey];
|
|
12738
12772
|
if (agent?.transport === "ssh") {
|
|
12739
12773
|
throw new Error(`Installed-state normalization cannot hand off SSH target '${graph.targetKey}' locally.`);
|
|
@@ -12742,10 +12776,10 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12742
12776
|
throw new Error(`Graph adapter '${graph.adapter}' does not match configured target adapter '${agent.adapter}'.`);
|
|
12743
12777
|
}
|
|
12744
12778
|
const graphPackages = graph.lock.canonical.roots.map((root) => scope.config.packages.find((pkg) => pkg.name === root.rootId));
|
|
12745
|
-
if (graphPackages.some((pkg) => !pkg)) {
|
|
12779
|
+
if (graphPackages.some((pkg) => !pkg) && !(allowMissingNamedAgentPackages && agent && agent.installationType)) {
|
|
12746
12780
|
throw new Error(`Graph target '${graph.targetKey}' contains a package absent from the fleet configuration.`);
|
|
12747
12781
|
}
|
|
12748
|
-
const packages = graphPackages;
|
|
12782
|
+
const packages = graphPackages.filter((pkg) => pkg !== void 0);
|
|
12749
12783
|
if (!agent) {
|
|
12750
12784
|
if (graph.targetKey !== graph.adapter) {
|
|
12751
12785
|
throw new Error(
|
|
@@ -12760,18 +12794,18 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12760
12794
|
throw new Error(`Graph adapter '${graph.adapter}' does not match direct package adapter '${packageAdapter}'.`);
|
|
12761
12795
|
}
|
|
12762
12796
|
}
|
|
12763
|
-
const installationType = oneEffectiveValue(
|
|
12797
|
+
const installationType = packages.length > 0 ? oneEffectiveValue(
|
|
12764
12798
|
packages.map((pkg) => pkg.installationType ?? agent?.installationType ?? "local"),
|
|
12765
12799
|
`installation type for target '${graph.targetKey}'`
|
|
12766
|
-
);
|
|
12767
|
-
const adapterConfig = oneEffectiveOptionalValue(
|
|
12800
|
+
) : agent.installationType;
|
|
12801
|
+
const adapterConfig = packages.length > 0 ? oneEffectiveOptionalValue(
|
|
12768
12802
|
packages.map((pkg) => agent?.adapterConfig ?? pkg.adapterConfig),
|
|
12769
12803
|
`adapter config for target '${graph.targetKey}'`
|
|
12770
|
-
);
|
|
12771
|
-
const adapterModule = oneEffectiveOptionalValue(
|
|
12804
|
+
) : agent?.adapterConfig;
|
|
12805
|
+
const adapterModule = packages.length > 0 ? oneEffectiveOptionalValue(
|
|
12772
12806
|
packages.map((pkg) => agent?.adapterModule ?? pkg.adapterModule),
|
|
12773
12807
|
`adapter module for target '${graph.targetKey}'`
|
|
12774
|
-
);
|
|
12808
|
+
) : agent?.adapterModule;
|
|
12775
12809
|
const adapter = await resolveAdapter({
|
|
12776
12810
|
adapter: agent?.adapter ?? graph.adapter,
|
|
12777
12811
|
adapterConfig,
|
|
@@ -12841,8 +12875,8 @@ async function installedTargetStateForGraph(scope, graph, manifests, owners, sel
|
|
|
12841
12875
|
assertCorrelatedGraphManifest(graph, state, manifest, owners, selected);
|
|
12842
12876
|
return state;
|
|
12843
12877
|
}
|
|
12844
|
-
async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null) {
|
|
12845
|
-
const current = await targetStateForGraph(scope, graph, identityFleetId);
|
|
12878
|
+
async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope.fleetId ?? null, allowMissingNamedAgentPackages = false) {
|
|
12879
|
+
const current = await targetStateForGraph(scope, graph, identityFleetId, allowMissingNamedAgentPackages);
|
|
12846
12880
|
const targetFingerprint = graph.lock.canonical.targetFingerprint;
|
|
12847
12881
|
if (!targetFingerprint || !/^[a-f0-9]{64}$/u.test(targetFingerprint)) {
|
|
12848
12882
|
throw new Error(`Released graph lock has an invalid target fingerprint: ${graph.path}`);
|
|
@@ -12887,8 +12921,8 @@ function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected)
|
|
|
12887
12921
|
}
|
|
12888
12922
|
}
|
|
12889
12923
|
}
|
|
12890
|
-
async function legacyTargetStateForGraph(scope, graph) {
|
|
12891
|
-
return releasedTargetStateForGraph(scope, graph, null);
|
|
12924
|
+
async function legacyTargetStateForGraph(scope, graph, allowMissingNamedAgentPackages = false) {
|
|
12925
|
+
return releasedTargetStateForGraph(scope, graph, null, allowMissingNamedAgentPackages);
|
|
12892
12926
|
}
|
|
12893
12927
|
function assertGraphIsSubset(source, destination) {
|
|
12894
12928
|
const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId, canonicalJson2(root)]));
|
|
@@ -14000,7 +14034,7 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
|
|
|
14000
14034
|
await rm10(stage, { recursive: true, force: true });
|
|
14001
14035
|
}
|
|
14002
14036
|
});
|
|
14003
|
-
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) => {
|
|
14037
|
+
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) => {
|
|
14004
14038
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
14005
14039
|
const fleet = await showRegisteredFleet(options.fleet);
|
|
14006
14040
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
@@ -14015,19 +14049,16 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
14015
14049
|
if (target.fleetId !== options.fleet || resolve26(target.workspaceRoot) !== resolve26(fleet.root)) {
|
|
14016
14050
|
throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
|
|
14017
14051
|
}
|
|
14018
|
-
const
|
|
14019
|
-
const
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
"Migrate the target state before ownership retirement by running a full agentwheel install for this target."
|
|
14023
|
-
);
|
|
14024
|
-
}
|
|
14052
|
+
const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
|
|
14053
|
+
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
14054
|
+
const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter, void 0);
|
|
14055
|
+
const stableState = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
14025
14056
|
const request = {
|
|
14026
|
-
targetRoot:
|
|
14057
|
+
targetRoot: stableState.installRoot,
|
|
14027
14058
|
adapter: adapter.name,
|
|
14028
14059
|
installationType,
|
|
14029
14060
|
sourceStateKey: options.sourceStateKey,
|
|
14030
|
-
destinationStateKey:
|
|
14061
|
+
destinationStateKey: options.destinationStateKey ?? stableState.stateKey,
|
|
14031
14062
|
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
14032
14063
|
toWorkspaceRoot,
|
|
14033
14064
|
toFleetId: fleet.id,
|
|
@@ -14050,6 +14081,8 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
14050
14081
|
request.toWorkspaceRoot,
|
|
14051
14082
|
"--source-state-key",
|
|
14052
14083
|
request.sourceStateKey,
|
|
14084
|
+
"--destination-state-key",
|
|
14085
|
+
request.destinationStateKey,
|
|
14053
14086
|
"--fleet",
|
|
14054
14087
|
request.toFleetId,
|
|
14055
14088
|
...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.14"
|
|
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.
|