mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-14 02:15:36 +00:00
fix(websearch): harden settings-profile hook setup
- fail enabled settings-profile create and launch flows when hook preparation is still unusable - refresh stale shared hook binaries without rewriting unchanged installs or failing benign races - add rollback and regression coverage for disabled, stale, invalid, copy, import, and launch paths
This commit is contained in:
1 parent
fbbdd80830
commit
0e6965d205
12 files changed
+549
-46
No files matched your search
@@ -9,7 +9,7 @@ import * as path from 'path';
|
||||
import type { Config, Settings } from '../../types';
|
||||
import type { TargetType } from '../../targets/target-adapter';
|
||||
import { getCcsDir, getConfigPath, loadConfigSafe } from '../../utils/config-manager';
|
||||
import { ensureProfileHooks } from '../../utils/websearch/profile-hook-injector';
|
||||
import { ensureProfileHooksOrThrow } from '../../utils/websearch/profile-hook-injector';
|
||||
import { isSensitiveKey } from '../../utils/sensitive-keys';
|
||||
import { isReservedName } from '../../config/reserved-names';
|
||||
import { isUnifiedMode, mutateUnifiedConfig } from '../../config/unified-config-loader';
|
||||
@@ -216,8 +216,8 @@ export function registerApiProfileOrphans(options?: {
|
||||
}
|
||||
|
||||
try {
|
||||
ensureProfileHooksOrThrow(orphan.name);
|
||||
registerApiProfileInConfig(orphan.name, options?.target || 'claude', options?.force || false);
|
||||
ensureProfileHooks(orphan.name);
|
||||
result.registered.push(orphan.name);
|
||||
} catch (error) {
|
||||
result.skipped.push({ name: orphan.name, reason: (error as Error).message });
|
||||
@@ -264,7 +264,12 @@ export function copyApiProfile(
|
||||
: null;
|
||||
|
||||
writeJsonObjectAtomically(destinationSettingsPath, sourceSettings);
|
||||
ensureProfileHooks(destination);
|
||||
try {
|
||||
ensureProfileHooksOrThrow(destination);
|
||||
} catch (hookError) {
|
||||
rollbackSettingsFile(destinationSettingsPath, previousDestinationContent, destinationExisted);
|
||||
throw hookError;
|
||||
}
|
||||
try {
|
||||
registerApiProfileInConfig(
|
||||
destination,
|
||||
@@ -384,7 +389,12 @@ export function importApiProfileBundle(
|
||||
const previousSettingsContent = settingsExisted ? fs.readFileSync(settingsPath, 'utf8') : null;
|
||||
|
||||
writeJsonObjectAtomically(settingsPath, settings);
|
||||
ensureProfileHooks(name);
|
||||
try {
|
||||
ensureProfileHooksOrThrow(name);
|
||||
} catch (hookError) {
|
||||
rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted);
|
||||
throw hookError;
|
||||
}
|
||||
try {
|
||||
registerApiProfileInConfig(name, options?.target || bundleTarget || 'claude', options?.force);
|
||||
} catch (registrationError) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getCcsDir, getConfigPath, loadConfigSafe } from '../../utils/config-man
|
||||
import { expandPath } from '../../utils/helpers';
|
||||
import { validateApiName } from './validation-service';
|
||||
import { mutateUnifiedConfig, isUnifiedMode } from '../../config/unified-config-loader';
|
||||
import { ensureProfileHooks } from '../../utils/websearch/profile-hook-injector';
|
||||
import { ensureProfileHooksOrThrow } from '../../utils/websearch/profile-hook-injector';
|
||||
import type { TargetType } from '../../targets/target-adapter';
|
||||
import { resolveDroidProvider } from '../../targets/droid-provider';
|
||||
import { isReservedName } from '../../config/reserved-names';
|
||||
@@ -69,6 +69,21 @@ function getDeniedModelReason(baseUrl: string, models: ModelMapping): string | n
|
||||
return null;
|
||||
}
|
||||
|
||||
function rollbackSettingsFile(
|
||||
filePath: string,
|
||||
previousContent: string | null,
|
||||
existedBefore: boolean
|
||||
): void {
|
||||
if (existedBefore && previousContent !== null) {
|
||||
fs.writeFileSync(filePath, previousContent, 'utf8');
|
||||
return;
|
||||
}
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/** Create settings.json file for API profile (legacy format) */
|
||||
function createSettingsFile(
|
||||
name: string,
|
||||
@@ -105,11 +120,18 @@ function createSettingsFile(
|
||||
},
|
||||
};
|
||||
|
||||
const settingsExisted = fs.existsSync(settingsPath);
|
||||
const previousSettingsContent = settingsExisted ? fs.readFileSync(settingsPath, 'utf8') : null;
|
||||
fs.mkdirSync(ccsDir, { recursive: true });
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
||||
|
||||
// Inject WebSearch hooks into profile settings
|
||||
ensureProfileHooks(name);
|
||||
try {
|
||||
// Inject WebSearch hooks into profile settings
|
||||
ensureProfileHooksOrThrow(name);
|
||||
} catch (error) {
|
||||
rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return settingsPath;
|
||||
}
|
||||
@@ -189,10 +211,17 @@ function createApiProfileUnified(
|
||||
fs.mkdirSync(ccsDir, { recursive: true });
|
||||
}
|
||||
|
||||
const settingsExisted = fs.existsSync(settingsPath);
|
||||
const previousSettingsContent = settingsExisted ? fs.readFileSync(settingsPath, 'utf8') : null;
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
||||
|
||||
// Inject WebSearch hooks into profile settings
|
||||
ensureProfileHooks(name);
|
||||
try {
|
||||
// Inject WebSearch hooks into profile settings
|
||||
ensureProfileHooksOrThrow(name);
|
||||
} catch (error) {
|
||||
rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted);
|
||||
throw error;
|
||||
}
|
||||
|
||||
mutateUnifiedConfig((config) => {
|
||||
config.profiles[name] = {
|
||||
|
||||
Reference in new issue
Block a user