mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 01:25:33 +08:00
feat(grokbuild): add Grok Official provider with official-state import
Add a "Grok Official" preset and seed (grokbuild-official) whose empty config represents the official login state: no custom [model.*] tables are written, so Grok CLI falls back to its built-in xAI OAuth login and cc-switch never touches those credentials. Backend: - Seed entry in OFFICIAL_SEEDS plus ensure_grokbuild_official_provider command for on-demand repair (the one-shot master seeding flag is already set for existing databases). - Split validation into syntax-only (empty allowed) for live reads, writes and official snapshots, keeping the full custom-model shape check for non-official provider writes and imports. Backup/restore can now round-trip an official-state live file. - Manual import (command layer only) recognizes an official-state live config and imports it as the official entry set as current, matching the Codex official-login import outcome. Startup auto-import keeps rejecting official-state live so a deleted official entry is never resurrected on launch: startup import only captures real user data as "default" and never manufactures official entries. - Manual import also ensures the official entry before importing (claude-desktop precedent) and after a successful custom import, so first-time users end up with default + official like other apps. - Proxy takeover guards skip or reject official-state live configs in all three takeover paths, consistent with the official-provider takeover ban. Frontend: - Grok Official preset entry in the GrokBuild form: official category hides connection fields and passes the raw config through untouched. - Filter managed-OAuth presets out of the GrokBuild preset list; they were never wired for this app and produced keyless broken configs. Tests cover seed presence, official round-trip, ensure-after-deletion, and the four import scenarios including startup non-resurrection.
This commit is contained in:
@@ -712,6 +712,7 @@ mod ensure_official_seed_tests {
|
||||
use crate::app_config::AppType;
|
||||
use crate::database::{
|
||||
Database, CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID, CODEX_OFFICIAL_PROVIDER_ID,
|
||||
GROKBUILD_OFFICIAL_PROVIDER_ID,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -790,6 +791,26 @@ mod ensure_official_seed_tests {
|
||||
assert_eq!(provider.settings_config["auth"], serde_json::json!({}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_recreates_grokbuild_official_seed_after_deletion() {
|
||||
let db = Database::memory().expect("memory db");
|
||||
db.init_default_official_providers().expect("seed");
|
||||
db.delete_provider(AppType::GrokBuild.as_str(), GROKBUILD_OFFICIAL_PROVIDER_ID)
|
||||
.expect("delete Grok Build official");
|
||||
|
||||
let inserted = db
|
||||
.ensure_official_seed_by_id(GROKBUILD_OFFICIAL_PROVIDER_ID, AppType::GrokBuild)
|
||||
.expect("ensure Grok Build official");
|
||||
assert!(inserted);
|
||||
let provider = db
|
||||
.get_provider_by_id(GROKBUILD_OFFICIAL_PROVIDER_ID, AppType::GrokBuild.as_str())
|
||||
.expect("query")
|
||||
.expect("Grok Build official restored");
|
||||
assert_eq!(provider.category.as_deref(), Some("official"));
|
||||
// 空 config:切换时不注入自定义模型表,Grok CLI 回落到自带 OAuth 登录
|
||||
assert_eq!(provider.settings_config["config"], serde_json::json!(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_rejects_unknown_seed() {
|
||||
let db = Database::memory().expect("memory db");
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
//! - `src/config/claudeProviderPresets.ts`("Claude Official")
|
||||
//! - `src/config/codexProviderPresets.ts`("OpenAI Official")
|
||||
//! - `src/config/geminiProviderPresets.ts`("Google Official")
|
||||
//! - `src/components/providers/forms/GrokBuildProviderForm.tsx`("Grok Official")
|
||||
|
||||
use crate::app_config::AppType;
|
||||
|
||||
pub(crate) const CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID: &str = "claude-desktop-official";
|
||||
pub(crate) const CODEX_OFFICIAL_PROVIDER_ID: &str = "codex-official";
|
||||
pub(crate) const GROKBUILD_OFFICIAL_PROVIDER_ID: &str = "grokbuild-official";
|
||||
|
||||
/// 单条官方供应商种子定义。
|
||||
pub(crate) struct OfficialProviderSeed {
|
||||
@@ -69,6 +71,16 @@ pub(crate) const OFFICIAL_SEEDS: &[OfficialProviderSeed] = &[
|
||||
// 空 env + 空 config 让用户走 Google OAuth
|
||||
settings_config_json: r#"{"env":{},"config":{}}"#,
|
||||
},
|
||||
OfficialProviderSeed {
|
||||
id: GROKBUILD_OFFICIAL_PROVIDER_ID,
|
||||
app_type: AppType::GrokBuild,
|
||||
name: "Grok Official",
|
||||
website_url: "https://x.ai/grok",
|
||||
icon: "grok",
|
||||
icon_color: "currentColor",
|
||||
// 空 config = 不写自定义模型表,Grok CLI 回落到自带的 xAI OAuth 登录
|
||||
settings_config_json: r#"{"config":""}"#,
|
||||
},
|
||||
];
|
||||
|
||||
/// 判断给定的 provider id 是否属于内置官方种子。
|
||||
@@ -92,4 +104,17 @@ mod tests {
|
||||
assert_eq!(seed.app_type, AppType::ClaudeDesktop);
|
||||
assert!(is_official_seed_id(CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn official_seeds_include_grokbuild() {
|
||||
let seed = OFFICIAL_SEEDS
|
||||
.iter()
|
||||
.find(|seed| seed.id == GROKBUILD_OFFICIAL_PROVIDER_ID)
|
||||
.expect("grok build official seed");
|
||||
|
||||
assert_eq!(seed.app_type, AppType::GrokBuild);
|
||||
assert!(is_official_seed_id(GROKBUILD_OFFICIAL_PROVIDER_ID));
|
||||
// 空 config = 官方登录态:切换时不注入自定义模型表
|
||||
assert_eq!(seed.settings_config_json, r#"{"config":""}"#);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ mod tests;
|
||||
// DAO 类型导出供外部使用
|
||||
pub(crate) use dao::providers_seed::{
|
||||
is_official_seed_id, CLAUDE_DESKTOP_OFFICIAL_PROVIDER_ID, CODEX_OFFICIAL_PROVIDER_ID,
|
||||
GROKBUILD_OFFICIAL_PROVIDER_ID,
|
||||
};
|
||||
pub(crate) use dao::proxy::{
|
||||
validate_cost_multiplier, validate_pricing_source, PRICING_SOURCE_REQUEST,
|
||||
|
||||
Reference in New Issue
Block a user