mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat(grokbuild): add first-class Grok Build support (#5453)
* feat(grokbuild): add backend integration * feat(grokbuild): add provider and management UI * test(grokbuild): cover configuration and integrations * fix(grokbuild): address backend review feedback * fix(grokbuild): complete UI review feedback * feat(grokbuild): add CLI lifecycle management * fix(grokbuild): align provider icon fallback * test(grokbuild): cover provider icon persistence
This commit is contained in:
@@ -6,6 +6,14 @@ use cc_switch_lib::AppType;
|
||||
fn parse_known_apps_case_insensitive_and_trim() {
|
||||
assert!(matches!(AppType::from_str("claude"), Ok(AppType::Claude)));
|
||||
assert!(matches!(AppType::from_str("codex"), Ok(AppType::Codex)));
|
||||
assert!(matches!(
|
||||
AppType::from_str("grokbuild"),
|
||||
Ok(AppType::GrokBuild)
|
||||
));
|
||||
assert!(matches!(
|
||||
AppType::from_str("Grok-Build"),
|
||||
Ok(AppType::GrokBuild)
|
||||
));
|
||||
assert!(matches!(
|
||||
AppType::from_str(" ClAuDe \n"),
|
||||
Ok(AppType::Claude)
|
||||
|
||||
@@ -748,6 +748,7 @@ command = "echo"
|
||||
claude: false,
|
||||
codex: false, // 初始未启用
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -877,6 +878,7 @@ fn import_from_claude_merges_into_config() {
|
||||
claude: false, // 初始未启用
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
|
||||
@@ -227,6 +227,7 @@ command = "echo"
|
||||
claude: false,
|
||||
codex: true,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -371,6 +372,7 @@ fn set_mcp_enabled_for_codex_writes_live_config() {
|
||||
claude: false,
|
||||
codex: false, // 初始未启用
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -436,6 +438,7 @@ fn enabling_codex_mcp_skips_when_codex_dir_missing() {
|
||||
claude: false,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -481,6 +484,7 @@ fn upsert_mcp_server_disabling_app_removes_from_claude_live_config() {
|
||||
claude: true,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -515,6 +519,7 @@ fn upsert_mcp_server_disabling_app_removes_from_claude_live_config() {
|
||||
claude: false,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -648,6 +653,7 @@ fn enabling_gemini_mcp_skips_when_gemini_dir_missing() {
|
||||
claude: false,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -703,6 +709,7 @@ fn enabling_claude_mcp_skips_when_claude_config_absent() {
|
||||
claude: false,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -758,6 +765,7 @@ fn explicit_default_claude_dir_keeps_default_split_mcp_path() {
|
||||
claude: true,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -814,6 +822,7 @@ fn custom_claude_dir_writes_mcp_inside_config_dir() {
|
||||
claude: true,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -893,6 +902,7 @@ fn custom_claude_dir_sync_does_not_copy_default_profile() {
|
||||
claude: true,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -1032,6 +1042,7 @@ fn sync_all_enabled_removes_known_disabled_but_preserves_unknown_live_entries()
|
||||
claude: false,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -1054,6 +1065,7 @@ fn sync_all_enabled_removes_known_disabled_but_preserves_unknown_live_entries()
|
||||
claude: true,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
|
||||
@@ -19,6 +19,81 @@ fn settings_path(home: &Path) -> PathBuf {
|
||||
home.join(".cc-switch").join("settings.json")
|
||||
}
|
||||
|
||||
fn grokbuild_config(name: &str, endpoint: &str, api_key: &str) -> String {
|
||||
format!(
|
||||
r#"[models]
|
||||
default = "grok-4.5"
|
||||
|
||||
[model."grok-4.5"]
|
||||
model = "grok-4.5"
|
||||
base_url = "{endpoint}"
|
||||
name = "{name}"
|
||||
api_key = "{api_key}"
|
||||
api_backend = "responses"
|
||||
context_window = 500000
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grokbuild_import_and_switch_write_live_config() {
|
||||
let _guard = test_mutex().lock().expect("acquire test mutex");
|
||||
reset_test_fs();
|
||||
let home = ensure_test_home();
|
||||
let live_path = home.join(".grok").join("config.toml");
|
||||
std::fs::create_dir_all(live_path.parent().expect("grok config dir"))
|
||||
.expect("create grok config dir");
|
||||
let imported_config = grokbuild_config("Imported", "https://old.example/v1", "old-key");
|
||||
std::fs::write(&live_path, &imported_config).expect("seed Grok Build config");
|
||||
|
||||
let state = create_test_state().expect("create test state");
|
||||
import_default_config_test_hook(&state, AppType::GrokBuild)
|
||||
.expect("import Grok Build default provider");
|
||||
|
||||
let imported = state
|
||||
.db
|
||||
.get_provider_by_id("default", AppType::GrokBuild.as_str())
|
||||
.expect("query imported provider")
|
||||
.expect("imported provider exists");
|
||||
assert_eq!(
|
||||
imported
|
||||
.settings_config
|
||||
.get("config")
|
||||
.and_then(|value| value.as_str()),
|
||||
Some(imported_config.as_str())
|
||||
);
|
||||
|
||||
let next_config = grokbuild_config("Relay", "https://new.example/v1", "new-key");
|
||||
state
|
||||
.db
|
||||
.save_provider(
|
||||
AppType::GrokBuild.as_str(),
|
||||
&Provider::with_id(
|
||||
"relay".to_string(),
|
||||
"Relay".to_string(),
|
||||
json!({ "config": next_config }),
|
||||
None,
|
||||
),
|
||||
)
|
||||
.expect("save second Grok Build provider");
|
||||
|
||||
switch_provider_test_hook(&state, AppType::GrokBuild, "relay")
|
||||
.expect("switch Grok Build provider");
|
||||
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(&live_path).expect("read switched Grok Build config"),
|
||||
next_config
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.db
|
||||
.get_current_provider(AppType::GrokBuild.as_str())
|
||||
.expect("read Grok Build current provider")
|
||||
.as_deref(),
|
||||
Some("relay")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_startup_import_fresh_install_imports_once_and_syncs_current_setting() {
|
||||
let _guard = test_mutex().lock().expect("acquire test mutex");
|
||||
@@ -300,6 +375,7 @@ command = "say"
|
||||
claude: false,
|
||||
codex: true, // 启用 Codex
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
|
||||
@@ -164,6 +164,7 @@ command = "say"
|
||||
claude: false,
|
||||
codex: true,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -934,6 +935,7 @@ fn reapply_codex_official_live_resyncs_mcp_servers() {
|
||||
claude: false,
|
||||
codex: true,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -1032,6 +1034,7 @@ fn reapply_codex_official_live_projects_mcp_despite_broken_claude_json() {
|
||||
claude: false,
|
||||
codex: true,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -1124,6 +1127,7 @@ fn switch_codex_projects_mcp_despite_broken_claude_json() {
|
||||
claude: false,
|
||||
codex: true,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
@@ -1187,6 +1191,7 @@ fn sync_all_enabled_reports_broken_app_but_projects_the_rest() {
|
||||
claude: false,
|
||||
codex: true,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
hermes: false,
|
||||
},
|
||||
|
||||
@@ -31,6 +31,7 @@ pub fn reset_test_fs() {
|
||||
".codex",
|
||||
".cc-switch",
|
||||
".gemini",
|
||||
".grok",
|
||||
".config",
|
||||
".openclaw",
|
||||
"profiles",
|
||||
|
||||
Reference in New Issue
Block a user