mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-03-23 23:59:24 +08:00
feat(opencode): sync all providers to live config on directory change
Add additive mode support for OpenCode in sync_current_to_live: - Add AppType::is_additive_mode() to distinguish switch vs additive mode - Add AppType::all() iterator to avoid hardcoding app lists - Add sync_all_providers_to_live() for additive mode apps - Refactor sync_current_to_live to handle both modes Frontend changes (directory settings): - Track opencodeDirChanged in useDirectorySettings - Trigger syncCurrentProvidersLiveSafe when OpenCode dir changes - Add i18n strings for OpenCode directory settings
This commit is contained in:
@@ -282,6 +282,25 @@ impl AppType {
|
||||
AppType::OpenCode => "opencode",
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this app uses additive mode
|
||||
///
|
||||
/// - Switch mode (false): Only the current provider is written to live config (Claude, Codex, Gemini)
|
||||
/// - Additive mode (true): All providers are written to live config (OpenCode)
|
||||
pub fn is_additive_mode(&self) -> bool {
|
||||
matches!(self, AppType::OpenCode)
|
||||
}
|
||||
|
||||
/// Return an iterator over all app types
|
||||
pub fn all() -> impl Iterator<Item = AppType> {
|
||||
[
|
||||
AppType::Claude,
|
||||
AppType::Codex,
|
||||
AppType::Gemini,
|
||||
AppType::OpenCode,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for AppType {
|
||||
|
||||
@@ -182,33 +182,67 @@ pub(crate) fn write_live_snapshot(app_type: &AppType, provider: &Provider) -> Re
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sync all providers to live configuration (for additive mode apps)
|
||||
///
|
||||
/// Writes all providers from the database to the live configuration file.
|
||||
/// Used for OpenCode and other additive mode applications.
|
||||
fn sync_all_providers_to_live(state: &AppState, app_type: &AppType) -> Result<(), AppError> {
|
||||
let providers = state.db.get_all_providers(app_type.as_str())?;
|
||||
|
||||
for provider in providers.values() {
|
||||
if let Err(e) = write_live_snapshot(app_type, provider) {
|
||||
log::warn!(
|
||||
"Failed to sync {:?} provider '{}' to live: {e}",
|
||||
app_type,
|
||||
provider.id
|
||||
);
|
||||
// Continue syncing other providers, don't abort
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"Synced {} {:?} providers to live config",
|
||||
providers.len(),
|
||||
app_type
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sync current provider to live configuration
|
||||
///
|
||||
/// 使用有效的当前供应商 ID(验证过存在性)。
|
||||
/// 优先从本地 settings 读取,验证后 fallback 到数据库的 is_current 字段。
|
||||
/// 这确保了配置导入后无效 ID 会自动 fallback 到数据库。
|
||||
///
|
||||
/// For additive mode apps (OpenCode), all providers are synced instead of just the current one.
|
||||
pub fn sync_current_to_live(state: &AppState) -> Result<(), AppError> {
|
||||
for app_type in [AppType::Claude, AppType::Codex, AppType::Gemini] {
|
||||
// Use validated effective current provider
|
||||
let current_id =
|
||||
match crate::settings::get_effective_current_provider(&state.db, &app_type)? {
|
||||
Some(id) => id,
|
||||
None => continue,
|
||||
};
|
||||
// Sync providers based on mode
|
||||
for app_type in AppType::all() {
|
||||
if app_type.is_additive_mode() {
|
||||
// Additive mode: sync ALL providers
|
||||
sync_all_providers_to_live(state, &app_type)?;
|
||||
} else {
|
||||
// Switch mode: sync only current provider
|
||||
let current_id =
|
||||
match crate::settings::get_effective_current_provider(&state.db, &app_type)? {
|
||||
Some(id) => id,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let providers = state.db.get_all_providers(app_type.as_str())?;
|
||||
if let Some(provider) = providers.get(¤t_id) {
|
||||
write_live_snapshot(&app_type, provider)?;
|
||||
let providers = state.db.get_all_providers(app_type.as_str())?;
|
||||
if let Some(provider) = providers.get(¤t_id) {
|
||||
write_live_snapshot(&app_type, provider)?;
|
||||
}
|
||||
// Note: get_effective_current_provider already validates existence,
|
||||
// so providers.get() should always succeed here
|
||||
}
|
||||
// Note: get_effective_current_provider already validates existence,
|
||||
// so providers.get() should always succeed here
|
||||
}
|
||||
|
||||
// MCP sync
|
||||
McpService::sync_all_enabled(state)?;
|
||||
|
||||
// Skill sync
|
||||
for app_type in [AppType::Claude, AppType::Codex, AppType::Gemini] {
|
||||
for app_type in AppType::all() {
|
||||
if let Err(e) = crate::services::skill::SkillService::sync_to_app(&state.db, &app_type) {
|
||||
log::warn!("同步 Skill 到 {app_type:?} 失败: {e}");
|
||||
// Continue syncing other apps, don't abort
|
||||
|
||||
@@ -15,6 +15,7 @@ interface DirectorySettingsProps {
|
||||
claudeDir?: string;
|
||||
codexDir?: string;
|
||||
geminiDir?: string;
|
||||
opencodeDir?: string;
|
||||
onDirectoryChange: (app: AppId, value?: string) => void;
|
||||
onBrowseDirectory: (app: AppId) => Promise<void>;
|
||||
onResetDirectory: (app: AppId) => Promise<void>;
|
||||
@@ -29,6 +30,7 @@ export function DirectorySettings({
|
||||
claudeDir,
|
||||
codexDir,
|
||||
geminiDir,
|
||||
opencodeDir,
|
||||
onDirectoryChange,
|
||||
onBrowseDirectory,
|
||||
onResetDirectory,
|
||||
@@ -117,6 +119,17 @@ export function DirectorySettings({
|
||||
onBrowse={() => onBrowseDirectory("gemini")}
|
||||
onReset={() => onResetDirectory("gemini")}
|
||||
/>
|
||||
|
||||
<DirectoryInput
|
||||
label={t("settings.opencodeConfigDir")}
|
||||
description={undefined}
|
||||
value={opencodeDir}
|
||||
resolvedValue={resolvedDirs.opencode}
|
||||
placeholder={t("settings.browsePlaceholderOpencode")}
|
||||
onChange={(val) => onDirectoryChange("opencode", val)}
|
||||
onBrowse={() => onBrowseDirectory("opencode")}
|
||||
onReset={() => onResetDirectory("opencode")}
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -307,6 +307,7 @@ export function SettingsPage({
|
||||
claudeDir={settings.claudeConfigDir}
|
||||
codexDir={settings.codexConfigDir}
|
||||
geminiDir={settings.geminiConfigDir}
|
||||
opencodeDir={settings.opencodeConfigDir}
|
||||
onDirectoryChange={updateDirectory}
|
||||
onBrowseDirectory={browseDirectory}
|
||||
onResetDirectory={resetDirectory}
|
||||
|
||||
@@ -5,13 +5,14 @@ import { homeDir, join } from "@tauri-apps/api/path";
|
||||
import { settingsApi, type AppId } from "@/lib/api";
|
||||
import type { SettingsFormState } from "./useSettingsForm";
|
||||
|
||||
type DirectoryKey = "appConfig" | "claude" | "codex" | "gemini";
|
||||
type DirectoryKey = "appConfig" | "claude" | "codex" | "gemini" | "opencode";
|
||||
|
||||
export interface ResolvedDirectories {
|
||||
appConfig: string;
|
||||
claude: string;
|
||||
codex: string;
|
||||
gemini: string;
|
||||
opencode: string;
|
||||
}
|
||||
|
||||
const sanitizeDir = (value?: string | null): string | undefined => {
|
||||
@@ -39,7 +40,13 @@ const computeDefaultConfigDir = async (
|
||||
try {
|
||||
const home = await homeDir();
|
||||
const folder =
|
||||
app === "claude" ? ".claude" : app === "codex" ? ".codex" : ".gemini";
|
||||
app === "claude"
|
||||
? ".claude"
|
||||
: app === "codex"
|
||||
? ".codex"
|
||||
: app === "gemini"
|
||||
? ".gemini"
|
||||
: ".config/opencode";
|
||||
return await join(home, folder);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
@@ -70,6 +77,7 @@ export interface UseDirectorySettingsResult {
|
||||
claudeDir?: string,
|
||||
codexDir?: string,
|
||||
geminiDir?: string,
|
||||
opencodeDir?: string,
|
||||
) => void;
|
||||
}
|
||||
|
||||
@@ -96,6 +104,7 @@ export function useDirectorySettings({
|
||||
claude: "",
|
||||
codex: "",
|
||||
gemini: "",
|
||||
opencode: "",
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
@@ -104,6 +113,7 @@ export function useDirectorySettings({
|
||||
claude: "",
|
||||
codex: "",
|
||||
gemini: "",
|
||||
opencode: "",
|
||||
});
|
||||
const initialAppConfigDirRef = useRef<string | undefined>(undefined);
|
||||
|
||||
@@ -119,19 +129,23 @@ export function useDirectorySettings({
|
||||
claudeDir,
|
||||
codexDir,
|
||||
geminiDir,
|
||||
opencodeDir,
|
||||
defaultAppConfig,
|
||||
defaultClaudeDir,
|
||||
defaultCodexDir,
|
||||
defaultGeminiDir,
|
||||
defaultOpencodeDir,
|
||||
] = await Promise.all([
|
||||
settingsApi.getAppConfigDirOverride(),
|
||||
settingsApi.getConfigDir("claude"),
|
||||
settingsApi.getConfigDir("codex"),
|
||||
settingsApi.getConfigDir("gemini"),
|
||||
settingsApi.getConfigDir("opencode"),
|
||||
computeDefaultAppConfigDir(),
|
||||
computeDefaultConfigDir("claude"),
|
||||
computeDefaultConfigDir("codex"),
|
||||
computeDefaultConfigDir("gemini"),
|
||||
computeDefaultConfigDir("opencode"),
|
||||
]);
|
||||
|
||||
if (!active) return;
|
||||
@@ -143,6 +157,7 @@ export function useDirectorySettings({
|
||||
claude: defaultClaudeDir ?? "",
|
||||
codex: defaultCodexDir ?? "",
|
||||
gemini: defaultGeminiDir ?? "",
|
||||
opencode: defaultOpencodeDir ?? "",
|
||||
};
|
||||
|
||||
setAppConfigDir(normalizedOverride);
|
||||
@@ -153,6 +168,7 @@ export function useDirectorySettings({
|
||||
claude: claudeDir || defaultsRef.current.claude,
|
||||
codex: codexDir || defaultsRef.current.codex,
|
||||
gemini: geminiDir || defaultsRef.current.gemini,
|
||||
opencode: opencodeDir || defaultsRef.current.opencode,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
@@ -183,7 +199,9 @@ export function useDirectorySettings({
|
||||
? { claudeConfigDir: sanitized }
|
||||
: key === "codex"
|
||||
? { codexConfigDir: sanitized }
|
||||
: { geminiConfigDir: sanitized },
|
||||
: key === "gemini"
|
||||
? { geminiConfigDir: sanitized }
|
||||
: { opencodeConfigDir: sanitized },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -205,7 +223,13 @@ export function useDirectorySettings({
|
||||
const updateDirectory = useCallback(
|
||||
(app: AppId, value?: string) => {
|
||||
updateDirectoryState(
|
||||
app === "claude" ? "claude" : app === "codex" ? "codex" : "gemini",
|
||||
app === "claude"
|
||||
? "claude"
|
||||
: app === "codex"
|
||||
? "codex"
|
||||
: app === "gemini"
|
||||
? "gemini"
|
||||
: "opencode",
|
||||
value,
|
||||
);
|
||||
},
|
||||
@@ -215,13 +239,21 @@ export function useDirectorySettings({
|
||||
const browseDirectory = useCallback(
|
||||
async (app: AppId) => {
|
||||
const key: DirectoryKey =
|
||||
app === "claude" ? "claude" : app === "codex" ? "codex" : "gemini";
|
||||
app === "claude"
|
||||
? "claude"
|
||||
: app === "codex"
|
||||
? "codex"
|
||||
: app === "gemini"
|
||||
? "gemini"
|
||||
: "opencode";
|
||||
const currentValue =
|
||||
key === "claude"
|
||||
? (settings?.claudeConfigDir ?? resolvedDirs.claude)
|
||||
: key === "codex"
|
||||
? (settings?.codexConfigDir ?? resolvedDirs.codex)
|
||||
: (settings?.geminiConfigDir ?? resolvedDirs.gemini);
|
||||
: key === "gemini"
|
||||
? (settings?.geminiConfigDir ?? resolvedDirs.gemini)
|
||||
: (settings?.opencodeConfigDir ?? resolvedDirs.opencode);
|
||||
|
||||
try {
|
||||
const picked = await settingsApi.selectConfigDirectory(currentValue);
|
||||
@@ -263,7 +295,13 @@ export function useDirectorySettings({
|
||||
const resetDirectory = useCallback(
|
||||
async (app: AppId) => {
|
||||
const key: DirectoryKey =
|
||||
app === "claude" ? "claude" : app === "codex" ? "codex" : "gemini";
|
||||
app === "claude"
|
||||
? "claude"
|
||||
: app === "codex"
|
||||
? "codex"
|
||||
: app === "gemini"
|
||||
? "gemini"
|
||||
: "opencode";
|
||||
if (!defaultsRef.current[key]) {
|
||||
const fallback = await computeDefaultConfigDir(app);
|
||||
if (fallback) {
|
||||
@@ -292,7 +330,12 @@ export function useDirectorySettings({
|
||||
}, [updateDirectoryState]);
|
||||
|
||||
const resetAllDirectories = useCallback(
|
||||
(claudeDir?: string, codexDir?: string, geminiDir?: string) => {
|
||||
(
|
||||
claudeDir?: string,
|
||||
codexDir?: string,
|
||||
geminiDir?: string,
|
||||
opencodeDir?: string,
|
||||
) => {
|
||||
setAppConfigDir(initialAppConfigDirRef.current);
|
||||
setResolvedDirs({
|
||||
appConfig:
|
||||
@@ -300,6 +343,7 @@ export function useDirectorySettings({
|
||||
claude: claudeDir ?? defaultsRef.current.claude,
|
||||
codex: codexDir ?? defaultsRef.current.codex,
|
||||
gemini: geminiDir ?? defaultsRef.current.gemini,
|
||||
opencode: opencodeDir ?? defaultsRef.current.opencode,
|
||||
});
|
||||
},
|
||||
[],
|
||||
|
||||
@@ -109,6 +109,7 @@ export function useSettings(): UseSettingsResult {
|
||||
sanitizeDir(data?.claudeConfigDir),
|
||||
sanitizeDir(data?.codexConfigDir),
|
||||
sanitizeDir(data?.geminiConfigDir),
|
||||
sanitizeDir(data?.opencodeConfigDir),
|
||||
);
|
||||
setRequiresRestart(false);
|
||||
}, [
|
||||
@@ -131,12 +132,16 @@ export function useSettings(): UseSettingsResult {
|
||||
const sanitizedClaudeDir = sanitizeDir(mergedSettings.claudeConfigDir);
|
||||
const sanitizedCodexDir = sanitizeDir(mergedSettings.codexConfigDir);
|
||||
const sanitizedGeminiDir = sanitizeDir(mergedSettings.geminiConfigDir);
|
||||
const sanitizedOpencodeDir = sanitizeDir(
|
||||
mergedSettings.opencodeConfigDir,
|
||||
);
|
||||
|
||||
const payload: Settings = {
|
||||
...mergedSettings,
|
||||
claudeConfigDir: sanitizedClaudeDir,
|
||||
codexConfigDir: sanitizedCodexDir,
|
||||
geminiConfigDir: sanitizedGeminiDir,
|
||||
opencodeConfigDir: sanitizedOpencodeDir,
|
||||
language: mergedSettings.language,
|
||||
};
|
||||
|
||||
@@ -238,16 +243,21 @@ export function useSettings(): UseSettingsResult {
|
||||
const sanitizedClaudeDir = sanitizeDir(mergedSettings.claudeConfigDir);
|
||||
const sanitizedCodexDir = sanitizeDir(mergedSettings.codexConfigDir);
|
||||
const sanitizedGeminiDir = sanitizeDir(mergedSettings.geminiConfigDir);
|
||||
const sanitizedOpencodeDir = sanitizeDir(
|
||||
mergedSettings.opencodeConfigDir,
|
||||
);
|
||||
const previousAppDir = initialAppConfigDir;
|
||||
const previousClaudeDir = sanitizeDir(data?.claudeConfigDir);
|
||||
const previousCodexDir = sanitizeDir(data?.codexConfigDir);
|
||||
const previousGeminiDir = sanitizeDir(data?.geminiConfigDir);
|
||||
const previousOpencodeDir = sanitizeDir(data?.opencodeConfigDir);
|
||||
|
||||
const payload: Settings = {
|
||||
...mergedSettings,
|
||||
claudeConfigDir: sanitizedClaudeDir,
|
||||
codexConfigDir: sanitizedCodexDir,
|
||||
geminiConfigDir: sanitizedGeminiDir,
|
||||
opencodeConfigDir: sanitizedOpencodeDir,
|
||||
language: mergedSettings.language,
|
||||
};
|
||||
|
||||
@@ -344,11 +354,17 @@ export function useSettings(): UseSettingsResult {
|
||||
console.warn("[useSettings] Failed to refresh tray menu", error);
|
||||
}
|
||||
|
||||
// 如果 Claude/Codex/Gemini 的目录覆盖发生变化,则立即将“当前使用的供应商”写回对应应用的 live 配置
|
||||
// 如果 Claude/Codex/Gemini/OpenCode 的目录覆盖发生变化,则立即将"当前使用的供应商"写回对应应用的 live 配置
|
||||
const claudeDirChanged = sanitizedClaudeDir !== previousClaudeDir;
|
||||
const codexDirChanged = sanitizedCodexDir !== previousCodexDir;
|
||||
const geminiDirChanged = sanitizedGeminiDir !== previousGeminiDir;
|
||||
if (claudeDirChanged || codexDirChanged || geminiDirChanged) {
|
||||
const opencodeDirChanged = sanitizedOpencodeDir !== previousOpencodeDir;
|
||||
if (
|
||||
claudeDirChanged ||
|
||||
codexDirChanged ||
|
||||
geminiDirChanged ||
|
||||
opencodeDirChanged
|
||||
) {
|
||||
const syncResult = await syncCurrentProvidersLiveSafe();
|
||||
if (!syncResult.ok) {
|
||||
console.warn(
|
||||
|
||||
@@ -87,6 +87,8 @@ export function useSettingsForm(): UseSettingsFormResult {
|
||||
skipClaudeOnboarding: data.skipClaudeOnboarding ?? false,
|
||||
claudeConfigDir: sanitizeDir(data.claudeConfigDir),
|
||||
codexConfigDir: sanitizeDir(data.codexConfigDir),
|
||||
geminiConfigDir: sanitizeDir(data.geminiConfigDir),
|
||||
opencodeConfigDir: sanitizeDir(data.opencodeConfigDir),
|
||||
language: normalizedLanguage,
|
||||
};
|
||||
|
||||
@@ -143,6 +145,8 @@ export function useSettingsForm(): UseSettingsFormResult {
|
||||
skipClaudeOnboarding: serverData.skipClaudeOnboarding ?? false,
|
||||
claudeConfigDir: sanitizeDir(serverData.claudeConfigDir),
|
||||
codexConfigDir: sanitizeDir(serverData.codexConfigDir),
|
||||
geminiConfigDir: sanitizeDir(serverData.geminiConfigDir),
|
||||
opencodeConfigDir: sanitizeDir(serverData.opencodeConfigDir),
|
||||
language: normalizedLanguage,
|
||||
};
|
||||
|
||||
|
||||
@@ -327,9 +327,12 @@
|
||||
"codexConfigDirDescription": "Override Codex configuration directory.",
|
||||
"geminiConfigDir": "Gemini Configuration Directory",
|
||||
"geminiConfigDirDescription": "Override Gemini configuration directory (.env).",
|
||||
"opencodeConfigDir": "OpenCode Configuration Directory",
|
||||
"opencodeConfigDirDescription": "Override OpenCode configuration directory (opencode.json).",
|
||||
"browsePlaceholderClaude": "e.g., /home/<your-username>/.claude",
|
||||
"browsePlaceholderCodex": "e.g., /home/<your-username>/.codex",
|
||||
"browsePlaceholderGemini": "e.g., /home/<your-username>/.gemini",
|
||||
"browsePlaceholderOpencode": "e.g., /home/<your-username>/.config/opencode",
|
||||
"browseDirectory": "Browse Directory",
|
||||
"resetDefault": "Reset to default directory (takes effect after saving)",
|
||||
"checkForUpdates": "Check for Updates",
|
||||
|
||||
@@ -327,9 +327,12 @@
|
||||
"codexConfigDirDescription": "Codex の設定ディレクトリを上書きします。",
|
||||
"geminiConfigDir": "Gemini 設定ディレクトリ",
|
||||
"geminiConfigDirDescription": "Gemini の設定ディレクトリ(.env)を上書きします。",
|
||||
"opencodeConfigDir": "OpenCode 設定ディレクトリ",
|
||||
"opencodeConfigDirDescription": "OpenCode の設定ディレクトリ(opencode.json)を上書きします。",
|
||||
"browsePlaceholderClaude": "例: /home/<your-username>/.claude",
|
||||
"browsePlaceholderCodex": "例: /home/<your-username>/.codex",
|
||||
"browsePlaceholderGemini": "例: /home/<your-username>/.gemini",
|
||||
"browsePlaceholderOpencode": "例: /home/<your-username>/.config/opencode",
|
||||
"browseDirectory": "ディレクトリを選択",
|
||||
"resetDefault": "デフォルトに戻す(保存後に反映)",
|
||||
"checkForUpdates": "アップデートを確認",
|
||||
|
||||
@@ -327,9 +327,12 @@
|
||||
"codexConfigDirDescription": "覆盖 Codex 配置目录。",
|
||||
"geminiConfigDir": "Gemini 配置目录",
|
||||
"geminiConfigDirDescription": "覆盖 Gemini 配置目录 (.env)。",
|
||||
"opencodeConfigDir": "OpenCode 配置目录",
|
||||
"opencodeConfigDirDescription": "覆盖 OpenCode 配置目录 (opencode.json)。",
|
||||
"browsePlaceholderClaude": "例如:/home/<你的用户名>/.claude",
|
||||
"browsePlaceholderCodex": "例如:/home/<你的用户名>/.codex",
|
||||
"browsePlaceholderGemini": "例如:/home/<你的用户名>/.gemini",
|
||||
"browsePlaceholderOpencode": "例如:/home/<你的用户名>/.config/opencode",
|
||||
"browseDirectory": "浏览目录",
|
||||
"resetDefault": "恢复默认目录(需保存后生效)",
|
||||
"checkForUpdates": "检查更新",
|
||||
|
||||
@@ -64,9 +64,12 @@ describe("useDirectorySettings", () => {
|
||||
);
|
||||
|
||||
getAppConfigDirOverrideMock.mockResolvedValue(null);
|
||||
getConfigDirMock.mockImplementation(async (app: string) =>
|
||||
app === "claude" ? "/remote/claude" : "/remote/codex",
|
||||
);
|
||||
getConfigDirMock.mockImplementation(async (app: string) => {
|
||||
if (app === "claude") return "/remote/claude";
|
||||
if (app === "codex") return "/remote/codex";
|
||||
if (app === "gemini") return "/remote/gemini";
|
||||
return "/remote/opencode";
|
||||
});
|
||||
selectConfigDirectoryMock.mockReset();
|
||||
});
|
||||
|
||||
@@ -84,7 +87,8 @@ describe("useDirectorySettings", () => {
|
||||
appConfig: "/override/app",
|
||||
claude: "/remote/claude",
|
||||
codex: "/remote/codex",
|
||||
gemini: "/remote/codex", // Gemini 使用 codex 作为默认
|
||||
gemini: "/remote/gemini",
|
||||
opencode: "/remote/opencode",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -214,10 +218,17 @@ describe("useDirectorySettings", () => {
|
||||
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
||||
|
||||
act(() => {
|
||||
result.current.resetAllDirectories("/server/claude", "/server/codex");
|
||||
result.current.resetAllDirectories(
|
||||
"/server/claude",
|
||||
"/server/codex",
|
||||
"/server/gemini",
|
||||
"/server/opencode",
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.resolvedDirs.claude).toBe("/server/claude");
|
||||
expect(result.current.resolvedDirs.codex).toBe("/server/codex");
|
||||
expect(result.current.resolvedDirs.gemini).toBe("/server/gemini");
|
||||
expect(result.current.resolvedDirs.opencode).toBe("/server/opencode");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -381,6 +381,7 @@ describe("useSettings hook", () => {
|
||||
"/server/claude",
|
||||
undefined,
|
||||
undefined, // geminiConfigDir
|
||||
undefined, // opencodeConfigDir
|
||||
);
|
||||
expect(metadataMock.setRequiresRestart).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user