@lesliechan721/aw 0.1.0-beta.15 → 0.1.0-beta.17
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/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +1 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/commands/offline-cleanup-evidence.d.ts +8 -0
- package/dist/src/commands/offline-cleanup-evidence.js +18 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +4 -1
- package/dist/src/commands/rebuild-command.js +658 -172
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/review-inspection.d.ts +169 -0
- package/dist/src/commands/review-inspection.js +916 -0
- package/dist/src/commands/review-inspection.js.map +1 -0
- package/dist/src/commands/skill-eval.d.ts +5 -0
- package/dist/src/commands/skill-eval.js +86 -63
- package/dist/src/commands/skill-eval.js.map +1 -1
- package/dist/src/eval/manifest-v1.js +0 -5
- package/dist/src/eval/manifest-v1.js.map +1 -1
- package/dist/src/index.js +40 -7
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.js +11 -6
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/portable/commands.d.ts +1 -0
- package/dist/src/portable/commands.js +43 -1
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +12 -0
- package/dist/src/portable/materializer.js +67 -41
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/review/eval-source-input.d.ts +43 -0
- package/dist/src/review/eval-source-input.js +207 -0
- package/dist/src/review/eval-source-input.js.map +1 -0
- package/dist/src/review/source-binding.d.ts +170 -0
- package/dist/src/review/source-binding.js +622 -0
- package/dist/src/review/source-binding.js.map +1 -0
- package/dist/src/tui/console-app-v4.js +3 -3
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.js +191 -151
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +1 -0
- package/dist/src/tui/controller.js +10 -7
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/model.d.ts +1 -1
- package/dist/src/tui/model.js +18 -6
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-review-planner.js +103 -26
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import fs from 'fs-extra';
|
|
3
4
|
import { getCodexOperationRequirement, probeAgentVersion } from '../utils/agent-version.js';
|
|
@@ -38,7 +39,7 @@ import { AwCapabilityUnsupportedError, IMPLEMENTED_AW_CAPABILITIES, } from './aw
|
|
|
38
39
|
import { checkAwCapabilitySupport } from '../utils/manifest.js';
|
|
39
40
|
import { createDependencyPlacementPlan, } from './dependency-operation-plan.js';
|
|
40
41
|
import { createReconcileOperationPlan, } from './reconcile-operation-plan.js';
|
|
41
|
-
import { forceOfflineCleanupEvidence, offlineCleanupEvidenceFromInventory, offlineCleanupEvidenceMatches, } from './offline-cleanup-evidence.js';
|
|
42
|
+
import { forceOfflineCleanupEvidence, intentOnlyOfflineCleanupEvidence, offlineCleanupEvidenceFromInventory, offlineCleanupEvidenceMatches, } from './offline-cleanup-evidence.js';
|
|
42
43
|
async function measureCommandPerformanceStage(runtime, name, operation) {
|
|
43
44
|
const startedAt = Date.now();
|
|
44
45
|
let phase = 'success';
|
|
@@ -108,6 +109,37 @@ function omitSelectedPluginConfigErrors(inventory, targets) {
|
|
|
108
109
|
function selectedPlatforms(selection) {
|
|
109
110
|
return selection === 'both' ? ['codex', 'claude'] : [selection];
|
|
110
111
|
}
|
|
112
|
+
async function selectedProjectCodexSkillReferences(options) {
|
|
113
|
+
const skillsRoot = path.join(resolveRepoRootSync(options.cwd), '.agents', 'skills');
|
|
114
|
+
const entries = await fs.readdir(skillsRoot, { withFileTypes: true }).catch((error) => {
|
|
115
|
+
const code = error.code;
|
|
116
|
+
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
117
|
+
return [];
|
|
118
|
+
throw error;
|
|
119
|
+
});
|
|
120
|
+
const references = [];
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
if (!entry.isDirectory() || entry.isSymbolicLink())
|
|
123
|
+
continue;
|
|
124
|
+
const skillRoot = path.join(skillsRoot, entry.name);
|
|
125
|
+
const marker = await readSkillOwnerMarker({ skillRoot, allowedRoot: skillsRoot }).catch(() => null);
|
|
126
|
+
if (!marker?.sourceId
|
|
127
|
+
|| marker.pluginId !== `${marker.sourceId}/${marker.plugin}`
|
|
128
|
+
|| marker.platform !== 'codex'
|
|
129
|
+
|| marker.scope !== 'project')
|
|
130
|
+
continue;
|
|
131
|
+
references.push({
|
|
132
|
+
kind: 'namespacedOwner',
|
|
133
|
+
location: path.join(skillRoot, SKILL_OWNER_MARKER),
|
|
134
|
+
sourceId: marker.sourceId,
|
|
135
|
+
pluginName: marker.plugin,
|
|
136
|
+
platform: marker.platform,
|
|
137
|
+
scope: marker.scope,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return references;
|
|
141
|
+
}
|
|
142
|
+
const cleanupProjectSkillsByInventory = new WeakMap();
|
|
111
143
|
function selectCodexActivationPlans(plans, targetScopes, preservedActivations) {
|
|
112
144
|
return plans.flatMap((plan) => {
|
|
113
145
|
const includesScope = (pluginId, scope) => targetScopes.has(`${pluginId}\0${scope}`);
|
|
@@ -253,10 +285,12 @@ function deriveDesiredIntentState(options) {
|
|
|
253
285
|
next = mergePluginIntentRootPlatforms(next, root);
|
|
254
286
|
next = updateIntentOverrides({ state: next, plan: options.plan });
|
|
255
287
|
}
|
|
256
|
-
else if (options.command === 'uninstall') {
|
|
288
|
+
else if (options.command === 'uninstall' || options.command === 'cleanup') {
|
|
257
289
|
for (const root of options.selectedRoots)
|
|
258
290
|
next = removePluginIntentRootPlatforms(next, root);
|
|
259
|
-
|
|
291
|
+
if (options.command === 'uninstall') {
|
|
292
|
+
next = pruneUnreachableDependencyScopeOverrides(next, options.catalogEntries);
|
|
293
|
+
}
|
|
260
294
|
}
|
|
261
295
|
return next;
|
|
262
296
|
}
|
|
@@ -767,6 +801,199 @@ function projectLegacyTargets(references) {
|
|
|
767
801
|
return [`${target.pluginName}\0${target.platform}\0${target.scope}`, target];
|
|
768
802
|
})).values()];
|
|
769
803
|
}
|
|
804
|
+
function cleanupIntentTargets(options) {
|
|
805
|
+
const selectedPlatforms = new Set(options.platforms);
|
|
806
|
+
const liveByKey = new Map(options.liveTargets.map((target) => [
|
|
807
|
+
`${target.pluginId}\0${target.platform}`,
|
|
808
|
+
target,
|
|
809
|
+
]));
|
|
810
|
+
const selectedIds = options.all
|
|
811
|
+
? new Set(options.state.roots.map((root) => root.pluginId))
|
|
812
|
+
: new Set(options.selectors.flatMap((selector) => {
|
|
813
|
+
const parsed = parsePluginId(selector);
|
|
814
|
+
if (parsed.sourceId)
|
|
815
|
+
return [formatPluginId(parsed)];
|
|
816
|
+
const matches = options.state.roots
|
|
817
|
+
.filter((root) => parsePluginId(root.pluginId).pluginName === parsed.pluginName)
|
|
818
|
+
.map((root) => root.pluginId);
|
|
819
|
+
const liveMatches = options.liveTargets
|
|
820
|
+
.filter((target) => target.pluginName === parsed.pluginName)
|
|
821
|
+
.map((target) => target.pluginId);
|
|
822
|
+
const identities = [...new Set([...matches, ...liveMatches])];
|
|
823
|
+
if (identities.length > 1) {
|
|
824
|
+
throw new Error(`pluginSelectorAmbiguous: Plugin "${parsed.pluginName}" has multiple Intent or live matches: ${identities.sort().join(', ')}.`);
|
|
825
|
+
}
|
|
826
|
+
return identities.length === 1
|
|
827
|
+
? identities
|
|
828
|
+
: options.primarySourceId
|
|
829
|
+
? [formatPluginId({ sourceId: options.primarySourceId, pluginName: parsed.pluginName })]
|
|
830
|
+
: [];
|
|
831
|
+
}));
|
|
832
|
+
return options.state.roots.flatMap((root) => root.platforms.flatMap((platform) => {
|
|
833
|
+
if (!selectedIds.has(root.pluginId) || !selectedPlatforms.has(platform))
|
|
834
|
+
return [];
|
|
835
|
+
const live = liveByKey.get(`${root.pluginId}\0${platform}`);
|
|
836
|
+
if (live)
|
|
837
|
+
return [live];
|
|
838
|
+
const parsed = parsePluginId(root.pluginId);
|
|
839
|
+
return [{
|
|
840
|
+
pluginId: root.pluginId,
|
|
841
|
+
pluginName: parsed.pluginName,
|
|
842
|
+
sourceId: parsed.sourceId,
|
|
843
|
+
sourceIdentity: 'unavailable',
|
|
844
|
+
platform,
|
|
845
|
+
scope: options.scope,
|
|
846
|
+
}];
|
|
847
|
+
}));
|
|
848
|
+
}
|
|
849
|
+
function cleanupSelectedRoots(targets) {
|
|
850
|
+
const platformsByPluginId = new Map();
|
|
851
|
+
for (const target of targets) {
|
|
852
|
+
const platforms = platformsByPluginId.get(target.pluginId) ?? new Set();
|
|
853
|
+
platforms.add(target.platform);
|
|
854
|
+
platformsByPluginId.set(target.pluginId, platforms);
|
|
855
|
+
}
|
|
856
|
+
return [...platformsByPluginId].map(([pluginId, platforms]) => ({
|
|
857
|
+
pluginId,
|
|
858
|
+
platforms: [...platforms],
|
|
859
|
+
}));
|
|
860
|
+
}
|
|
861
|
+
function cleanupIntentResult(target) {
|
|
862
|
+
return {
|
|
863
|
+
status: 'succeeded',
|
|
864
|
+
command: 'cleanup',
|
|
865
|
+
target: {
|
|
866
|
+
pluginId: target.pluginId,
|
|
867
|
+
pluginName: target.pluginName,
|
|
868
|
+
sourceId: target.sourceId,
|
|
869
|
+
sourceIdentity: target.sourceIdentity,
|
|
870
|
+
pluginVersion: '0.0.0',
|
|
871
|
+
platform: target.platform,
|
|
872
|
+
scope: target.scope,
|
|
873
|
+
cwd: '.',
|
|
874
|
+
},
|
|
875
|
+
actions: [],
|
|
876
|
+
cleanupAttempted: false,
|
|
877
|
+
removed: [],
|
|
878
|
+
absent: [],
|
|
879
|
+
residual: [],
|
|
880
|
+
quarantined: [],
|
|
881
|
+
failures: [],
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function cleanupInventoryWarnings(inventory, targets) {
|
|
885
|
+
return inventory.errors.flatMap((error) => {
|
|
886
|
+
const relevance = error.relevance;
|
|
887
|
+
const relevant = relevance.global || targets.some((target) => (relevance.sourceId === undefined || relevance.sourceId === target.sourceId)
|
|
888
|
+
&& (relevance.pluginName === undefined || relevance.pluginName === target.pluginName)
|
|
889
|
+
&& (relevance.platform === undefined || relevance.platform === target.platform)
|
|
890
|
+
&& (relevance.scope === undefined || relevance.scope === target.scope));
|
|
891
|
+
if (!relevant)
|
|
892
|
+
return [];
|
|
893
|
+
const digest = crypto.createHash('sha256').update(JSON.stringify(error)).digest('hex');
|
|
894
|
+
return [`stateInventoryResidual: ${error.kind} (sha256:${digest})`];
|
|
895
|
+
}).sort();
|
|
896
|
+
}
|
|
897
|
+
function cleanupInventoryErrorsForTarget(inventory, target) {
|
|
898
|
+
return inventory.errors.filter((error) => {
|
|
899
|
+
const relevance = error.relevance;
|
|
900
|
+
return relevance.global || ((relevance.sourceId === undefined || relevance.sourceId === target.sourceId)
|
|
901
|
+
&& (relevance.pluginName === undefined || relevance.pluginName === target.pluginName)
|
|
902
|
+
&& (relevance.platform === undefined || relevance.platform === target.platform)
|
|
903
|
+
&& (relevance.scope === undefined || relevance.scope === target.scope));
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
function cleanupInventoryErrorsForSelection(options) {
|
|
907
|
+
const selectors = options.selectors.map(parsePluginId);
|
|
908
|
+
return options.inventory.errors.filter((error) => {
|
|
909
|
+
const relevance = error.relevance;
|
|
910
|
+
if (relevance.global)
|
|
911
|
+
return true;
|
|
912
|
+
if (relevance.scope !== undefined && relevance.scope !== options.scope)
|
|
913
|
+
return false;
|
|
914
|
+
if (relevance.platform !== undefined && !options.platforms.includes(relevance.platform))
|
|
915
|
+
return false;
|
|
916
|
+
if (options.all)
|
|
917
|
+
return true;
|
|
918
|
+
return selectors.some((selector) => (relevance.pluginName === undefined || relevance.pluginName === selector.pluginName)
|
|
919
|
+
&& (selector.sourceId === undefined
|
|
920
|
+
|| relevance.sourceId === undefined
|
|
921
|
+
|| relevance.sourceId === selector.sourceId));
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
function cleanupActionCoversLocation(evidence, location, env, cwd) {
|
|
925
|
+
const rawRepoRoot = resolveRepoRootSync(cwd);
|
|
926
|
+
const sensitiveRoots = [
|
|
927
|
+
canonicalizePathThroughExistingAncestorSync(getCodexHome(env)),
|
|
928
|
+
canonicalizePathThroughExistingAncestorSync(getClaudeHome(env)),
|
|
929
|
+
canonicalizePathThroughExistingAncestorSync(rawRepoRoot),
|
|
930
|
+
getCodexHome(env),
|
|
931
|
+
getClaudeHome(env),
|
|
932
|
+
rawRepoRoot,
|
|
933
|
+
];
|
|
934
|
+
const normalizedLocation = location.replaceAll('\\', '/');
|
|
935
|
+
return evidence?.plan.actions.some((action) => {
|
|
936
|
+
if (action.action !== 'removeOwnedOutput')
|
|
937
|
+
return false;
|
|
938
|
+
const normalizedTarget = sanitizeCatalogSourceErrorMessage(action.target, env, sensitiveRoots).replaceAll('\\', '/');
|
|
939
|
+
const relative = path.posix.relative(normalizedTarget, normalizedLocation);
|
|
940
|
+
return relative === ''
|
|
941
|
+
|| (relative !== '..' && !relative.startsWith('../') && !path.posix.isAbsolute(relative));
|
|
942
|
+
}) ?? false;
|
|
943
|
+
}
|
|
944
|
+
function cleanupLocatorPaths(component) {
|
|
945
|
+
const locator = component.locator;
|
|
946
|
+
switch (locator.kind) {
|
|
947
|
+
case 'claude-permissions': return [locator.settingsPath];
|
|
948
|
+
case 'codex-instruction': return [locator.configPath];
|
|
949
|
+
case 'codex-activation': return [locator.configPath];
|
|
950
|
+
case 'legacy-codex-mcp': return [locator.configPath];
|
|
951
|
+
case 'legacy-codex-hook': return [locator.hooksPath];
|
|
952
|
+
case 'codex-role-registration': return [locator.configPath, locator.rolePath];
|
|
953
|
+
case 'codex-marketplace':
|
|
954
|
+
case 'claude-marketplace': return [locator.marketplaceRoot];
|
|
955
|
+
case 'corrupted': return [];
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
function cleanupPathAuthorized(pathValue, evidence) {
|
|
959
|
+
const normalizedPath = canonicalizePathThroughExistingAncestorSync(pathValue);
|
|
960
|
+
return evidence.plan.actions.some((action) => {
|
|
961
|
+
if (action.action !== 'removeOwnedOutput')
|
|
962
|
+
return false;
|
|
963
|
+
const actionPath = action.target.split('#', 1)[0];
|
|
964
|
+
const normalizedActionPath = canonicalizePathThroughExistingAncestorSync(actionPath);
|
|
965
|
+
const relative = path.relative(normalizedPath, normalizedActionPath);
|
|
966
|
+
return relative === ''
|
|
967
|
+
|| (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
function cleanupComponentAuthorized(component, evidence) {
|
|
971
|
+
if ('path' in component)
|
|
972
|
+
return cleanupPathAuthorized(component.path, evidence);
|
|
973
|
+
if (component.kind === 'native-plugin') {
|
|
974
|
+
return evidence.plan.actions.some((action) => action.action === 'removeNativeRegistration' && action.target === component.pluginId);
|
|
975
|
+
}
|
|
976
|
+
const locator = component.locator;
|
|
977
|
+
if (locator.kind === 'codex-activation') {
|
|
978
|
+
return evidence.plan.actions.some((action) => action.action === 'removeNativeRegistration'
|
|
979
|
+
&& action.target === locator.codexPluginId);
|
|
980
|
+
}
|
|
981
|
+
if (locator.kind === 'codex-marketplace'
|
|
982
|
+
|| locator.kind === 'claude-marketplace') {
|
|
983
|
+
return evidence.plan.actions.some((action) => action.action === 'removeNativeRegistration'
|
|
984
|
+
&& action.marketplaceName === locator.marketplaceName);
|
|
985
|
+
}
|
|
986
|
+
if (evidence.plan.actions.some((action) => action.action === 'removeOwnedOutput' && action.target === component.label))
|
|
987
|
+
return true;
|
|
988
|
+
return cleanupLocatorPaths(component).some((candidate) => cleanupPathAuthorized(candidate, evidence));
|
|
989
|
+
}
|
|
990
|
+
function cleanupComponentResidual(component) {
|
|
991
|
+
if ('path' in component)
|
|
992
|
+
return component.path;
|
|
993
|
+
if (component.kind === 'native-plugin')
|
|
994
|
+
return component.pluginId;
|
|
995
|
+
return component.label;
|
|
996
|
+
}
|
|
770
997
|
function resolveLiveCleanupTargets(options) {
|
|
771
998
|
const visible = options.references.filter((reference) => (reference.kind === 'namespacedOwner'
|
|
772
999
|
|| reference.kind === 'namespacedMarketplace')
|
|
@@ -857,84 +1084,145 @@ async function cleanupOfflineOwnedComponents(options) {
|
|
|
857
1084
|
};
|
|
858
1085
|
const integration = createRebuildIntegration({ context });
|
|
859
1086
|
for (const target of options.targets) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1087
|
+
try {
|
|
1088
|
+
const inventoryErrors = (options.inventoryErrors ?? []).filter((error) => error.relevance.global || ((error.relevance.sourceId === undefined || error.relevance.sourceId === target.sourceId)
|
|
1089
|
+
&& (error.relevance.pluginName === undefined || error.relevance.pluginName === target.pluginName)
|
|
1090
|
+
&& (error.relevance.platform === undefined || error.relevance.platform === target.platform)
|
|
1091
|
+
&& (error.relevance.scope === undefined || error.relevance.scope === target.scope)));
|
|
1092
|
+
const evidence = options.evidence?.find((candidate) => candidate.pluginId === target.pluginId
|
|
1093
|
+
&& candidate.sourceIdentity === target.sourceIdentity
|
|
1094
|
+
&& candidate.platform === target.platform
|
|
1095
|
+
&& candidate.scope === target.scope);
|
|
1096
|
+
const activationAction = target.platform === 'codex'
|
|
1097
|
+
? evidence?.plan.actions.find((action) => action.action === 'removeNativeRegistration'
|
|
1098
|
+
&& action.activationScope === target.scope)
|
|
1099
|
+
: undefined;
|
|
1100
|
+
let activationDisposition = 'remove';
|
|
1101
|
+
if (activationAction && target.scope === 'user') {
|
|
1102
|
+
activationDisposition = 'disable';
|
|
1103
|
+
}
|
|
1104
|
+
const plugin = createSyntheticPlugin(target.pluginName, options.env);
|
|
1105
|
+
plugin.sourceId = target.sourceId;
|
|
1106
|
+
plugin.sourceIdentity = target.sourceIdentity;
|
|
1107
|
+
plugin.pluginId = target.pluginId;
|
|
1108
|
+
const marketplaceName = target.marketplaceName ?? 'aw';
|
|
1109
|
+
plugin.marketplaceName = marketplaceName;
|
|
1110
|
+
const desired = await integration.buildTarget({
|
|
1111
|
+
sourceId: target.sourceId,
|
|
1112
|
+
sourceIdentity: target.sourceIdentity,
|
|
1113
|
+
pluginId: target.pluginId,
|
|
1114
|
+
marketplaceName,
|
|
1115
|
+
marketplaceRoot: path.join(resolveAwHome(options.env), 'marketplaces', target.sourceId),
|
|
1116
|
+
plugin,
|
|
1117
|
+
platform: target.platform,
|
|
1118
|
+
scope: target.scope,
|
|
1119
|
+
cwd: options.cwd,
|
|
1120
|
+
command: 'cleanup',
|
|
1121
|
+
catalog: {
|
|
1122
|
+
rootDir: resolveAwHome(options.env),
|
|
1123
|
+
source: 'local',
|
|
1124
|
+
catalogVersion: '0.0.0',
|
|
1125
|
+
channel: 'beta',
|
|
1126
|
+
},
|
|
1127
|
+
includeNativeCleanupCandidate: target.nativeRegistration === true,
|
|
1128
|
+
preserveCodexSharedNativePayload: target.platform === 'codex',
|
|
1129
|
+
...(activationAction
|
|
1130
|
+
? {
|
|
1131
|
+
codexActivationCleanup: [{
|
|
1132
|
+
codexPluginId: activationAction.target,
|
|
1133
|
+
scope: activationAction.activationScope,
|
|
1134
|
+
disposition: activationDisposition,
|
|
1135
|
+
}],
|
|
1136
|
+
}
|
|
1137
|
+
: {}),
|
|
1138
|
+
});
|
|
1139
|
+
const unplannedCandidates = evidence
|
|
1140
|
+
? desired.ownedComponents.filter((component) => !cleanupComponentAuthorized(component, evidence))
|
|
1141
|
+
: [];
|
|
1142
|
+
const unplannedComponents = unplannedCandidates.length > 0
|
|
1143
|
+
? (await inspectOwnedComponents({
|
|
1144
|
+
components: unplannedCandidates,
|
|
1145
|
+
executor: integration.driver.ownership,
|
|
1146
|
+
})).components.flatMap((entry) => entry.state === 'present' ? [entry.component] : [])
|
|
1147
|
+
: [];
|
|
1148
|
+
if (evidence) {
|
|
1149
|
+
const authorized = (component) => cleanupComponentAuthorized(component, evidence);
|
|
1150
|
+
desired.ownedComponents = desired.ownedComponents.filter(authorized);
|
|
1151
|
+
desired.desiredOwnedComponents = desired.desiredOwnedComponents?.filter(authorized);
|
|
1152
|
+
desired.optionalOwnedComponents = desired.optionalOwnedComponents?.filter(authorized);
|
|
1153
|
+
desired.artifacts = desired.artifacts.filter((artifact) => cleanupPathAuthorized(artifact.path, evidence));
|
|
1154
|
+
const nativeAuthorized = evidence.plan.actions.some((action) => action.action === 'removeNativeRegistration');
|
|
1155
|
+
desired.requiresNative &&= nativeAuthorized;
|
|
1156
|
+
desired.writeNativeRequired &&= nativeAuthorized;
|
|
1157
|
+
desired.operationActions = desired.operationActions?.filter((action) => {
|
|
1158
|
+
if (action.kind === 'removeOwnedArtifact') {
|
|
1159
|
+
return cleanupPathAuthorized(action.path, evidence);
|
|
1160
|
+
}
|
|
1161
|
+
if (action.kind === 'removeNativeRegistration') {
|
|
1162
|
+
return evidence.plan.actions.some((planned) => planned.action === 'removeNativeRegistration'
|
|
1163
|
+
&& planned.target === action.registration);
|
|
1164
|
+
}
|
|
1165
|
+
if (action.kind === 'setActivation')
|
|
1166
|
+
return nativeAuthorized;
|
|
1167
|
+
return false;
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
const result = await (options.executeCleanupOperation ?? executeRebuildOperation)({
|
|
1171
|
+
command: 'cleanup',
|
|
1172
|
+
target: desired,
|
|
1173
|
+
driver: integration.driver,
|
|
1174
|
+
});
|
|
1175
|
+
if (target.marketplaceIdentityUnresolved) {
|
|
1176
|
+
result.residual.push('nativeMarketplaceIdentityUnresolved');
|
|
1177
|
+
warnings.push(`nativeMarketplaceIdentityUnresolved: Preserved ambiguous native registration for ${target.pluginId} on ${target.platform}/${target.scope}.`);
|
|
1178
|
+
}
|
|
1179
|
+
let finalResult = result;
|
|
1180
|
+
if (unplannedComponents.length > 0) {
|
|
1181
|
+
const message = 'operationPlanResidual: Newly discovered owned output was not included in the cleanup plan and was preserved.';
|
|
1182
|
+
result.residual = [...new Set([
|
|
1183
|
+
...result.residual,
|
|
1184
|
+
...unplannedComponents.map(cleanupComponentResidual),
|
|
1185
|
+
])];
|
|
1186
|
+
warnings.push(message);
|
|
1187
|
+
if (result.status === 'succeeded') {
|
|
1188
|
+
finalResult = {
|
|
1189
|
+
...result,
|
|
1190
|
+
status: 'failed',
|
|
1191
|
+
failures: [...result.failures, { phase: 'cleanup.owned', message }],
|
|
1192
|
+
error: { phase: 'cleanup.owned', message },
|
|
1193
|
+
};
|
|
908
1194
|
}
|
|
909
|
-
: {}),
|
|
910
|
-
});
|
|
911
|
-
const result = await executeRebuildOperation({
|
|
912
|
-
command: 'cleanup',
|
|
913
|
-
target: desired,
|
|
914
|
-
driver: integration.driver,
|
|
915
|
-
});
|
|
916
|
-
if (target.marketplaceIdentityUnresolved) {
|
|
917
|
-
result.residual.push('nativeMarketplaceIdentityUnresolved');
|
|
918
|
-
warnings.push(`nativeMarketplaceIdentityUnresolved: Preserved ambiguous native registration for ${target.pluginId} on ${target.platform}/${target.scope}.`);
|
|
919
|
-
}
|
|
920
|
-
let finalResult = result;
|
|
921
|
-
if (inventoryErrors.length > 0) {
|
|
922
|
-
const message = 'stateInventoryResidual: Some weak state could not be verified. Retry cleanup, use cleanup --force only for exclusively owned damaged output, or run reinstall/sync to restore the target.';
|
|
923
|
-
result.residual = [...new Set([
|
|
924
|
-
...result.residual,
|
|
925
|
-
...inventoryErrors.map((error) => error.location),
|
|
926
|
-
])];
|
|
927
|
-
warnings.push(...inventoryErrors.map((error) => `stateInventoryResidual: ${error.kind}: ${error.message}`));
|
|
928
|
-
if (result.status === 'succeeded') {
|
|
929
|
-
finalResult = {
|
|
930
|
-
...result,
|
|
931
|
-
status: 'failed',
|
|
932
|
-
failures: [...result.failures, { phase: 'cleanup.owned', message }],
|
|
933
|
-
error: { phase: 'cleanup.owned', message },
|
|
934
|
-
};
|
|
935
1195
|
}
|
|
1196
|
+
if (inventoryErrors.length > 0) {
|
|
1197
|
+
const message = 'stateInventoryResidual: Some weak state could not be verified. Retry cleanup, use cleanup --force only for exclusively owned damaged output, or run reinstall/sync to restore the target.';
|
|
1198
|
+
result.residual = [...new Set([
|
|
1199
|
+
...result.residual,
|
|
1200
|
+
...inventoryErrors.map((error) => error.location),
|
|
1201
|
+
])];
|
|
1202
|
+
warnings.push(...inventoryErrors.map((error) => `stateInventoryResidual: ${error.kind}: ${error.message}`));
|
|
1203
|
+
if (finalResult.status === 'succeeded') {
|
|
1204
|
+
finalResult = {
|
|
1205
|
+
...finalResult,
|
|
1206
|
+
status: 'failed',
|
|
1207
|
+
failures: [...finalResult.failures, { phase: 'cleanup.owned', message }],
|
|
1208
|
+
error: { phase: 'cleanup.owned', message },
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
results.push(finalResult);
|
|
1213
|
+
}
|
|
1214
|
+
catch (error) {
|
|
1215
|
+
results.push(failedResult('cleanup', {
|
|
1216
|
+
pluginId: target.pluginId,
|
|
1217
|
+
pluginName: target.pluginName,
|
|
1218
|
+
sourceId: target.sourceId,
|
|
1219
|
+
sourceIdentity: target.sourceIdentity,
|
|
1220
|
+
pluginVersion: '0.0.0',
|
|
1221
|
+
platform: target.platform,
|
|
1222
|
+
scope: target.scope,
|
|
1223
|
+
cwd: options.cwd,
|
|
1224
|
+
}, error instanceof Error ? error.message : String(error), 'cleanup.owned'));
|
|
936
1225
|
}
|
|
937
|
-
results.push(finalResult);
|
|
938
1226
|
}
|
|
939
1227
|
const obsoleteContracts = path.join(resolveAwHome(options.env), 'installation-contracts');
|
|
940
1228
|
try {
|
|
@@ -973,9 +1261,16 @@ async function previewOfflineOwnedComponents(options) {
|
|
|
973
1261
|
await options.onPreviewStage?.('dependencyPlan');
|
|
974
1262
|
return preview;
|
|
975
1263
|
}
|
|
976
|
-
function createOfflineCleanupOperationPlan(evidence) {
|
|
1264
|
+
function createOfflineCleanupOperationPlan(evidence, intent, warnings = []) {
|
|
1265
|
+
const beforeDigest = intent ? calculatePluginIntentDigest(intent.before) : undefined;
|
|
1266
|
+
const afterDigest = intent ? calculatePluginIntentDigest(intent.after) : undefined;
|
|
1267
|
+
const removesIntent = (target) => intent !== undefined
|
|
1268
|
+
&& intent.before.roots.some((root) => root.pluginId === target.pluginId && root.platforms.includes(target.platform))
|
|
1269
|
+
&& !intent.after.roots.some((root) => root.pluginId === target.pluginId && root.platforms.includes(target.platform));
|
|
977
1270
|
return createOperationPlan({
|
|
978
|
-
intentChanges:
|
|
1271
|
+
intentChanges: intent && beforeDigest !== afterDigest
|
|
1272
|
+
? [{ scope: intent.scope, beforeDigest: beforeDigest, afterDigest: afterDigest }]
|
|
1273
|
+
: [],
|
|
979
1274
|
actions: evidence.flatMap((target) => target.plan.actions.map((action) => action.action === 'removeNativeRegistration'
|
|
980
1275
|
? {
|
|
981
1276
|
kind: 'removeNativeRegistration',
|
|
@@ -992,14 +1287,19 @@ function createOfflineCleanupOperationPlan(evidence) {
|
|
|
992
1287
|
path: action.target,
|
|
993
1288
|
ownerFingerprint: target.targetFingerprint,
|
|
994
1289
|
})),
|
|
995
|
-
changes:
|
|
996
|
-
|
|
1290
|
+
changes: [
|
|
1291
|
+
...evidence.flatMap((target) => removesIntent(target)
|
|
1292
|
+
? [`${target.pluginId}:${target.platform}:${target.scope}:removeIntent`]
|
|
1293
|
+
: []),
|
|
1294
|
+
...evidence.flatMap((target) => target.plan.actions.map((action) => `${target.pluginId}:${target.platform}:${target.scope}:${action.action}:${action.target}`)),
|
|
1295
|
+
],
|
|
1296
|
+
warnings: [...warnings],
|
|
997
1297
|
evidence: {
|
|
998
1298
|
catalogDigests: evidence.map((target) => ({
|
|
999
1299
|
sourceId: target.sourceId,
|
|
1000
1300
|
digest: target.sourceIdentity,
|
|
1001
1301
|
})),
|
|
1002
|
-
intentDigests: [],
|
|
1302
|
+
intentDigests: intent ? [{ scope: intent.scope, digest: beforeDigest }] : [],
|
|
1003
1303
|
configDigests: [],
|
|
1004
1304
|
targetOwnerFingerprints: evidence.map((target) => ({
|
|
1005
1305
|
pluginId: target.pluginId,
|
|
@@ -1244,15 +1544,51 @@ async function runRebuildCommandInSession(options) {
|
|
|
1244
1544
|
?? inventoryCatalogSourceReferences;
|
|
1245
1545
|
let ordinaryInventory;
|
|
1246
1546
|
const readOrdinaryInventory = async () => {
|
|
1247
|
-
ordinaryInventory ??= await measureCommandPerformanceStage(options.runtime, 'inventory', () =>
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1547
|
+
ordinaryInventory ??= await measureCommandPerformanceStage(options.runtime, 'inventory', async () => {
|
|
1548
|
+
const inventory = await readInventory({
|
|
1549
|
+
cwd: options.commandOptions.cwd,
|
|
1550
|
+
env: runtimeEnv,
|
|
1551
|
+
});
|
|
1552
|
+
if (options.command !== 'cleanup'
|
|
1553
|
+
|| options.commandOptions.scope !== 'project'
|
|
1554
|
+
|| !selectedPlatforms(options.commandOptions.platform).includes('codex'))
|
|
1555
|
+
return inventory;
|
|
1556
|
+
let projectSkills = cleanupProjectSkillsByInventory.get(inventory);
|
|
1557
|
+
if (!projectSkills) {
|
|
1558
|
+
projectSkills = await selectedProjectCodexSkillReferences({
|
|
1559
|
+
cwd: options.commandOptions.cwd,
|
|
1560
|
+
});
|
|
1561
|
+
cleanupProjectSkillsByInventory.set(inventory, projectSkills);
|
|
1562
|
+
}
|
|
1563
|
+
const selectors = options.pluginNames.map(parsePluginId);
|
|
1564
|
+
const selectedProjectSkills = options.all === true
|
|
1565
|
+
? projectSkills
|
|
1566
|
+
: projectSkills.filter((reference) => selectors.some((selector) => selector.pluginName === reference.pluginName
|
|
1567
|
+
&& (selector.sourceId === undefined || selector.sourceId === reference.sourceId)));
|
|
1568
|
+
return {
|
|
1569
|
+
...inventory,
|
|
1570
|
+
references: [...new Map([...inventory.references, ...selectedProjectSkills].map((reference) => [[
|
|
1571
|
+
reference.kind,
|
|
1572
|
+
reference.location,
|
|
1573
|
+
reference.sourceId ?? '',
|
|
1574
|
+
reference.pluginName ?? '',
|
|
1575
|
+
reference.platform ?? '',
|
|
1576
|
+
reference.scope ?? '',
|
|
1577
|
+
].join('\0'), reference])).values()],
|
|
1578
|
+
};
|
|
1579
|
+
});
|
|
1251
1580
|
return ordinaryInventory;
|
|
1252
1581
|
};
|
|
1253
1582
|
let requestedPluginNames = options.pluginNames;
|
|
1254
1583
|
const cleanupForce = options.command === 'cleanup'
|
|
1255
1584
|
&& options.commandOptions.force === true;
|
|
1585
|
+
const cleanupIntentBefore = options.command === 'cleanup'
|
|
1586
|
+
? await readPluginIntentState({
|
|
1587
|
+
scope: options.commandOptions.scope,
|
|
1588
|
+
cwd: options.commandOptions.cwd,
|
|
1589
|
+
env: runtimeEnv,
|
|
1590
|
+
})
|
|
1591
|
+
: undefined;
|
|
1256
1592
|
const expectedOfflineCleanup = options.runtime?.expectedOfflineCleanupEvidence;
|
|
1257
1593
|
let cleanupInventory;
|
|
1258
1594
|
let cleanupLiveTargets = [];
|
|
@@ -1269,7 +1605,9 @@ async function runRebuildCommandInSession(options) {
|
|
|
1269
1605
|
});
|
|
1270
1606
|
requestedPluginNames = live.selectors;
|
|
1271
1607
|
cleanupLiveTargets = live.targets;
|
|
1272
|
-
if (expectedOfflineCleanup
|
|
1608
|
+
if (expectedOfflineCleanup
|
|
1609
|
+
&& expectedOfflineCleanup.forceExclusive !== true
|
|
1610
|
+
&& expectedOfflineCleanup.plan.actions.length > 0) {
|
|
1273
1611
|
const liveTarget = cleanupLiveTargets.find((target) => target.pluginId === expectedOfflineCleanup.pluginId
|
|
1274
1612
|
&& target.pluginName === expectedOfflineCleanup.pluginName
|
|
1275
1613
|
&& target.sourceId === expectedOfflineCleanup.sourceId
|
|
@@ -1338,17 +1676,96 @@ async function runRebuildCommandInSession(options) {
|
|
|
1338
1676
|
const pluginId = formatPluginId({ sourceId: reference.sourceId, pluginName: reference.pluginName });
|
|
1339
1677
|
identities.add(pluginId);
|
|
1340
1678
|
}
|
|
1679
|
+
if (options.command === 'cleanup') {
|
|
1680
|
+
for (const root of cleanupIntentBefore.roots)
|
|
1681
|
+
identities.add(root.pluginId);
|
|
1682
|
+
}
|
|
1341
1683
|
requestedPluginNames = [...identities];
|
|
1342
1684
|
}
|
|
1685
|
+
let cleanupTargets = [];
|
|
1686
|
+
let cleanupIntentAfter;
|
|
1687
|
+
let cleanupSelectionErrors = [];
|
|
1688
|
+
if (options.command === 'cleanup') {
|
|
1689
|
+
const primarySourceId = cleanupForce
|
|
1690
|
+
? await readCatalogSourcesConfigSnapshot(runtimeEnv)
|
|
1691
|
+
.then((snapshot) => snapshot.config.sources.find((source) => source.primary)?.id)
|
|
1692
|
+
.catch(() => undefined)
|
|
1693
|
+
: undefined;
|
|
1694
|
+
const intentTargets = cleanupIntentTargets({
|
|
1695
|
+
state: cleanupIntentBefore,
|
|
1696
|
+
selectors: options.pluginNames,
|
|
1697
|
+
platforms: selectedPlatforms(options.commandOptions.platform),
|
|
1698
|
+
scope: options.commandOptions.scope,
|
|
1699
|
+
all: options.all === true,
|
|
1700
|
+
liveTargets: cleanupLiveTargets,
|
|
1701
|
+
primarySourceId,
|
|
1702
|
+
});
|
|
1703
|
+
const damagedTargets = options.pluginNames.flatMap((selector) => {
|
|
1704
|
+
const parsed = parsePluginId(selector);
|
|
1705
|
+
if (!parsed.sourceId)
|
|
1706
|
+
return [];
|
|
1707
|
+
return selectedPlatforms(options.commandOptions.platform).flatMap((platform) => cleanupInventory.errors.some((error) => error.relevance.global || ((error.relevance.sourceId === undefined || error.relevance.sourceId === parsed.sourceId)
|
|
1708
|
+
&& (error.relevance.pluginName === undefined || error.relevance.pluginName === parsed.pluginName)
|
|
1709
|
+
&& (error.relevance.platform === undefined || error.relevance.platform === platform)
|
|
1710
|
+
&& (error.relevance.scope === undefined || error.relevance.scope === options.commandOptions.scope))) ? [{
|
|
1711
|
+
pluginId: formatPluginId(parsed),
|
|
1712
|
+
pluginName: parsed.pluginName,
|
|
1713
|
+
sourceId: parsed.sourceId,
|
|
1714
|
+
sourceIdentity: 'unavailable',
|
|
1715
|
+
platform,
|
|
1716
|
+
scope: options.commandOptions.scope,
|
|
1717
|
+
}] : []);
|
|
1718
|
+
});
|
|
1719
|
+
cleanupTargets = [...new Map([...damagedTargets, ...intentTargets, ...cleanupLiveTargets].map((target) => [
|
|
1720
|
+
`${target.pluginId}\0${target.platform}\0${target.scope}`,
|
|
1721
|
+
target,
|
|
1722
|
+
])).values()];
|
|
1723
|
+
requestedPluginNames = [...new Set([
|
|
1724
|
+
...requestedPluginNames,
|
|
1725
|
+
...cleanupTargets.map((target) => target.pluginId),
|
|
1726
|
+
])];
|
|
1727
|
+
cleanupIntentAfter = deriveDesiredIntentState({
|
|
1728
|
+
command: 'cleanup',
|
|
1729
|
+
state: cleanupIntentBefore,
|
|
1730
|
+
selectedRoots: cleanupSelectedRoots(cleanupTargets),
|
|
1731
|
+
plan: { requestedPluginIds: [], resolvedPluginIds: [], dependencyEdges: [], targets: [], scopeOverrides: [] },
|
|
1732
|
+
});
|
|
1733
|
+
const filtered = omitSelectedPluginConfigErrors(cleanupInventory, cleanupTargets);
|
|
1734
|
+
cleanupInventory = filtered.inventory;
|
|
1735
|
+
malformedSelectedConfigPreserved ||= filtered.omitted;
|
|
1736
|
+
cleanupSelectionErrors = cleanupInventoryErrorsForSelection({
|
|
1737
|
+
inventory: cleanupInventory,
|
|
1738
|
+
selectors: options.pluginNames,
|
|
1739
|
+
platforms: selectedPlatforms(options.commandOptions.platform),
|
|
1740
|
+
scope: options.commandOptions.scope,
|
|
1741
|
+
all: options.all === true,
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1343
1744
|
const selectors = requestedPluginNames.map(parsePluginId);
|
|
1344
1745
|
const sourceIds = planPluginSelectorSourceIds(selectors);
|
|
1345
|
-
if (options.all && selectors.length === 0) {
|
|
1746
|
+
if (options.all && selectors.length === 0 && options.command !== 'cleanup') {
|
|
1346
1747
|
if (legacyLiveTargets.length > 0) {
|
|
1347
1748
|
throw namespacedPreflightError(`legacyStateUnsupported: legacy live runtime state exists for ${legacyLiveTargets.map((target) => `${target.pluginName}:${target.platform}/${target.scope}`).join(', ')}.`, [], legacyLiveTargets);
|
|
1348
1749
|
}
|
|
1349
1750
|
return mergeRebuildCommandResults([]);
|
|
1350
1751
|
}
|
|
1351
|
-
if (options.command === 'cleanup'
|
|
1752
|
+
if (options.command === 'cleanup') {
|
|
1753
|
+
if (!cleanupForce
|
|
1754
|
+
&& cleanupTargets.length === 0
|
|
1755
|
+
&& cleanupSelectionErrors.length === 0
|
|
1756
|
+
&& legacyLiveTargets.length === 0) {
|
|
1757
|
+
const result = mergeRebuildCommandResults([]);
|
|
1758
|
+
const operationPlan = createOfflineCleanupOperationPlan([], {
|
|
1759
|
+
scope: options.commandOptions.scope,
|
|
1760
|
+
before: cleanupIntentBefore,
|
|
1761
|
+
after: cleanupIntentAfter,
|
|
1762
|
+
}, cleanupInventoryWarnings(cleanupInventory, []));
|
|
1763
|
+
if (options.runtime?.expectedPlanFingerprint) {
|
|
1764
|
+
assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, operationPlan);
|
|
1765
|
+
}
|
|
1766
|
+
result.operationPlan = operationPlan;
|
|
1767
|
+
return result;
|
|
1768
|
+
}
|
|
1352
1769
|
if (cleanupForce) {
|
|
1353
1770
|
const requestedPlatforms = selectedPlatforms(options.commandOptions.platform);
|
|
1354
1771
|
const cleanupSourceSnapshot = await readCatalogSourcesConfigSnapshot(runtimeEnv).catch(() => undefined);
|
|
@@ -1379,18 +1796,27 @@ async function runRebuildCommandInSession(options) {
|
|
|
1379
1796
|
&& (error.relevance.scope === undefined || error.relevance.scope === options.commandOptions.scope));
|
|
1380
1797
|
return missingLiveTarget || damagedExclusiveEvidence;
|
|
1381
1798
|
});
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1799
|
+
const supplementAttempts = await Promise.allSettled(selectorsNeedingSupplement.map((selector) => buildForceExclusiveCleanupPlan({
|
|
1800
|
+
selectors: [selector],
|
|
1801
|
+
platforms: requestedPlatforms,
|
|
1802
|
+
scope: options.commandOptions.scope,
|
|
1803
|
+
cwd: options.commandOptions.cwd,
|
|
1804
|
+
env: runtimeEnv,
|
|
1805
|
+
primarySourceId: primarySource,
|
|
1806
|
+
damagedOnly: cleanupLiveTargets.length > 0,
|
|
1807
|
+
})));
|
|
1808
|
+
const forcePlan = await mergeForceCleanupPlans(livePlan, ...supplementAttempts.flatMap((attempt) => attempt.status === 'fulfilled' ? [attempt.value] : []));
|
|
1809
|
+
const forceCleanupTargets = [...new Map([
|
|
1810
|
+
...cleanupTargets,
|
|
1811
|
+
...forcePlan.targets.map((target) => ({
|
|
1812
|
+
pluginId: target.identity.pluginId,
|
|
1813
|
+
pluginName: target.identity.pluginName,
|
|
1814
|
+
sourceId: target.identity.sourceId,
|
|
1815
|
+
sourceIdentity: target.identity.sourceIdentity,
|
|
1816
|
+
platform: target.identity.platform,
|
|
1817
|
+
scope: target.identity.scope,
|
|
1818
|
+
})),
|
|
1819
|
+
].map((target) => [`${target.pluginId}\0${target.platform}\0${target.scope}`, target])).values()];
|
|
1394
1820
|
const requestedTargets = requestedPluginNames.flatMap((selector) => {
|
|
1395
1821
|
const parsed = parsePluginId(selector);
|
|
1396
1822
|
const sourceId = parsed.sourceId ?? primarySource;
|
|
@@ -1405,7 +1831,11 @@ async function runRebuildCommandInSession(options) {
|
|
|
1405
1831
|
});
|
|
1406
1832
|
const unresolvedTargets = requestedTargets.filter((requested) => !forcePlan.targets.some((target) => target.identity.pluginId === requested.pluginId
|
|
1407
1833
|
&& target.identity.platform === requested.platform
|
|
1408
|
-
&& target.identity.scope === requested.scope)
|
|
1834
|
+
&& target.identity.scope === requested.scope)
|
|
1835
|
+
&& cleanupInventory.errors.some((error) => error.relevance.global || ((error.relevance.sourceId === undefined || error.relevance.sourceId === requested.sourceId)
|
|
1836
|
+
&& (error.relevance.pluginName === undefined || error.relevance.pluginName === requested.pluginName)
|
|
1837
|
+
&& (error.relevance.platform === undefined || error.relevance.platform === requested.platform)
|
|
1838
|
+
&& (error.relevance.scope === undefined || error.relevance.scope === requested.scope))));
|
|
1409
1839
|
if (expectedOfflineCleanup?.forceExclusive === true) {
|
|
1410
1840
|
const actual = forcePlan.evidence.find((candidate) => candidate.pluginId === expectedOfflineCleanup.pluginId
|
|
1411
1841
|
&& candidate.platform === expectedOfflineCleanup.platform
|
|
@@ -1414,21 +1844,23 @@ async function runRebuildCommandInSession(options) {
|
|
|
1414
1844
|
throw namespacedPreflightError('operationReviewStale: force cleanup path or ownership evidence changed before mutation.', cleanupLiveTargets);
|
|
1415
1845
|
}
|
|
1416
1846
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1847
|
+
const forceEvidence = forceCleanupTargets.map((target) => forcePlan.evidence.find((candidate) => candidate.pluginId === target.pluginId
|
|
1848
|
+
&& candidate.platform === target.platform
|
|
1849
|
+
&& candidate.scope === target.scope)
|
|
1850
|
+
?? intentOnlyOfflineCleanupEvidence(target));
|
|
1851
|
+
const operationPlan = createOfflineCleanupOperationPlan(forceEvidence, {
|
|
1852
|
+
scope: options.commandOptions.scope,
|
|
1853
|
+
before: cleanupIntentBefore,
|
|
1854
|
+
after: cleanupIntentAfter,
|
|
1855
|
+
}, cleanupInventoryWarnings(cleanupInventory, forceCleanupTargets));
|
|
1424
1856
|
if (options.runtime?.expectedPlanFingerprint) {
|
|
1425
1857
|
assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, operationPlan);
|
|
1426
1858
|
}
|
|
1427
1859
|
if (options.runtime?.previewOperationPlan) {
|
|
1428
1860
|
return {
|
|
1429
1861
|
...await previewOfflineOwnedComponents({
|
|
1430
|
-
targets:
|
|
1431
|
-
evidence:
|
|
1862
|
+
targets: forceCleanupTargets,
|
|
1863
|
+
evidence: forceEvidence,
|
|
1432
1864
|
cwd: options.commandOptions.cwd,
|
|
1433
1865
|
env: runtimeEnv,
|
|
1434
1866
|
onPreviewStage: options.runtime.onPreviewStage,
|
|
@@ -1436,6 +1868,14 @@ async function runRebuildCommandInSession(options) {
|
|
|
1436
1868
|
operationPlan,
|
|
1437
1869
|
};
|
|
1438
1870
|
}
|
|
1871
|
+
if (calculatePluginIntentDigest(cleanupIntentBefore)
|
|
1872
|
+
!== calculatePluginIntentDigest(cleanupIntentAfter)) {
|
|
1873
|
+
await (options.runtime?.writePluginIntentState ?? writePluginIntentState)({
|
|
1874
|
+
scope: options.commandOptions.scope,
|
|
1875
|
+
cwd: options.commandOptions.cwd,
|
|
1876
|
+
env: runtimeEnv,
|
|
1877
|
+
}, cleanupIntentAfter);
|
|
1878
|
+
}
|
|
1439
1879
|
const forceResults = await Promise.all(forcePlan.targets.map(async (target) => {
|
|
1440
1880
|
const integration = createRebuildIntegration({
|
|
1441
1881
|
context: {
|
|
@@ -1446,86 +1886,89 @@ async function runRebuildCommandInSession(options) {
|
|
|
1446
1886
|
},
|
|
1447
1887
|
});
|
|
1448
1888
|
const { finalizeCleanup: _finalizeCleanup, ...pathOnlyDriver } = integration.driver;
|
|
1449
|
-
const result = await
|
|
1889
|
+
const result = await (options.runtime?.executeCleanupOperation ?? executeRebuildOperation)({
|
|
1890
|
+
command: 'cleanup', target, driver: pathOnlyDriver,
|
|
1891
|
+
});
|
|
1450
1892
|
if (cleanupLiveTargets.some((liveTarget) => liveTarget.marketplaceIdentityUnresolved
|
|
1451
1893
|
&& liveTarget.pluginId === result.target.pluginId
|
|
1452
1894
|
&& liveTarget.platform === result.target.platform
|
|
1453
1895
|
&& liveTarget.scope === result.target.scope)) {
|
|
1454
1896
|
result.residual.push('nativeMarketplaceIdentityUnresolved');
|
|
1455
1897
|
}
|
|
1898
|
+
const targetEvidence = forceEvidence.find((candidate) => candidate.pluginId === target.identity.pluginId
|
|
1899
|
+
&& candidate.platform === target.identity.platform
|
|
1900
|
+
&& candidate.scope === target.identity.scope);
|
|
1901
|
+
const targetDescriptor = forceCleanupTargets.find((candidate) => candidate.pluginId === target.identity.pluginId
|
|
1902
|
+
&& candidate.platform === target.identity.platform
|
|
1903
|
+
&& candidate.scope === target.identity.scope);
|
|
1904
|
+
const unresolvedErrors = cleanupInventoryErrorsForTarget(cleanupInventory, targetDescriptor).filter((error) => !cleanupActionCoversLocation(targetEvidence, error.location, runtimeEnv, options.commandOptions.cwd));
|
|
1905
|
+
if (unresolvedErrors.length > 0) {
|
|
1906
|
+
const message = 'stateInventoryResidual: Some weak state could not be verified after force cleanup.';
|
|
1907
|
+
result.residual = [...new Set([
|
|
1908
|
+
...result.residual,
|
|
1909
|
+
...unresolvedErrors.map((error) => error.location),
|
|
1910
|
+
])];
|
|
1911
|
+
if (result.status === 'succeeded') {
|
|
1912
|
+
return {
|
|
1913
|
+
...result,
|
|
1914
|
+
status: 'failed',
|
|
1915
|
+
failures: [...result.failures, { phase: 'cleanup.owned', message }],
|
|
1916
|
+
error: { phase: 'cleanup.owned', message },
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1456
1920
|
return result;
|
|
1457
1921
|
}));
|
|
1922
|
+
const unresolvedResults = unresolvedTargets.map((target) => failedResult('cleanup', { ...target, pluginVersion: '0.0.0', cwd: options.commandOptions.cwd }, 'forceCleanupPathUnavailable: No exact exclusive output is available from live evidence or the local Active shallow plan. Restore the local Active bundle, then retry cleanup --force, or inspect and remove the exact output manually.'));
|
|
1923
|
+
const intentOnlyResults = forceCleanupTargets
|
|
1924
|
+
.filter((target) => !forcePlan.targets.some((forceTarget) => forceTarget.identity.pluginId === target.pluginId
|
|
1925
|
+
&& forceTarget.identity.platform === target.platform
|
|
1926
|
+
&& forceTarget.identity.scope === target.scope)
|
|
1927
|
+
&& !unresolvedTargets.some((unresolved) => unresolved.pluginId === target.pluginId
|
|
1928
|
+
&& unresolved.platform === target.platform
|
|
1929
|
+
&& unresolved.scope === target.scope))
|
|
1930
|
+
.map(cleanupIntentResult);
|
|
1931
|
+
const allResults = [...forceResults, ...unresolvedResults, ...intentOnlyResults];
|
|
1458
1932
|
const warnings = cleanupLiveTargets.flatMap((target) => target.marketplaceIdentityUnresolved
|
|
1459
1933
|
? [`nativeMarketplaceIdentityUnresolved: Preserved ambiguous native registration for ${target.pluginId} on ${target.platform}/${target.scope}.`]
|
|
1460
1934
|
: []);
|
|
1461
1935
|
return leaseSafeRebuildResult({
|
|
1462
|
-
platforms: [...new Set(
|
|
1463
|
-
plugins:
|
|
1936
|
+
platforms: [...new Set(allResults.map((entry) => entry.target.platform))],
|
|
1937
|
+
plugins: allResults.filter((entry) => entry.status === 'succeeded')
|
|
1464
1938
|
.map((entry) => entry.target.pluginName),
|
|
1465
1939
|
warnings,
|
|
1466
|
-
rebuildResults:
|
|
1467
|
-
targetResults:
|
|
1468
|
-
overallState:
|
|
1940
|
+
rebuildResults: allResults,
|
|
1941
|
+
targetResults: allResults.map(toTargetResult),
|
|
1942
|
+
overallState: allResults.every((entry) => entry.status === 'succeeded')
|
|
1469
1943
|
? 'succeeded'
|
|
1470
|
-
:
|
|
1471
|
-
|
|
1472
|
-
|
|
1944
|
+
: operationPlan.intentChanges.length > 0
|
|
1945
|
+
|| allResults.some((entry) => entry.status === 'succeeded'
|
|
1946
|
+
|| entry.actions.some((action) => action.status === 'succeeded'))
|
|
1947
|
+
? 'partial' : 'failed',
|
|
1948
|
+
receiptTargets: allResults.map((entry) => entry.target),
|
|
1949
|
+
offlineCleanupPlans: forceEvidence,
|
|
1473
1950
|
operationPlan,
|
|
1474
1951
|
});
|
|
1475
1952
|
}
|
|
1476
|
-
if (
|
|
1477
|
-
const requestedPlatforms = selectedPlatforms(options.commandOptions.platform);
|
|
1478
|
-
const primarySource = await readCatalogSourcesConfigSnapshot(runtimeEnv)
|
|
1479
|
-
.then((snapshot) => snapshot.config.sources.find((source) => source.primary)?.id)
|
|
1480
|
-
.catch(() => undefined);
|
|
1481
|
-
const unresolvedTargets = requestedPluginNames.flatMap((selector) => {
|
|
1482
|
-
const parsed = parsePluginId(selector);
|
|
1483
|
-
const sourceId = parsed.sourceId ?? primarySource;
|
|
1484
|
-
return sourceId ? requestedPlatforms.map((platform) => ({
|
|
1485
|
-
pluginId: formatPluginId({ sourceId, pluginName: parsed.pluginName }),
|
|
1486
|
-
pluginName: parsed.pluginName,
|
|
1487
|
-
sourceId,
|
|
1488
|
-
sourceIdentity: 'unavailable',
|
|
1489
|
-
platform,
|
|
1490
|
-
scope: options.commandOptions.scope,
|
|
1491
|
-
})) : [];
|
|
1492
|
-
});
|
|
1493
|
-
const qualifiedTargets = requestedPluginNames.flatMap((selector) => {
|
|
1494
|
-
const parsed = parsePluginId(selector);
|
|
1495
|
-
return parsed.sourceId ? [{ sourceId: parsed.sourceId, pluginName: parsed.pluginName }] : [];
|
|
1496
|
-
});
|
|
1497
|
-
const filtered = cleanupInventory && qualifiedTargets.length > 0
|
|
1498
|
-
? omitSelectedPluginConfigErrors(cleanupInventory, qualifiedTargets)
|
|
1499
|
-
: { inventory: cleanupInventory, omitted: false };
|
|
1500
|
-
malformedSelectedConfigPreserved ||= filtered.omitted;
|
|
1501
|
-
const inventoryError = filtered.inventory?.errors.find((error) => {
|
|
1502
|
-
if (!cleanupForce
|
|
1503
|
-
|| (error.kind !== 'ownerMarkerRead' && error.kind !== 'ownerMarkerValidation')
|
|
1504
|
-
|| error.relevance.global)
|
|
1505
|
-
return true;
|
|
1506
|
-
const relevant = unresolvedTargets.some((target) => !error.relevance.global
|
|
1507
|
-
&& (error.relevance.sourceId === undefined || error.relevance.sourceId === target.sourceId)
|
|
1508
|
-
&& (error.relevance.pluginName === undefined || error.relevance.pluginName === target.pluginName)
|
|
1509
|
-
&& (error.relevance.platform === undefined || error.relevance.platform === target.platform)
|
|
1510
|
-
&& (error.relevance.scope === undefined || error.relevance.scope === target.scope));
|
|
1511
|
-
return relevant
|
|
1512
|
-
&& !(error.relevance.platform === 'codex' && error.relevance.scope === 'project');
|
|
1513
|
-
});
|
|
1514
|
-
if (inventoryError) {
|
|
1515
|
-
throw namespacedPreflightError(`stateInventoryFailed: ${inventoryError.message}`, unresolvedTargets);
|
|
1516
|
-
}
|
|
1953
|
+
if (cleanupTargets.length === 0) {
|
|
1517
1954
|
const result = mergeRebuildCommandResults([]);
|
|
1955
|
+
const operationPlan = createOfflineCleanupOperationPlan([], {
|
|
1956
|
+
scope: options.commandOptions.scope,
|
|
1957
|
+
before: cleanupIntentBefore,
|
|
1958
|
+
after: cleanupIntentAfter,
|
|
1959
|
+
}, cleanupInventoryWarnings(cleanupInventory, []));
|
|
1960
|
+
if (options.runtime?.expectedPlanFingerprint) {
|
|
1961
|
+
assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, operationPlan);
|
|
1962
|
+
}
|
|
1963
|
+
result.operationPlan = operationPlan;
|
|
1964
|
+
result.overallState = cleanupSelectionErrors.length > 0 || legacyLiveTargets.length > 0
|
|
1965
|
+
? 'failed'
|
|
1966
|
+
: 'succeeded';
|
|
1967
|
+
result.warnings.push(...cleanupSelectionErrors.map((error) => `stateInventoryResidual: ${error.kind}: ${error.message}`));
|
|
1968
|
+
result.warnings.push(...legacyLiveTargets.map((target) => `legacyStateUnsupported: legacy live runtime state exists for ${target.pluginName}:${target.platform}/${target.scope}.`));
|
|
1518
1969
|
if (malformedSelectedConfigPreserved) {
|
|
1519
1970
|
result.warnings.push(MALFORMED_CONFIG_PRESERVED_WARNING);
|
|
1520
1971
|
}
|
|
1521
|
-
if (cleanupForce && !options.runtime?.previewOperationPlan) {
|
|
1522
|
-
try {
|
|
1523
|
-
await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
|
|
1524
|
-
}
|
|
1525
|
-
catch {
|
|
1526
|
-
result.warnings.push(OBSOLETE_CONTRACTS_CLEANUP_WARNING);
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
1972
|
return result;
|
|
1530
1973
|
}
|
|
1531
1974
|
const cleanupSourceSnapshot = await readCatalogSourcesConfigSnapshot(runtimeEnv).catch(() => undefined);
|
|
@@ -1545,19 +1988,37 @@ async function runRebuildCommandInSession(options) {
|
|
|
1545
1988
|
});
|
|
1546
1989
|
return value ? [value] : [];
|
|
1547
1990
|
});
|
|
1991
|
+
evidence = cleanupTargets.map((target) => evidence.find((candidate) => candidate.pluginId === target.pluginId
|
|
1992
|
+
&& candidate.platform === target.platform
|
|
1993
|
+
&& candidate.scope === target.scope)
|
|
1994
|
+
?? intentOnlyOfflineCleanupEvidence(target));
|
|
1995
|
+
const operationPlan = createOfflineCleanupOperationPlan(evidence, {
|
|
1996
|
+
scope: options.commandOptions.scope,
|
|
1997
|
+
before: cleanupIntentBefore,
|
|
1998
|
+
after: cleanupIntentAfter,
|
|
1999
|
+
}, cleanupInventoryWarnings(cleanupInventory, cleanupTargets));
|
|
1548
2000
|
if (options.runtime?.expectedPlanFingerprint) {
|
|
1549
|
-
assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint,
|
|
2001
|
+
assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, operationPlan);
|
|
1550
2002
|
}
|
|
1551
2003
|
if (options.runtime?.previewOperationPlan) {
|
|
1552
2004
|
const previewResult = await previewOfflineOwnedComponents({
|
|
1553
|
-
targets:
|
|
2005
|
+
targets: cleanupTargets,
|
|
1554
2006
|
evidence,
|
|
1555
2007
|
cwd: options.commandOptions.cwd,
|
|
1556
2008
|
env: runtimeEnv,
|
|
1557
2009
|
onPreviewStage: options.runtime.onPreviewStage,
|
|
1558
2010
|
});
|
|
2011
|
+
previewResult.operationPlan = operationPlan;
|
|
1559
2012
|
return previewResult;
|
|
1560
2013
|
}
|
|
2014
|
+
if (calculatePluginIntentDigest(cleanupIntentBefore)
|
|
2015
|
+
!== calculatePluginIntentDigest(cleanupIntentAfter)) {
|
|
2016
|
+
await (options.runtime?.writePluginIntentState ?? writePluginIntentState)({
|
|
2017
|
+
scope: options.commandOptions.scope,
|
|
2018
|
+
cwd: options.commandOptions.cwd,
|
|
2019
|
+
env: runtimeEnv,
|
|
2020
|
+
}, cleanupIntentAfter);
|
|
2021
|
+
}
|
|
1561
2022
|
const cleanupResult = await cleanupOfflineOwnedComponents({
|
|
1562
2023
|
targets: cleanupLiveTargets,
|
|
1563
2024
|
evidence,
|
|
@@ -1567,7 +2028,32 @@ async function runRebuildCommandInSession(options) {
|
|
|
1567
2028
|
removeObsoleteInstallationContracts: options.runtime?.removeObsoleteInstallationContracts,
|
|
1568
2029
|
nativeCliRunner: options.runtime?.nativeCliRunner,
|
|
1569
2030
|
codexManager: options.runtime?.codexManager,
|
|
2031
|
+
executeCleanupOperation: options.runtime?.executeCleanupOperation,
|
|
2032
|
+
});
|
|
2033
|
+
const liveKeys = new Set(cleanupLiveTargets.map((target) => `${target.pluginId}\0${target.platform}\0${target.scope}`));
|
|
2034
|
+
const intentOnlyResults = cleanupTargets
|
|
2035
|
+
.filter((target) => !liveKeys.has(`${target.pluginId}\0${target.platform}\0${target.scope}`))
|
|
2036
|
+
.map((target) => {
|
|
2037
|
+
const inventoryError = cleanupInventory.errors.find((error) => error.relevance.global || ((error.relevance.sourceId === undefined || error.relevance.sourceId === target.sourceId)
|
|
2038
|
+
&& (error.relevance.pluginName === undefined || error.relevance.pluginName === target.pluginName)
|
|
2039
|
+
&& (error.relevance.platform === undefined || error.relevance.platform === target.platform)
|
|
2040
|
+
&& (error.relevance.scope === undefined || error.relevance.scope === target.scope)));
|
|
2041
|
+
return inventoryError
|
|
2042
|
+
? failedResult('cleanup', { ...cleanupIntentResult(target).target, cwd: options.commandOptions.cwd }, `stateInventoryResidual: ${inventoryError.message}`)
|
|
2043
|
+
: cleanupIntentResult(target);
|
|
1570
2044
|
});
|
|
2045
|
+
cleanupResult.rebuildResults.push(...intentOnlyResults);
|
|
2046
|
+
cleanupResult.targetResults.push(...intentOnlyResults.map(toTargetResult));
|
|
2047
|
+
cleanupResult.receiptTargets.push(...intentOnlyResults.map((result) => result.target));
|
|
2048
|
+
cleanupResult.platforms = [...new Set([...cleanupResult.platforms, ...intentOnlyResults.map((result) => result.target.platform)])];
|
|
2049
|
+
cleanupResult.plugins = [...new Set([...cleanupResult.plugins, ...intentOnlyResults.map((result) => result.target.pluginName)])];
|
|
2050
|
+
cleanupResult.operationPlan = operationPlan;
|
|
2051
|
+
if (cleanupResult.overallState === 'failed'
|
|
2052
|
+
&& operationPlan.intentChanges.length > 0)
|
|
2053
|
+
cleanupResult.overallState = 'partial';
|
|
2054
|
+
else if (intentOnlyResults.some((result) => result.status === 'failed')) {
|
|
2055
|
+
cleanupResult.overallState = operationPlan.intentChanges.length > 0 ? 'partial' : 'failed';
|
|
2056
|
+
}
|
|
1571
2057
|
if (malformedSelectedConfigPreserved) {
|
|
1572
2058
|
cleanupResult.warnings.push(MALFORMED_CONFIG_PRESERVED_WARNING);
|
|
1573
2059
|
}
|