mirror of
https://github.com/affaan-m/ECC.git
synced 2026-11-07 16:45:53 +00:00
fix(install): clean legacy home agents state
This commit is contained in:
1 parent
1b9d599aab
commit
8e641003ba
3 files changed
+203
-4
No files matched your search
@@ -13,6 +13,10 @@ const { getRecordedHookConsent } = require('./install/hook-consent');
|
||||
const {
|
||||
prepareClaudeSkillMigration,
|
||||
} = require('./install/claude-skill-migration');
|
||||
const {
|
||||
prepareLegacyAgentsMigration,
|
||||
removeLegacyAgentsFiles,
|
||||
} = require('./install/legacy-agents-migration');
|
||||
const {
|
||||
getLegacyAntigravityLocation,
|
||||
inspectLegacyAntigravityState,
|
||||
@@ -1908,7 +1912,10 @@ function prepareRepairMigration(plan, record) {
|
||||
installStatePath: record.installStatePath,
|
||||
statePreview: buildAdapterDerivedStatePreview(plan.statePreview, record),
|
||||
};
|
||||
const migration = prepareClaudeSkillMigration(trustedPlan);
|
||||
const migration = prepareLegacyAgentsMigration(
|
||||
trustedPlan,
|
||||
prepareClaudeSkillMigration(trustedPlan)
|
||||
);
|
||||
return {
|
||||
migration,
|
||||
plan: {
|
||||
@@ -2131,10 +2138,16 @@ function repairInstalledStates(options = {}) {
|
||||
const legacyMigrationPaths = migration.legacyOperationsToRemove.map(
|
||||
operation => operation.destinationPath
|
||||
);
|
||||
const legacyAgentsMigrationPaths = migration.legacyAgentsOperationsToRemove.map(
|
||||
operation => operation.destinationPath
|
||||
);
|
||||
const hasLegacyAgentsStateMigration = migration.legacyAgentsOperationsToDetach.length > 0;
|
||||
const plannedRepairs = [...new Set([
|
||||
...(needsOpencodeBuild ? [opencodeBuildRepairPath] : []),
|
||||
...repairOperations.map(operation => operation.destinationPath),
|
||||
...legacyMigrationPaths,
|
||||
...legacyAgentsMigrationPaths,
|
||||
...(hasLegacyAgentsStateMigration ? [record.installStatePath] : []),
|
||||
])];
|
||||
|
||||
if (options.dryRun) {
|
||||
@@ -2150,7 +2163,8 @@ function repairInstalledStates(options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
const hasLegacyMigration = migration.legacyOperationsToRemove.length > 0;
|
||||
const hasLegacyMigration = migration.legacyOperationsToRemove.length > 0
|
||||
|| hasLegacyAgentsStateMigration;
|
||||
const repairedPaths = needsOpencodeBuild ? [opencodeBuildRepairPath] : [];
|
||||
if (migration.requiresBridgeState && (repairOperations.length > 0 || hasLegacyMigration)) {
|
||||
writeRefreshedInstallState(record, migration.bridgeState);
|
||||
@@ -2181,6 +2195,8 @@ function repairInstalledStates(options = {}) {
|
||||
repairedPaths.push(removedPath);
|
||||
}
|
||||
}
|
||||
removeLegacyAgentsFiles(migration, record.targetRoot);
|
||||
repairedPaths.push(...legacyAgentsMigrationPaths.filter(filePath => !fs.existsSync(filePath)));
|
||||
}
|
||||
const changedInstalledBytes = repairOperations.length > 0
|
||||
|| needsOpencodeBuild
|
||||
|
||||
@@ -22,6 +22,10 @@ const {
|
||||
} = require('./claude-settings');
|
||||
const { filterMcpConfig, parseDisabledMcpServers } = require('../mcp-config');
|
||||
const { assertWithinTrustedRoot } = require('../path-safety');
|
||||
const {
|
||||
prepareLegacyAgentsMigration,
|
||||
removeLegacyAgentsFiles,
|
||||
} = require('./legacy-agents-migration');
|
||||
const {
|
||||
assertSafeClaudeSkillOperation,
|
||||
prepareClaudeSkillMigration,
|
||||
@@ -398,7 +402,10 @@ function prepareHookConsentMigration(plan, migration) {
|
||||
function previewInstallPlan(plan) {
|
||||
const migration = prepareHookConsentMigration(
|
||||
plan,
|
||||
prepareUserOwnedFileGuard(plan, prepareClaudeSkillMigration(plan))
|
||||
prepareUserOwnedFileGuard(
|
||||
plan,
|
||||
prepareLegacyAgentsMigration(plan, prepareClaudeSkillMigration(plan))
|
||||
)
|
||||
);
|
||||
const appliedPlan = {
|
||||
...plan,
|
||||
@@ -451,7 +458,10 @@ function applyInstallPlanLocked(plan, dependencies = {}, settingsLockHeld = fals
|
||||
}
|
||||
const migration = prepareHookConsentMigration(
|
||||
plan,
|
||||
prepareUserOwnedFileGuard(plan, prepareClaudeSkillMigration(plan))
|
||||
prepareUserOwnedFileGuard(
|
||||
plan,
|
||||
prepareLegacyAgentsMigration(plan, prepareClaudeSkillMigration(plan))
|
||||
)
|
||||
);
|
||||
const appliedPlan = {
|
||||
...plan,
|
||||
@@ -590,6 +600,7 @@ function applyInstallPlanLocked(plan, dependencies = {}, settingsLockHeld = fals
|
||||
if (hasLegacyMigration) {
|
||||
removeLegacyClaudeSkillFiles(migration, plan.targetRoot);
|
||||
}
|
||||
removeLegacyAgentsFiles(migration, plan.targetRoot);
|
||||
|
||||
if (shouldSetClaudeCommitAttributionPreference(appliedPlan)) {
|
||||
writeClaudeCommitAttributionPreference(
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const { readInstallState } = require('../install-state');
|
||||
const { assertWithinTrustedRoot } = require('../path-safety');
|
||||
|
||||
const HOME_TARGET_IDS = new Set(['claude-home', 'codex-home']);
|
||||
|
||||
function comparablePath(filePath) {
|
||||
const resolvedPath = path.resolve(filePath);
|
||||
return process.platform === 'win32' ? resolvedPath.toLowerCase() : resolvedPath;
|
||||
}
|
||||
|
||||
function normalizeRelativePath(relativePath) {
|
||||
return String(relativePath || '').replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
function isLegacyAgentsOperation(plan, operation) {
|
||||
if (
|
||||
!operation
|
||||
|| operation.kind !== 'copy-file'
|
||||
|| operation.ownership !== 'managed'
|
||||
|| typeof operation.destinationPath !== 'string'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sourceRelativePath = normalizeRelativePath(operation.sourceRelativePath);
|
||||
if (!sourceRelativePath.startsWith('.agents/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const expectedDestination = path.join(
|
||||
plan.targetRoot,
|
||||
...sourceRelativePath.split('/')
|
||||
);
|
||||
return comparablePath(operation.destinationPath) === comparablePath(expectedDestination);
|
||||
}
|
||||
|
||||
function inspectLegacyAgentsOperation(plan, operation) {
|
||||
assertWithinTrustedRoot(
|
||||
operation.destinationPath,
|
||||
plan.targetRoot,
|
||||
'inspect legacy managed .agents file'
|
||||
);
|
||||
|
||||
let stat;
|
||||
try {
|
||||
stat = fs.lstatSync(operation.destinationPath);
|
||||
} catch (error) {
|
||||
if (error && error.code === 'ENOENT') {
|
||||
return { removable: false, warning: null };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!stat.isFile() || stat.isSymbolicLink()) {
|
||||
return {
|
||||
removable: false,
|
||||
warning: `Preserved legacy .agents path ${operation.destinationPath}: it is not a regular file.`,
|
||||
};
|
||||
}
|
||||
|
||||
if (!/^[a-f0-9]{64}$/i.test(operation.contentSha256 || '')) {
|
||||
return {
|
||||
removable: false,
|
||||
warning: `Preserved legacy .agents file ${operation.destinationPath}: install-state has no verified content digest.`,
|
||||
};
|
||||
}
|
||||
|
||||
const contentSha256 = crypto.createHash('sha256')
|
||||
.update(fs.readFileSync(operation.destinationPath))
|
||||
.digest('hex');
|
||||
if (contentSha256 !== operation.contentSha256.toLowerCase()) {
|
||||
return {
|
||||
removable: false,
|
||||
warning: `Preserved modified legacy .agents file ${operation.destinationPath} and released it from ECC management.`,
|
||||
};
|
||||
}
|
||||
|
||||
return { removable: true, warning: null };
|
||||
}
|
||||
|
||||
function prepareLegacyAgentsMigration(plan, migration) {
|
||||
if (!HOME_TARGET_IDS.has(plan && plan.adapter && plan.adapter.id)) {
|
||||
return {
|
||||
...migration,
|
||||
legacyAgentsOperationsToDetach: [],
|
||||
legacyAgentsOperationsToRemove: [],
|
||||
};
|
||||
}
|
||||
|
||||
const previousState = fs.existsSync(plan.installStatePath)
|
||||
? readInstallState(plan.installStatePath)
|
||||
: null;
|
||||
const legacyOperations = ((previousState && previousState.operations) || [])
|
||||
.filter(operation => isLegacyAgentsOperation(plan, operation));
|
||||
if (legacyOperations.length === 0) {
|
||||
return {
|
||||
...migration,
|
||||
legacyAgentsOperationsToDetach: [],
|
||||
legacyAgentsOperationsToRemove: [],
|
||||
};
|
||||
}
|
||||
|
||||
const inspections = legacyOperations.map(operation => ({
|
||||
operation,
|
||||
...inspectLegacyAgentsOperation(plan, operation),
|
||||
}));
|
||||
const legacyDestinations = new Set(
|
||||
legacyOperations.map(operation => comparablePath(operation.destinationPath))
|
||||
);
|
||||
const withoutLegacyAgents = state => ({
|
||||
...state,
|
||||
operations: (state.operations || []).filter(operation => (
|
||||
!legacyDestinations.has(comparablePath(operation.destinationPath))
|
||||
)),
|
||||
});
|
||||
|
||||
return {
|
||||
...migration,
|
||||
finalState: withoutLegacyAgents(migration.finalState),
|
||||
legacyAgentsOperationsToDetach: legacyOperations,
|
||||
legacyAgentsOperationsToRemove: inspections
|
||||
.filter(inspection => inspection.removable)
|
||||
.map(inspection => inspection.operation),
|
||||
requiresBridgeState: true,
|
||||
warnings: [
|
||||
...(migration.warnings || []),
|
||||
...inspections.map(inspection => inspection.warning).filter(Boolean),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function cleanupEmptyAgentsParents(filePath, targetRoot) {
|
||||
const agentsRoot = path.join(targetRoot, '.agents');
|
||||
let currentPath = path.dirname(filePath);
|
||||
|
||||
while (comparablePath(currentPath) !== comparablePath(targetRoot)) {
|
||||
assertWithinTrustedRoot(currentPath, targetRoot, 'clean legacy managed .agents directory');
|
||||
if (!fs.existsSync(currentPath) || fs.readdirSync(currentPath).length > 0) {
|
||||
return;
|
||||
}
|
||||
fs.rmdirSync(currentPath);
|
||||
if (comparablePath(currentPath) === comparablePath(agentsRoot)) {
|
||||
return;
|
||||
}
|
||||
currentPath = path.dirname(currentPath);
|
||||
}
|
||||
}
|
||||
|
||||
function removeLegacyAgentsFiles(migration, targetRoot) {
|
||||
for (const operation of migration.legacyAgentsOperationsToRemove || []) {
|
||||
const latestInspection = inspectLegacyAgentsOperation(
|
||||
{ targetRoot },
|
||||
operation
|
||||
);
|
||||
if (!latestInspection.removable) {
|
||||
continue;
|
||||
}
|
||||
fs.rmSync(operation.destinationPath, { force: true });
|
||||
cleanupEmptyAgentsParents(operation.destinationPath, targetRoot);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
prepareLegacyAgentsMigration,
|
||||
removeLegacyAgentsFiles,
|
||||
};
|
||||
Reference in new issue
Block a user