fix(websearch): avoid partial settings-hook migration

- verify the hook binary before migrating global WebSearch hook state

- centralize migration-marker helpers in hook-installer exports

- expand regression coverage for existing-hook, disabled, invalid-name, and failed-install paths
This commit is contained in:
Tam Nhu Tran committed 2026-03-27 17:22:56 -04:00
1 parent 39a3e9dfc0
commit fbbdd80830
5 files changed
+127 -39

No files matched your search

+2 -1
View File
@@ -46,6 +46,7 @@ export {
hasWebSearchHook,
getWebSearchHookConfig,
installWebSearchHook,
removeMigrationMarker,
uninstallWebSearchHook,
} from './websearch/hook-installer';
@@ -62,7 +63,7 @@ export {
} from './websearch/status';
// Re-export profile hook injection
export { ensureProfileHooks, removeMigrationMarker } from './websearch/profile-hook-injector';
export { ensureProfileHooks } from './websearch/profile-hook-injector';
// Import for local use
import { clearGeminiCliCache, clearGrokCliCache, clearOpenCodeCliCache } from './websearch';
+1 -1
View File
@@ -19,7 +19,7 @@ export { getHookPath, getWebSearchHookConfig } from './hook-config';
// Hook file name
const WEBSEARCH_HOOK = 'websearch-transformer.cjs';
function getMigrationMarkerPath(): string {
export function getMigrationMarkerPath(): string {
return path.join(getCcsDir(), '.hook-migrated');
}
+2 -1
View File
@@ -40,6 +40,7 @@ export {
hasWebSearchHook,
getWebSearchHookConfig,
installWebSearchHook,
removeMigrationMarker,
uninstallWebSearchHook,
} from './hook-installer';
@@ -61,4 +62,4 @@ export {
export { WEBSEARCH_API_KEY_PROVIDERS, getWebSearchApiKeyStates } from './provider-secrets';
// Profile Hook Injection
export { ensureProfileHooks, removeMigrationMarker } from './profile-hook-injector';
export { ensureProfileHooks } from './profile-hook-injector';
+9 -32
View File
@@ -15,18 +15,11 @@ import { getWebSearchConfig } from '../../config/unified-config-loader';
import { removeHookConfig } from './hook-config';
import { getCcsDir } from '../config-manager';
import { isCcsWebSearchHook, deduplicateCcsHooks } from './hook-utils';
import { installWebSearchHook } from './hook-installer';
import { getMigrationMarkerPath, installWebSearchHook } from './hook-installer';
// Valid profile name pattern (alphanumeric, dash, underscore only)
const VALID_PROFILE_NAME = /^[a-zA-Z0-9_-]+$/;
/**
* Get migration marker path (respects CCS_HOME for test isolation)
*/
function getMigrationMarkerPath(): string {
return path.join(getCcsDir(), '.hook-migrated');
}
/**
* Check if CCS WebSearch hook exists in settings
*/
@@ -90,6 +83,14 @@ export function ensureProfileHooks(profileName: string): boolean {
return false;
}
// Keep the injected command target valid for all profile types, not just CLIProxy.
if (!installWebSearchHook() && !fs.existsSync(getHookPath())) {
if (process.env.CCS_DEBUG) {
console.error(warn('WebSearch hook binary is missing and could not be installed'));
}
return false;
}
// One-time migration from global settings
migrateGlobalHook();
@@ -101,14 +102,6 @@ export function ensureProfileHooks(profileName: string): boolean {
fs.mkdirSync(ccsDir, { recursive: true, mode: 0o700 });
}
// Keep the injected command target valid for all profile types, not just CLIProxy.
if (!installWebSearchHook() && !fs.existsSync(getHookPath())) {
if (process.env.CCS_DEBUG) {
console.error(warn('WebSearch hook binary is missing and could not be installed'));
}
return false;
}
const settingsPath = path.join(ccsDir, `${profileName}.settings.json`);
// Read existing settings or create empty
@@ -243,19 +236,3 @@ function updateHookTimeoutIfNeeded(
return false;
}
}
/**
* Remove migration marker (called during uninstall)
*/
export function removeMigrationMarker(): void {
try {
const markerPath = getMigrationMarkerPath();
if (fs.existsSync(markerPath)) {
fs.unlinkSync(markerPath);
}
} catch (error) {
if (process.env.CCS_DEBUG) {
console.error(warn(`removeMigrationMarker failed: ${(error as Error).message}`));
}
}
}