Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/1jehuang_jcode.json
T

1 line
1.0 MiB
Plaintext

{"repo": "1jehuang/jcode", "files": {"crates/jcode-base/src/provider_catalog.rs": {"d6c7c36d61fb266c4e4ccfac0d7b710d3f50f444": {"t": 1785595235.8975751, "n": 35081, "h": "507b7be45609d4b2", "c": "pub use jcode_provider_env::{\n load_api_key_from_env_or_config, load_env_value_from_config_file,\n load_env_value_from_env_or_config, register_api_key_fallback_resolver,\n save_env_value_to_env_file,\n};\npub use jcode_provider_metadata::*;\nuse std::collections::{HashMap, HashSet};\n\npub const OPENAI_COMPAT_LOCAL_ENABLED_ENV: &str = \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\";\npub const MINIMAX_CHINA_API_BASE: &str = \"https://api.minimaxi.com/v1\";\npub const MINIMAX_CHINA_SETUP_URL: &str = \"https://platform.minimaxi.com/docs/llms.txt\";\n\npub fn api_base_uses_localhost(raw: &str) -> bool {\n let Ok(parsed) = url::Url::parse(raw) else {\n return false;\n };\n\n matches!(\n parsed\n .host_str()\n .map(|host| host.to_ascii_lowercase())\n .as_deref(),\n Some(\"localhost\") | Some(\"127.0.0.1\") | Some(\"::1\")\n )\n}\n\npub fn resolve_openai_compatible_profile(\n profile: OpenAiCompatibleProfile,\n) -> ResolvedOpenAiCompatibleProfile {\n resolve_openai_compatible_profile_with_api_key_hint(profile, None)\n}\n\npub fn resolve_openai_compatible_profile_with_api_key_hint(\n profile: OpenAiCompatibleProfile,\n api_key_hint: Option<&str>,\n) -> ResolvedOpenAiCompatibleProfile {\n let mut resolved = ResolvedOpenAiCompatibleProfile {\n id: profile.id.to_string(),\n display_name: profile.display_name.to_string(),\n api_base: profile.api_base.to_string(),\n api_key_env: profile.api_key_env.to_string(),\n env_file: profile.env_file.to_string(),\n setup_url: profile.setup_url.to_string(),\n default_model: profile.default_model.map(ToString::to_string),\n requires_api_key: profile.requires_api_key,\n };\n\n apply_profile_key_based_endpoint_overrides(profile, &mut resolved, api_key_hint);\n\n if profile.id != OPENAI_COMPAT_PROFILE.id {\n if let Some(newest_model) =\n newest_released_model_for_resolved_openai_compatible_profile(profile.id, &resolved)\n {\n resolved.default_model = Some(newest_model);\n }\n return resolved;\n }\n\n if let Some(base) = env_override(\"JCODE_OPENAI_COMPAT_API_BASE\") {\n if let Some(normalized) = normalize_api_base(&base) {\n resolved.api_base = normalized;\n } else {\n eprintln!(\n \"Warning: ignoring invalid JCODE_OPENAI_COMPAT_API_BASE '{}'. Use https://... (or http://localhost).\",\n base\n );\n }\n }\n\n if let Some(key_name) = env_override(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\") {\n if is_safe_env_key_name(&key_name) {\n resolved.api_key_env = key_name;\n } else {\n eprintln!(\n \"Warning: ignoring invalid JCODE_OPENAI_COMPAT_API_KEY_NAME '{}'.\",\n key_name\n );\n }\n }\n\n if let Some(env_file) = env_override(\"JCODE_OPENAI_COMPAT_ENV_FILE\") {\n if is_safe_env_file_name(&env_file) {\n resolved.env_file = env_file;\n } else {\n eprintln!(\n \"Warning: ignoring invalid JCODE_OPENAI_COMPAT_ENV_FILE '{}'.\",\n env_file\n );\n }\n }\n\n if let Some(setup_url) = env_override(\"JCODE_OPENAI_COMPAT_SETUP_URL\") {\n resolved.setup_url = setup_url;\n }\n\n if let Some(model) = env_override(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\") {\n resolved.default_model = Some(model);\n }\n\n if api_base_uses_localhost(&resolved.api_base) {\n resolved.requires_api_key = false;\n }\n\n resolved\n}\n\npub fn newest_released_model_for_openai_compatible_profile(profile_id: &str) -> Option<String> {\n let profile = openai_compatible_profile_by_id(profile_id)?;\n let resolved = resolve_openai_compatible_profile(profile);\n newest_released_model_for_resolved_openai_compatible_profile(profile_id, &resolved)\n}\n\nfn newest_released_model_for_resolved_openai_compatible_profile(\n profile_id: &str,\n resolved: &ResolvedOpenAiCompatibleProfile,\n) -> Option<String> {\n openai_compatible_profile_by_id(profile_id)?;\n let cache = jcode_provider_openrouter::load_disk_cache_entry_for_namespace(&resolved.id)?;\n\n let source_matches = cache\n .source_api_base\n .as_deref()\n .and_then(normalize_api_base)\n == normalize_api_base(&resolved.api_base);\n if !source_matches {\n return None;\n }\n\n cache\n .models\n .into_iter()\n .enumerate()\n .filter_map(|(index, model)| {\n let id = model.id.trim().to_string();\n let created = model.created?;\n // Never auto-select an obviously non-chat model (TTS/speech/embeddings/\n // rerankers/image/etc.) as a profile's default. Catalogs like Groq,\n // NVIDIA NIM, and Chutes expose their entire model list, and the\n // newest-released entry is frequently a non-chat model (e.g. Groq's\n // `canopylabs/orpheus-*` TTS), which must not become the chat default.\n if id.is_empty() || !crate::provider::is_listable_model_name(&id) {\n return None;\n }\n let tier = openai_compatible_model_quality_tier(&id);\n Some((tier, created, std::cmp::Reverse(index), id))\n })\n // Pick the strongest tier first, then the newest within that tier, then\n // catalog order. Ranking by quality *before* recency stops a brand-new\n // cheap/small model (e.g. a freshly released `*-flash`/`*-mini`) from\n // becoming the default of a heterogeneous proxy catalog (OpenCode Zen,\n // Groq, ...) when a stronger sibling is available.\n .max_by_key(|(tier, created, reverse_index, _)| (*tier, *created, *reverse_index))\n .map(|(_, _, _, id)| id)\n}\n\n/// Cheap/small/fast tier markers. A model id whose tokens include one of these\n/// is a cheaper or smaller variant that must not become a profile's default\n/// while a stronger sibling exists. Matched on whole tokens (split on\n/// non-alphanumeric) so brand names like `minimax` are not mistaken for\n/// `mini`/`max`.\nconst OPENAI_COMPAT_CHEAP_TIER_MARKERS: &[&str] = &[\n \"mini\",\n \"nano\",\n \"lite\",\n \"small\",\n \"tiny\",\n \"flash\",\n \"instant\",\n \"air\",\n \"micro\",\n \"lightning\",\n \"haiku\",\n \"scout\",\n \"edge\",\n \"lowcost\",\n];\n\n/// Flagship/strong tier markers. A model id whose tokens include one of these\n/// advertises itself as a provider's top-tier or specialized-strong model.\nconst OPENAI_COMPAT_FLAGSHIP_TIER_MARKERS: &[&str] = &[\n \"opus\", \"max\", \"ultra\", \"pro\", \"plus\", \"large\", \"coder\", \"code\", \"reasoner\", \"405b\", \"480b\",\n \"235b\", \"671b\", \"256b\",\n];\n\n/// Coarse cross-vendor quality tier for an openai-compatible catalog model id\n/// (higher is stronger): `2` = flagship-marked, `0` = cheap/small-marked, `1`\n/// otherwise (a bare frontier id like `gpt-5.5` or `minimax-m2.7`).\n///\n/// This relies on the near-universal naming convention that cheaper variants\n/// carry a size/speed marker (`mini`, `flash`, `air`, ...) and flagship variants\n/// carry a top-tier marker (`max`, `pro`, `opus`, `coder`, ...). It is a coarse\n/// heuristic only used to break the \"newest model\" tie sensibly; the user can\n/// always override the selected model.\nfn openai_compatible_model_quality_tier(model_id: &str) -> u8 {\n let lower = model_id.to_ascii_lowercase();\n let tokens: Vec<&str> = lower\n .split(|c: char| !c.is_ascii_alphanumeric())\n .filter(|t| !t.is_empty())\n .collect();\n let has = |markers: &[&str]| markers.iter().any(|marker| tokens.contains(marker));\n // Flagship markers win when both are present (e.g. `qwen3-coder-30b`): a\n // strong-specialization signal outweighs a size token.\n if has(OPENAI_COMPAT_FLAGSHIP_TIER_MARKERS) {\n return 2;\n }\n if has(OPENAI_COMPAT_CHEAP_TIER_MARKERS) {\n return 0;\n }\n 1\n}\n\nfn apply_profile_key_based_endpoint_overrides(\n profile: OpenAiCompatibleProfile,\n resolved: &mut ResolvedOpenAiCompatibleProfile,\n api_key_hint: Option<&str>,\n) {\n if profile.id != MINIMAX_PROFILE.id {\n return;\n }\n\n let key = api_key_hint\n .map(str::trim)\n .filter(|key| !key.is_empty())\n .map(ToString::to_string)\n .or_else(|| load_env_value_from_env_or_config(profile.api_key_env, profile.env_file));\n\n if key\n .as_deref()\n .map(|key| key.trim_start().starts_with(\"sk-cp-\"))\n .unwrap_or(false)\n {\n resolved.api_base = MINIMAX_CHINA_API_BASE.to_string();\n resolved.setup_url = MINIMAX_CHINA_SETUP_URL.to_string();\n }\n}\n\npub fn resolve_openai_compatible_profile_selection(input: &str) -> Option<OpenAiCompatibleProfile> {\n let provider = resolve_login_provider(input)?;\n match provider.target {\n LoginProviderTarget::OpenAiCompatible(profile) => Some(profile),\n _ => None,\n }\n}\n\npub fn active_openai_compatible_display_name() -> Option<String> {\n if let Ok(profile_name) = std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\") {\n let trimmed = profile_name.trim();\n if !trimmed.is_empty() {\n return Some(trimmed.to_string());\n }\n }\n\n if let Ok(namespace) = std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\") {\n let trimmed = namespace.trim();\n if let Some(profile) = openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| profile.id == trimmed)\n {\n return Some(profile.display_name.to_string());\n }\n }\n\n let api_base = std::env::var(\"JCODE_OPENROUTER_API_BASE\")\n .ok()\n .map(|value| value.trim().to_string())\n .filter(|value| !value.is_empty())\n .or_else(|| env_override(\"JCODE_OPENAI_COMPAT_API_BASE\"));\n\n let api_base = api_base.and_then(|value| normalize_api_base(&value))?;\n\n for profile in openai_compatible_profiles().iter().copied() {\n if normalize_api_base(profile.api_base).as_deref() == Some(api_base.as_str()) {\n return Some(profile.display_name.to_string());\n }\n }\n\n if !api_base.contains(\"openrouter.ai\") {\n return Some(\"OpenAI-compatible\".to_string());\n }\n\n None\n}\n\npub fn runtime_provider_display_name(provider_name: &str) -> String {\n if provider_name.eq_ignore_ascii_case(\"openrouter\") {\n if let Ok(runtime_provider) = std::env::var(\"JCODE_RUNTIME_PROVIDER\")\n && runtime_provider.trim().eq_ignore_ascii_case(\"azure-openai\")\n {\n return \"Azure OpenAI\".to_string();\n }\n\n active_openai_compatible_display_name().unwrap_or_else(|| \"OpenRouter\".to_string())\n } else {\n provider_name.to_string()\n }\n}\n\npub fn openai_compatible_profile_by_id(id: &str) -> Option<OpenAiCompatibleProfile> {\n let normalized = id.trim().to_ascii_lowercase();\n openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| profile.id == normalized)\n}\n\npub fn openai_compatible_profile_id_for_api_base(api_base: &str) -> Option<&'static str> {\n let normalized = normalize_api_base(api_base)?;\n openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| {\n normalize_api_base(profile.api_base).as_deref() == Some(normalized.as_str())\n })\n .map(|profile| profile.id)\n}\n\npub fn openai_compatible_profile_id_for_display_name(display_name: &str) -> Option<&'static str> {\n let normalized = display_name.trim().to_ascii_lowercase();\n openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| {\n profile.id == normalized\n || profile\n .display_name\n .eq_ignore_ascii_case(display_name.trim())\n })\n .map(|profile| profile.id)\n}\n\npub fn openai_compatible_profile_static_models(profile: OpenAiCompatibleProfile) -> Vec<String> {\n let mut models = Vec::new();\n let mut push = |model: &str| {\n let model = model.trim();\n if !model.is_empty() && !models.iter().any(|existing| existing == model) {\n models.push(model.to_string());\n }\n };\n\n match profile.id {\n \"opencode\" => {\n push(\"minimax-m2.7\");\n push(\"kimi-k2.5\");\n push(\"glm-4.7\");\n push(\"glm-5\");\n push(\"claude-haiku-4-5\");\n push(\"gpt-5.1-codex-max\");\n }\n \"opencode-go\" => {\n push(\"minimax-m2.7\");\n push(\"kimi-k2.5\");\n push(\"glm-5\");\n push(\"glm-5.1\");\n push(\"deepseek-v4-flash\");\n push(\"qwen3.5-plus\");\n }\n \"zai\" => {\n push(\"glm-4.5\");\n push(\"glm-4.7\");\n push(\"glm-5\");\n push(\"glm-5.1\");\n push(\"glm-4.7-flash\");\n push(\"glm-4.7-flashx\");\n }\n \"302ai\" => {\n push(\"qwen3-235b-a22b-instruct-2507\");\n push(\"glm-4.7\");\n push(\"glm-5.1\");\n push(\"MiniMax-M2\");\n push(\"kimi-k2-0905-preview\");\n push(\"claude-haiku-4-5\");\n }\n \"baseten\" => {\n push(\"zai-org/GLM-4.7\");\n push(\"zai-org/GLM-5\");\n push(\"openai/gpt-oss-120b\");\n push(\"moonshotai/Kimi-K2.6\");\n push(\"moonshotai/Kimi-K2.5\");\n push(\"deepseek-ai/DeepSeek-V4-Pro\");\n }\n \"cortecs\" => {\n push(\"minimax-m2.7\");\n push(\"kimi-k2.5\");\n push(\"glm-4.7\");\n push(\"glm-5\");\n push(\"claude-haiku-4-5\");\n push(\"qwen3-235b-a22b-instruct-2507\");\n }\n // Issue #79: DeepSeek's live model catalog is not always available during\n // TUI startup, but both models should still be selectable once the direct\n // provider is configured.\n \"deepseek\" => {\n push(\"deepseek-v4-flash\");\n push(\"deepseek-v4-pro\");\n }\n \"comtegra\" => {\n push(\"gpt-oss-120b\");\n push(\"qwen35-122b\");\n push(\"gte-qwen2-7b\");\n push(\"glm-51-nvfp4\");\n }\n \"fpt\" => {\n push(\"GLM-5.1\");\n push(\"GLM-4.7\");\n push(\"Llama-3.3-70B-Instruct\");\n }\n \"kimi\" => {\n push(\"kimi-for-coding\");\n push(\"kimi-k2.5\");\n push(\"kimi-k2.6\");\n push(\"kimi-k2-thinking\");\n push(\"kimi-k2-thinking-turbo\");\n }\n \"firmware\" => {\n push(\"kimi-k2.5\");\n push(\"zai-glm-5-1\");\n push(\"claude-haiku-4-5\");\n push(\"claude-sonnet-4-6\");\n push(\"grok-code-fast-1\");\n push(\"gemini-2.5-flash\");\n }\n \"huggingface\" => {\n push(\"Qwen/Qwen3-Coder-480B-A35B-Instruct\");\n push(\"Qwen/Qwen3-Coder-Next\");\n push(\"zai-org/GLM-4.7\");\n push(\"zai-org/GLM-5.1\");\n push(\"deepseek-ai/DeepSeek-V3.2\");\n push(\"openai/gpt-oss-120b\");\n }\n \"moonshotai\" => {\n push(\"kimi-k2.5\");\n push(\"kimi-k2.6\");\n push(\"kimi-k2-thinking\");\n push(\"kimi-k2-thinking-turbo\");\n push(\"kimi-k2-turbo-preview\");\n }\n \"nebius\" => {\n push(\"openai/gpt-oss-120b\");\n push(\"Qwen/Qwen3-235B-A22B-Instruct-2507\");\n push(\"Qwen/Qwen3.5-397B-A17B\");\n push(\"zai-org/GLM-5\");\n push(\"meta-llama/Llama-3.3-70B-Instruct\");\n push(\"NousResearch/Hermes-4-70B\");\n }\n \"scaleway\" => {\n push(\"qwen3-coder-30b-a3b-instruct\");\n push(\"qwen3-235b-a22b-instruct-2507\");\n push(\"qwen3.5-397b-a17b\");\n push(\"gpt-oss-120b\");\n push(\"mistral-small-3.2-24b-instruct-2506\");\n push(\"llama-3.3-70b-instruct\");\n }\n \"stackit\" => {\n push(\"openai/gpt-oss-120b\");\n push(\"Qwen/Qwen3-VL-235B-A22B-Instruct-FP8\");\n push(\"cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic\");\n push(\"neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8\");\n push(\"google/gemma-3-27b-it\");\n }\n \"perplexity\" => {\n push(\"sonar\");\n push(\"sonar-pro\");\n push(\"sonar-reasoning-pro\");\n push(\"sonar-deep-research\");\n }\n \"deepinfra\" => {\n push(\"moonshotai/Kimi-K2-Instruct\");\n push(\"Qwen/Qwen3-Coder-480B-A35B-Instruct\");\n push(\"Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo\");\n push(\"zai-org/GLM-4.7\");\n push(\"zai-org/GLM-5.1\");\n push(\"meta-llama/Llama-3.1-70B-Instruct\");\n }\n \"fireworks\" => {\n push(\"accounts/fireworks/routers/kimi-k2p5-turbo\");\n push(\"accounts/fireworks/models/kimi-k2p5\");\n push(\"accounts/fireworks/models/kimi-k2p6\");\n push(\"accounts/fireworks/models/glm-4p7\");\n push(\"accounts/fireworks/models/glm-5p1\");\n push(\"accounts/fireworks/models/deepseek-v3p2\");\n }\n \"cerebras\" => {\n push(\"gpt-oss-120b\");\n push(\"zai-glm-4.7\");\n }\n // Celeris serves exactly one model per base URL today, and `/models`\n // requires auth, so keep the documented id available pre-refresh.\n \"celeris\" => {\n push(\"celeris-1\");\n }\n \"xiaomi-mimo\" => {\n push(\"mimo-v2.5\");\n push(\"mimo-v2.5-pro\");\n push(\"mimo-v2-pro\");\n push(\"mimo-v2-flash\");\n push(\"mimo-v2-omni\");\n }\n // MiniMax's `/models` endpoint is authenticated and live, but post-login\n // model activation should not depend on the catalog refresh completing\n // before the picker/routes are rebuilt. Keep the documented text models\n // selectable immediately after saving a key.\n \"minimax\" => {\n push(\"MiniMax-M2.7\");\n push(\"MiniMax-M2.7-highspeed\");\n push(\"MiniMax-M2.5\");\n push(\"MiniMax-M2.5-highspeed\");\n push(\"MiniMax-M2.1\");\n push(\"MiniMax-M2.1-highspeed\");\n push(\"MiniMax-M2\");\n }\n \"alibaba-coding-plan\" => {\n push(\"qwen3-coder-plus\");\n push(\"qwen3.5-plus\");\n push(\"qwen3-max-2026-01-23\");\n push(\"qwen3-coder-next\");\n push(\"glm-5\");\n push(\"glm-4.7\");\n push(\"kimi-k2.5\");\n push(\"MiniMax-M2.5\");\n }\n \"gemini-api\" => {\n push(\"gemini-2.5-flash\");\n push(\"gemini-2.5-pro\");\n push(\"gemini-2.0-flash\");\n push(\"gemini-2.0-flash-lite\");\n }\n _ => {}\n }\n\n models\n}\n\npub fn openai_compatible_profile_model_supports_chat(_profile_id: &str, _model: &str) -> bool {\n true\n}\n\npub fn openai_compatible_profile_static_context_limits(\n profile: OpenAiCompatibleProfile,\n) -> HashMap<String, usize> {\n openai_compatible_profile_static_models(profile)\n .into_iter()\n .filter_map(|model| {\n openai_compatible_profile_context_limit(profile.id, &model).map(|limit| (model, limit))\n })\n .collect()\n}\n\npub fn openai_compatible_profile_context_limit(profile_id: &str, model: &str) -> Option<usize> {\n let profile_id = profile_id.trim().to_ascii_lowercase();\n let model = model.trim().to_ascii_lowercase();\n\n match profile_id.as_str() {\n // DeepSeek V4 direct API models advertise a 1M token context window. The\n // direct profile runs through the OpenRouter/OpenAI-compatible provider\n // implementation, whose live catalog can be unavailable during startup.\n \"deepseek\" if model.starts_with(\"deepseek-v4-\") => Some(1_000_000),\n // Fall back to the shared open-weight family classifier. Many bundled\n // OpenAI-compatible gateways (Z.AI/GLM, Moonshot/Kimi, MiniMax, Qwen,\n // etc.) serve `/v1/models` entries without a `context_length`, so this\n // static table is the only reliable source before a live catalog (or an\n // explicit user `context_window` override) is available.\n _ => jcode_provider_core::models::open_weight_family_context_limit(&model),\n }\n}\n\npub fn apply_openai_compatible_profile_env(profile: Option<OpenAiCompatibleProfile>) {\n apply_openai_compatible_profile_env_impl(profile, true);\n}\n\npub fn force_apply_openai_compatible_profile_env(profile: Option<OpenAiCompatibleProfile>) {\n apply_openai_compatible_profile_env_impl(profile, false);\n}\n\nfn apply_openai_compatible_profile_env_impl(\n profile: Option<OpenAiCompatibleProfile>,\n respect_named_profile_lock: bool,\n) {\n if respect_named_profile_lock && std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_some() {\n return;\n }\n\n let vars = [\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ];\n\n for var in vars {\n crate::env::remove_var(var);\n }\n\n if let Some(profile) = profile {\n let resolved = resolve_openai_compatible_profile(profile);\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", &resolved.api_base);\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", &resolved.api_key_env);\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", &resolved.env_file);\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", &resolved.id);\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"0\");\n let static_models = openai_compatible_profile_static_models(profile);\n if static_models.is_empty() {\n crate::env::remove_var(\"JCODE_OPENROUTER_STATIC_MODELS\");\n } else {\n crate::env::set_var(\"JCODE_OPENROUTER_STATIC_MODELS\", static_models.join(\"\\n\"));\n }\n if resolved.requires_api_key {\n crate::env::remove_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-api-key\");\n } else {\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-no-auth\");\n }\n }\n}\n\nfn inline_key_env_name(profile_name: &str) -> String {\n let suffix = profile_name\n .chars()\n .map(|ch| {\n if ch.is_ascii_alphanumeric() {\n ch.to_ascii_uppercase()\n } else {\n '_'\n }\n })\n .collect::<String>();\n format!(\"JCODE_PROVIDER_{}_API_KEY\", suffix)\n}\n\npub fn apply_named_provider_profile_env(profile_name: &str) -> anyhow::Result<String> {\n let config = crate::config::Config::load_strict()?;\n apply_named_provider_profile_env_from_config(profile_name, &config)\n}\n\npub fn apply_named_provider_profile_env_from_config(\n profile_name: &str,\n config: &crate::config::Config,\n) -> anyhow::Result<String> {\n let Some(profile) = config.providers.get(profile_name) else {\n anyhow::bail!(\n \"Unknown provider profile '{}'. Add [providers.{}] to config.toml.\",\n profile_name,\n profile_name\n );\n };\n\n let api_base = normalize_api_base(&profile.base_url).ok_or_else(|| {\n anyhow::anyhow!(\n \"Provider profile '{}' has invalid base_url '{}'. Use https://... or http://localhost.\",\n profile_name,\n profile.base_url\n )\n })?;\n\n crate::env::remove_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\");\n crate::env::remove_var(\"JCODE_PROVIDER_PROFILE_NAME\");\n crate::env::remove_var(\"JCODE_NAMED_PROVIDER_PROFILE\");\n apply_openai_compatible_profile_env(None);\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", &api_base);\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", profile_name);\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", profile_name);\n let provider_is_openrouter = matches!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenRouter\n );\n\n let provider_features =\n provider_is_openrouter || profile.provider_routing || profile.allow_provider_pinning;\n crate::env::set_var(\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n if provider_features { \"1\" } else { \"0\" },\n );\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n if profile.model_catalog\n || matches!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenRouter\n )\n {\n \"1\"\n } else {\n \"0\"\n },\n );\n\n if let Some(model) = profile\n .default_model\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n {\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", model);\n }\n\n let static_models = profile\n .models\n .iter()\n .map(|model| model.id.trim())\n .filter(|id| !id.is_empty())\n .collect::<Vec<_>>();\n if !static_models.is_empty() {\n crate::env::set_var(\"JCODE_OPENROUTER_STATIC_MODELS\", static_models.join(\"\\n\"));\n }\n\n match profile.auth {\n crate::config::NamedProviderAuth::None => {\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-no-auth\");\n }\n crate::config::NamedProviderAuth::Bearer | crate::config::NamedProviderAuth::Header => {\n let key_env = profile\n .api_key_env\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n .map(ToString::to_string)\n .or_else(|| {\n profile.api_key.as_deref().map(str::trim).filter(|v| !v.is_empty()).map(|key| {\n let env_name = inline_key_env_name(profile_name);\n crate::env::set_var(&env_name, key);\n crate::logging::warn(&format!(\n \"Provider profile '{}' stores an inline API key in config.toml. Prefer api_key_env to avoid accidental leaks.\",\n profile_name\n ));\n env_name\n })\n });\n\n if let Some(key_env) = key_env {\n if !is_safe_env_key_name(&key_env) {\n anyhow::bail!(\n \"Provider profile '{}' has invalid api_key_env '{}'.\",\n profile_name,\n key_env\n );\n }\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", &key_env);\n }\n\n if let Some(env_file) = profile\n .env_file\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n {\n if !is_safe_env_file_name(env_file) {\n anyhow::bail!(\n \"Provider profile '{}' has invalid env_file '{}'.\",\n profile_name,\n env_file\n );\n }\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", env_file);\n }\n\n let requires_key = profile\n .requires_api_key\n .unwrap_or(!api_base_uses_localhost(&api_base));\n if !requires_key {\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-no-auth\");\n } else if provider_is_openrouter {\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"openrouter-api-key\");\n } else {\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-api-key\");\n }\n\n match profile.auth {\n crate::config::NamedProviderAuth::Bearer => {\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"bearer\");\n }\n crate::config::NamedProviderAuth::Header => {\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"api-key\");\n if let Some(header) = profile\n .auth_header\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n {\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", header);\n }\n }\n crate::config::NamedProviderAuth::None => {}\n }\n }\n }\n\n Ok(profile_name.to_string())\n}\n\npub fn openrouter_like_api_key_sources() -> Vec<(String, String)> {\n let mut sources = Vec::with_capacity(10);\n sources.push((\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string(),\n ));\n\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n sources.push((\n profile.api_key_env.to_string(),\n profile.env_file.to_string(),\n ));\n }\n }\n\n if let Some(source) = configured_api_key_source(\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"OPENROUTER_API_KEY\",\n \"openrouter.env\",\n ) {\n sources.push(source);\n }\n\n if let Some(source) = configured_api_key_source(\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n OPENAI_COMPAT_PROFILE.api_key_env,\n OPENAI_COMPAT_PROFILE.env_file,\n ) {\n sources.push(source);\n }\n\n dedup_sources(sources)\n}\n\nfn parse_bool_like(value: &str) -> bool {\n matches!(\n value.trim().to_ascii_lowercase().as_str(),\n \"1\" | \"true\" | \"yes\" | \"on\"\n )\n}\n\npub fn openai_compatible_profile_is_configured(profile: OpenAiCompatibleProfile) -> bool {\n // When a named config profile (`[providers.<name>]`, selected via\n // `--provider-profile`) is active, its credentials live under the runtime\n // env vars set by `apply_named_provider_profile_env`, not the built-in\n // `openai-compatible.env`. Honor those first so auth-test does not report a\n // false `not_configured` for a correctly-configured named profile (#402).\n if let Some(configured) = active_named_provider_profile_is_configured() {\n return configured;\n }\n\n let resolved = resolve_openai_compatible_profile(profile);\n if load_api_key_from_env_or_config(&resolved.api_key_env, &resolved.env_file).is_some() {\n return true;\n }\n\n if resolved.requires_api_key {\n return false;\n }\n\n if profile.id == OPENAI_COMPAT_PROFILE.id && api_base_uses_localhost(&resolved.api_base) {\n return true;\n }\n\n load_env_value_from_env_or_config(OPENAI_COMPAT_LOCAL_ENABLED_ENV, &resolved.env_file)\n .map(|value| parse_bool_like(&value))\n .unwrap_or(false)\n}\n\n/// Resolve the active named provider profile's credential env var + env file,\n/// as set by [`apply_named_provider_profile_env`], if one is active.\n///\n/// Returns `(api_key_env, env_file)` describing where to look for the key.\npub fn active_named_provider_profile_credential_source() -> Option<(String, String)> {\n // Presence of this var marks an active named profile (set by\n // `apply_named_provider_profile_env`).\n let _profile_name = std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())?;\n\n let key_env = std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| \"OPENROUTER_API_KEY\".to_string());\n let env_file = std::env::var(\"JCODE_OPENROUTER_ENV_FILE\")\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| \"openrouter.env\".to_string());\n Some((key_env, env_file))\n}\n\n/// Whether the active named provider profile has credentials available.\n///\n/// Returns `None` when no named profile is active (so callers fall back to the\n/// built-in profile checks), `Some(true)` when the profile is no-auth or has a\n/// key, and `Some(false)` when a key is required but missing.\nfn active_named_provider_profile_is_configured() -> Option<bool> {\n let (key_env, env_file) = active_named_provider_profile_credential_source()?;\n\n // A no-auth profile (localhost or explicit allow-no-auth) is configured.\n if std::env::var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\")\n .map(|v| parse_bool_like(&v))\n .unwrap_or(false)\n {\n return Some(true);\n }\n\n Some(load_api_key_from_env_or_config(&key_env, &env_file).is_some())\n}\n\npub fn configured_api_key_source(\n key_var: &str,\n file_var: &str,\n default_key: &str,\n default_file: &str,\n) -> Option<(String, String)> {\n if std::env::var_os(key_var).is_none() && std::env::var_os(file_var).is_none() {\n return None;\n }\n\n let env_key = std::env::var(key_var)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| default_key.to_string());\n let file_name = std::env::var(file_var)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| default_file.to_string());\n\n if !is_safe_env_key_name(&env_key) {\n crate::logging::warn(&format!(\n \"Ignoring invalid {}='{}' while probing auth status\",\n key_var, env_key\n ));\n return None;\n }\n if !is_safe_env_file_name(&file_name) {\n crate::logging::warn(&format!(\n \"Ignoring invalid {}='{}' while probing auth status\",\n file_var, file_name\n ));\n return None;\n }\n\n Some((env_key, file_name))\n}\n\nfn env_override(name: &str) -> Option<String> {\n std::env::var(name)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .or_else(|| load_env_value_from_env_or_config(name, OPENAI_COMPAT_PROFILE.env_file))\n}\n\nfn dedup_sources(sources: Vec<(String, String)>) -> Vec<(String, String)> {\n let mut seen = HashSet::new();\n let mut deduped = Vec::with_capacity(sources.len());\n for (env_key, env_file) in sources {\n if seen.insert((env_key.clone(), env_file.clone())) {\n deduped.push((env_key, env_file));\n }\n }\n deduped\n}\n\n#[cfg(test)]\n#[path = \"provider_catalog_tests.rs\"]\nmod provider_catalog_tests;\n"}, "eca1793180e9f2afaccac22b296d0eee84cc3577": {"t": 1785846811.1291447, "n": 35081, "h": "507b7be45609d4b2", "c": "pub use jcode_provider_env::{\n load_api_key_from_env_or_config, load_env_value_from_config_file,\n load_env_value_from_env_or_config, register_api_key_fallback_resolver,\n save_env_value_to_env_file,\n};\npub use jcode_provider_metadata::*;\nuse std::collections::{HashMap, HashSet};\n\npub const OPENAI_COMPAT_LOCAL_ENABLED_ENV: &str = \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\";\npub const MINIMAX_CHINA_API_BASE: &str = \"https://api.minimaxi.com/v1\";\npub const MINIMAX_CHINA_SETUP_URL: &str = \"https://platform.minimaxi.com/docs/llms.txt\";\n\npub fn api_base_uses_localhost(raw: &str) -> bool {\n let Ok(parsed) = url::Url::parse(raw) else {\n return false;\n };\n\n matches!(\n parsed\n .host_str()\n .map(|host| host.to_ascii_lowercase())\n .as_deref(),\n Some(\"localhost\") | Some(\"127.0.0.1\") | Some(\"::1\")\n )\n}\n\npub fn resolve_openai_compatible_profile(\n profile: OpenAiCompatibleProfile,\n) -> ResolvedOpenAiCompatibleProfile {\n resolve_openai_compatible_profile_with_api_key_hint(profile, None)\n}\n\npub fn resolve_openai_compatible_profile_with_api_key_hint(\n profile: OpenAiCompatibleProfile,\n api_key_hint: Option<&str>,\n) -> ResolvedOpenAiCompatibleProfile {\n let mut resolved = ResolvedOpenAiCompatibleProfile {\n id: profile.id.to_string(),\n display_name: profile.display_name.to_string(),\n api_base: profile.api_base.to_string(),\n api_key_env: profile.api_key_env.to_string(),\n env_file: profile.env_file.to_string(),\n setup_url: profile.setup_url.to_string(),\n default_model: profile.default_model.map(ToString::to_string),\n requires_api_key: profile.requires_api_key,\n };\n\n apply_profile_key_based_endpoint_overrides(profile, &mut resolved, api_key_hint);\n\n if profile.id != OPENAI_COMPAT_PROFILE.id {\n if let Some(newest_model) =\n newest_released_model_for_resolved_openai_compatible_profile(profile.id, &resolved)\n {\n resolved.default_model = Some(newest_model);\n }\n return resolved;\n }\n\n if let Some(base) = env_override(\"JCODE_OPENAI_COMPAT_API_BASE\") {\n if let Some(normalized) = normalize_api_base(&base) {\n resolved.api_base = normalized;\n } else {\n eprintln!(\n \"Warning: ignoring invalid JCODE_OPENAI_COMPAT_API_BASE '{}'. Use https://... (or http://localhost).\",\n base\n );\n }\n }\n\n if let Some(key_name) = env_override(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\") {\n if is_safe_env_key_name(&key_name) {\n resolved.api_key_env = key_name;\n } else {\n eprintln!(\n \"Warning: ignoring invalid JCODE_OPENAI_COMPAT_API_KEY_NAME '{}'.\",\n key_name\n );\n }\n }\n\n if let Some(env_file) = env_override(\"JCODE_OPENAI_COMPAT_ENV_FILE\") {\n if is_safe_env_file_name(&env_file) {\n resolved.env_file = env_file;\n } else {\n eprintln!(\n \"Warning: ignoring invalid JCODE_OPENAI_COMPAT_ENV_FILE '{}'.\",\n env_file\n );\n }\n }\n\n if let Some(setup_url) = env_override(\"JCODE_OPENAI_COMPAT_SETUP_URL\") {\n resolved.setup_url = setup_url;\n }\n\n if let Some(model) = env_override(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\") {\n resolved.default_model = Some(model);\n }\n\n if api_base_uses_localhost(&resolved.api_base) {\n resolved.requires_api_key = false;\n }\n\n resolved\n}\n\npub fn newest_released_model_for_openai_compatible_profile(profile_id: &str) -> Option<String> {\n let profile = openai_compatible_profile_by_id(profile_id)?;\n let resolved = resolve_openai_compatible_profile(profile);\n newest_released_model_for_resolved_openai_compatible_profile(profile_id, &resolved)\n}\n\nfn newest_released_model_for_resolved_openai_compatible_profile(\n profile_id: &str,\n resolved: &ResolvedOpenAiCompatibleProfile,\n) -> Option<String> {\n openai_compatible_profile_by_id(profile_id)?;\n let cache = jcode_provider_openrouter::load_disk_cache_entry_for_namespace(&resolved.id)?;\n\n let source_matches = cache\n .source_api_base\n .as_deref()\n .and_then(normalize_api_base)\n == normalize_api_base(&resolved.api_base);\n if !source_matches {\n return None;\n }\n\n cache\n .models\n .into_iter()\n .enumerate()\n .filter_map(|(index, model)| {\n let id = model.id.trim().to_string();\n let created = model.created?;\n // Never auto-select an obviously non-chat model (TTS/speech/embeddings/\n // rerankers/image/etc.) as a profile's default. Catalogs like Groq,\n // NVIDIA NIM, and Chutes expose their entire model list, and the\n // newest-released entry is frequently a non-chat model (e.g. Groq's\n // `canopylabs/orpheus-*` TTS), which must not become the chat default.\n if id.is_empty() || !crate::provider::is_listable_model_name(&id) {\n return None;\n }\n let tier = openai_compatible_model_quality_tier(&id);\n Some((tier, created, std::cmp::Reverse(index), id))\n })\n // Pick the strongest tier first, then the newest within that tier, then\n // catalog order. Ranking by quality *before* recency stops a brand-new\n // cheap/small model (e.g. a freshly released `*-flash`/`*-mini`) from\n // becoming the default of a heterogeneous proxy catalog (OpenCode Zen,\n // Groq, ...) when a stronger sibling is available.\n .max_by_key(|(tier, created, reverse_index, _)| (*tier, *created, *reverse_index))\n .map(|(_, _, _, id)| id)\n}\n\n/// Cheap/small/fast tier markers. A model id whose tokens include one of these\n/// is a cheaper or smaller variant that must not become a profile's default\n/// while a stronger sibling exists. Matched on whole tokens (split on\n/// non-alphanumeric) so brand names like `minimax` are not mistaken for\n/// `mini`/`max`.\nconst OPENAI_COMPAT_CHEAP_TIER_MARKERS: &[&str] = &[\n \"mini\",\n \"nano\",\n \"lite\",\n \"small\",\n \"tiny\",\n \"flash\",\n \"instant\",\n \"air\",\n \"micro\",\n \"lightning\",\n \"haiku\",\n \"scout\",\n \"edge\",\n \"lowcost\",\n];\n\n/// Flagship/strong tier markers. A model id whose tokens include one of these\n/// advertises itself as a provider's top-tier or specialized-strong model.\nconst OPENAI_COMPAT_FLAGSHIP_TIER_MARKERS: &[&str] = &[\n \"opus\", \"max\", \"ultra\", \"pro\", \"plus\", \"large\", \"coder\", \"code\", \"reasoner\", \"405b\", \"480b\",\n \"235b\", \"671b\", \"256b\",\n];\n\n/// Coarse cross-vendor quality tier for an openai-compatible catalog model id\n/// (higher is stronger): `2` = flagship-marked, `0` = cheap/small-marked, `1`\n/// otherwise (a bare frontier id like `gpt-5.5` or `minimax-m2.7`).\n///\n/// This relies on the near-universal naming convention that cheaper variants\n/// carry a size/speed marker (`mini`, `flash`, `air`, ...) and flagship variants\n/// carry a top-tier marker (`max`, `pro`, `opus`, `coder`, ...). It is a coarse\n/// heuristic only used to break the \"newest model\" tie sensibly; the user can\n/// always override the selected model.\nfn openai_compatible_model_quality_tier(model_id: &str) -> u8 {\n let lower = model_id.to_ascii_lowercase();\n let tokens: Vec<&str> = lower\n .split(|c: char| !c.is_ascii_alphanumeric())\n .filter(|t| !t.is_empty())\n .collect();\n let has = |markers: &[&str]| markers.iter().any(|marker| tokens.contains(marker));\n // Flagship markers win when both are present (e.g. `qwen3-coder-30b`): a\n // strong-specialization signal outweighs a size token.\n if has(OPENAI_COMPAT_FLAGSHIP_TIER_MARKERS) {\n return 2;\n }\n if has(OPENAI_COMPAT_CHEAP_TIER_MARKERS) {\n return 0;\n }\n 1\n}\n\nfn apply_profile_key_based_endpoint_overrides(\n profile: OpenAiCompatibleProfile,\n resolved: &mut ResolvedOpenAiCompatibleProfile,\n api_key_hint: Option<&str>,\n) {\n if profile.id != MINIMAX_PROFILE.id {\n return;\n }\n\n let key = api_key_hint\n .map(str::trim)\n .filter(|key| !key.is_empty())\n .map(ToString::to_string)\n .or_else(|| load_env_value_from_env_or_config(profile.api_key_env, profile.env_file));\n\n if key\n .as_deref()\n .map(|key| key.trim_start().starts_with(\"sk-cp-\"))\n .unwrap_or(false)\n {\n resolved.api_base = MINIMAX_CHINA_API_BASE.to_string();\n resolved.setup_url = MINIMAX_CHINA_SETUP_URL.to_string();\n }\n}\n\npub fn resolve_openai_compatible_profile_selection(input: &str) -> Option<OpenAiCompatibleProfile> {\n let provider = resolve_login_provider(input)?;\n match provider.target {\n LoginProviderTarget::OpenAiCompatible(profile) => Some(profile),\n _ => None,\n }\n}\n\npub fn active_openai_compatible_display_name() -> Option<String> {\n if let Ok(profile_name) = std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\") {\n let trimmed = profile_name.trim();\n if !trimmed.is_empty() {\n return Some(trimmed.to_string());\n }\n }\n\n if let Ok(namespace) = std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\") {\n let trimmed = namespace.trim();\n if let Some(profile) = openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| profile.id == trimmed)\n {\n return Some(profile.display_name.to_string());\n }\n }\n\n let api_base = std::env::var(\"JCODE_OPENROUTER_API_BASE\")\n .ok()\n .map(|value| value.trim().to_string())\n .filter(|value| !value.is_empty())\n .or_else(|| env_override(\"JCODE_OPENAI_COMPAT_API_BASE\"));\n\n let api_base = api_base.and_then(|value| normalize_api_base(&value))?;\n\n for profile in openai_compatible_profiles().iter().copied() {\n if normalize_api_base(profile.api_base).as_deref() == Some(api_base.as_str()) {\n return Some(profile.display_name.to_string());\n }\n }\n\n if !api_base.contains(\"openrouter.ai\") {\n return Some(\"OpenAI-compatible\".to_string());\n }\n\n None\n}\n\npub fn runtime_provider_display_name(provider_name: &str) -> String {\n if provider_name.eq_ignore_ascii_case(\"openrouter\") {\n if let Ok(runtime_provider) = std::env::var(\"JCODE_RUNTIME_PROVIDER\")\n && runtime_provider.trim().eq_ignore_ascii_case(\"azure-openai\")\n {\n return \"Azure OpenAI\".to_string();\n }\n\n active_openai_compatible_display_name().unwrap_or_else(|| \"OpenRouter\".to_string())\n } else {\n provider_name.to_string()\n }\n}\n\npub fn openai_compatible_profile_by_id(id: &str) -> Option<OpenAiCompatibleProfile> {\n let normalized = id.trim().to_ascii_lowercase();\n openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| profile.id == normalized)\n}\n\npub fn openai_compatible_profile_id_for_api_base(api_base: &str) -> Option<&'static str> {\n let normalized = normalize_api_base(api_base)?;\n openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| {\n normalize_api_base(profile.api_base).as_deref() == Some(normalized.as_str())\n })\n .map(|profile| profile.id)\n}\n\npub fn openai_compatible_profile_id_for_display_name(display_name: &str) -> Option<&'static str> {\n let normalized = display_name.trim().to_ascii_lowercase();\n openai_compatible_profiles()\n .iter()\n .copied()\n .find(|profile| {\n profile.id == normalized\n || profile\n .display_name\n .eq_ignore_ascii_case(display_name.trim())\n })\n .map(|profile| profile.id)\n}\n\npub fn openai_compatible_profile_static_models(profile: OpenAiCompatibleProfile) -> Vec<String> {\n let mut models = Vec::new();\n let mut push = |model: &str| {\n let model = model.trim();\n if !model.is_empty() && !models.iter().any(|existing| existing == model) {\n models.push(model.to_string());\n }\n };\n\n match profile.id {\n \"opencode\" => {\n push(\"minimax-m2.7\");\n push(\"kimi-k2.5\");\n push(\"glm-4.7\");\n push(\"glm-5\");\n push(\"claude-haiku-4-5\");\n push(\"gpt-5.1-codex-max\");\n }\n \"opencode-go\" => {\n push(\"minimax-m2.7\");\n push(\"kimi-k2.5\");\n push(\"glm-5\");\n push(\"glm-5.1\");\n push(\"deepseek-v4-flash\");\n push(\"qwen3.5-plus\");\n }\n \"zai\" => {\n push(\"glm-4.5\");\n push(\"glm-4.7\");\n push(\"glm-5\");\n push(\"glm-5.1\");\n push(\"glm-4.7-flash\");\n push(\"glm-4.7-flashx\");\n }\n \"302ai\" => {\n push(\"qwen3-235b-a22b-instruct-2507\");\n push(\"glm-4.7\");\n push(\"glm-5.1\");\n push(\"MiniMax-M2\");\n push(\"kimi-k2-0905-preview\");\n push(\"claude-haiku-4-5\");\n }\n \"baseten\" => {\n push(\"zai-org/GLM-4.7\");\n push(\"zai-org/GLM-5\");\n push(\"openai/gpt-oss-120b\");\n push(\"moonshotai/Kimi-K2.6\");\n push(\"moonshotai/Kimi-K2.5\");\n push(\"deepseek-ai/DeepSeek-V4-Pro\");\n }\n \"cortecs\" => {\n push(\"minimax-m2.7\");\n push(\"kimi-k2.5\");\n push(\"glm-4.7\");\n push(\"glm-5\");\n push(\"claude-haiku-4-5\");\n push(\"qwen3-235b-a22b-instruct-2507\");\n }\n // Issue #79: DeepSeek's live model catalog is not always available during\n // TUI startup, but both models should still be selectable once the direct\n // provider is configured.\n \"deepseek\" => {\n push(\"deepseek-v4-flash\");\n push(\"deepseek-v4-pro\");\n }\n \"comtegra\" => {\n push(\"gpt-oss-120b\");\n push(\"qwen35-122b\");\n push(\"gte-qwen2-7b\");\n push(\"glm-51-nvfp4\");\n }\n \"fpt\" => {\n push(\"GLM-5.1\");\n push(\"GLM-4.7\");\n push(\"Llama-3.3-70B-Instruct\");\n }\n \"kimi\" => {\n push(\"kimi-for-coding\");\n push(\"kimi-k2.5\");\n push(\"kimi-k2.6\");\n push(\"kimi-k2-thinking\");\n push(\"kimi-k2-thinking-turbo\");\n }\n \"firmware\" => {\n push(\"kimi-k2.5\");\n push(\"zai-glm-5-1\");\n push(\"claude-haiku-4-5\");\n push(\"claude-sonnet-4-6\");\n push(\"grok-code-fast-1\");\n push(\"gemini-2.5-flash\");\n }\n \"huggingface\" => {\n push(\"Qwen/Qwen3-Coder-480B-A35B-Instruct\");\n push(\"Qwen/Qwen3-Coder-Next\");\n push(\"zai-org/GLM-4.7\");\n push(\"zai-org/GLM-5.1\");\n push(\"deepseek-ai/DeepSeek-V3.2\");\n push(\"openai/gpt-oss-120b\");\n }\n \"moonshotai\" => {\n push(\"kimi-k2.5\");\n push(\"kimi-k2.6\");\n push(\"kimi-k2-thinking\");\n push(\"kimi-k2-thinking-turbo\");\n push(\"kimi-k2-turbo-preview\");\n }\n \"nebius\" => {\n push(\"openai/gpt-oss-120b\");\n push(\"Qwen/Qwen3-235B-A22B-Instruct-2507\");\n push(\"Qwen/Qwen3.5-397B-A17B\");\n push(\"zai-org/GLM-5\");\n push(\"meta-llama/Llama-3.3-70B-Instruct\");\n push(\"NousResearch/Hermes-4-70B\");\n }\n \"scaleway\" => {\n push(\"qwen3-coder-30b-a3b-instruct\");\n push(\"qwen3-235b-a22b-instruct-2507\");\n push(\"qwen3.5-397b-a17b\");\n push(\"gpt-oss-120b\");\n push(\"mistral-small-3.2-24b-instruct-2506\");\n push(\"llama-3.3-70b-instruct\");\n }\n \"stackit\" => {\n push(\"openai/gpt-oss-120b\");\n push(\"Qwen/Qwen3-VL-235B-A22B-Instruct-FP8\");\n push(\"cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic\");\n push(\"neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8\");\n push(\"google/gemma-3-27b-it\");\n }\n \"perplexity\" => {\n push(\"sonar\");\n push(\"sonar-pro\");\n push(\"sonar-reasoning-pro\");\n push(\"sonar-deep-research\");\n }\n \"deepinfra\" => {\n push(\"moonshotai/Kimi-K2-Instruct\");\n push(\"Qwen/Qwen3-Coder-480B-A35B-Instruct\");\n push(\"Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo\");\n push(\"zai-org/GLM-4.7\");\n push(\"zai-org/GLM-5.1\");\n push(\"meta-llama/Llama-3.1-70B-Instruct\");\n }\n \"fireworks\" => {\n push(\"accounts/fireworks/routers/kimi-k2p5-turbo\");\n push(\"accounts/fireworks/models/kimi-k2p5\");\n push(\"accounts/fireworks/models/kimi-k2p6\");\n push(\"accounts/fireworks/models/glm-4p7\");\n push(\"accounts/fireworks/models/glm-5p1\");\n push(\"accounts/fireworks/models/deepseek-v3p2\");\n }\n \"cerebras\" => {\n push(\"gpt-oss-120b\");\n push(\"zai-glm-4.7\");\n }\n // Celeris serves exactly one model per base URL today, and `/models`\n // requires auth, so keep the documented id available pre-refresh.\n \"celeris\" => {\n push(\"celeris-1\");\n }\n \"xiaomi-mimo\" => {\n push(\"mimo-v2.5\");\n push(\"mimo-v2.5-pro\");\n push(\"mimo-v2-pro\");\n push(\"mimo-v2-flash\");\n push(\"mimo-v2-omni\");\n }\n // MiniMax's `/models` endpoint is authenticated and live, but post-login\n // model activation should not depend on the catalog refresh completing\n // before the picker/routes are rebuilt. Keep the documented text models\n // selectable immediately after saving a key.\n \"minimax\" => {\n push(\"MiniMax-M2.7\");\n push(\"MiniMax-M2.7-highspeed\");\n push(\"MiniMax-M2.5\");\n push(\"MiniMax-M2.5-highspeed\");\n push(\"MiniMax-M2.1\");\n push(\"MiniMax-M2.1-highspeed\");\n push(\"MiniMax-M2\");\n }\n \"alibaba-coding-plan\" => {\n push(\"qwen3-coder-plus\");\n push(\"qwen3.5-plus\");\n push(\"qwen3-max-2026-01-23\");\n push(\"qwen3-coder-next\");\n push(\"glm-5\");\n push(\"glm-4.7\");\n push(\"kimi-k2.5\");\n push(\"MiniMax-M2.5\");\n }\n \"gemini-api\" => {\n push(\"gemini-2.5-flash\");\n push(\"gemini-2.5-pro\");\n push(\"gemini-2.0-flash\");\n push(\"gemini-2.0-flash-lite\");\n }\n _ => {}\n }\n\n models\n}\n\npub fn openai_compatible_profile_model_supports_chat(_profile_id: &str, _model: &str) -> bool {\n true\n}\n\npub fn openai_compatible_profile_static_context_limits(\n profile: OpenAiCompatibleProfile,\n) -> HashMap<String, usize> {\n openai_compatible_profile_static_models(profile)\n .into_iter()\n .filter_map(|model| {\n openai_compatible_profile_context_limit(profile.id, &model).map(|limit| (model, limit))\n })\n .collect()\n}\n\npub fn openai_compatible_profile_context_limit(profile_id: &str, model: &str) -> Option<usize> {\n let profile_id = profile_id.trim().to_ascii_lowercase();\n let model = model.trim().to_ascii_lowercase();\n\n match profile_id.as_str() {\n // DeepSeek V4 direct API models advertise a 1M token context window. The\n // direct profile runs through the OpenRouter/OpenAI-compatible provider\n // implementation, whose live catalog can be unavailable during startup.\n \"deepseek\" if model.starts_with(\"deepseek-v4-\") => Some(1_000_000),\n // Fall back to the shared open-weight family classifier. Many bundled\n // OpenAI-compatible gateways (Z.AI/GLM, Moonshot/Kimi, MiniMax, Qwen,\n // etc.) serve `/v1/models` entries without a `context_length`, so this\n // static table is the only reliable source before a live catalog (or an\n // explicit user `context_window` override) is available.\n _ => jcode_provider_core::models::open_weight_family_context_limit(&model),\n }\n}\n\npub fn apply_openai_compatible_profile_env(profile: Option<OpenAiCompatibleProfile>) {\n apply_openai_compatible_profile_env_impl(profile, true);\n}\n\npub fn force_apply_openai_compatible_profile_env(profile: Option<OpenAiCompatibleProfile>) {\n apply_openai_compatible_profile_env_impl(profile, false);\n}\n\nfn apply_openai_compatible_profile_env_impl(\n profile: Option<OpenAiCompatibleProfile>,\n respect_named_profile_lock: bool,\n) {\n if respect_named_profile_lock && std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_some() {\n return;\n }\n\n let vars = [\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ];\n\n for var in vars {\n crate::env::remove_var(var);\n }\n\n if let Some(profile) = profile {\n let resolved = resolve_openai_compatible_profile(profile);\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", &resolved.api_base);\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", &resolved.api_key_env);\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", &resolved.env_file);\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", &resolved.id);\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"0\");\n let static_models = openai_compatible_profile_static_models(profile);\n if static_models.is_empty() {\n crate::env::remove_var(\"JCODE_OPENROUTER_STATIC_MODELS\");\n } else {\n crate::env::set_var(\"JCODE_OPENROUTER_STATIC_MODELS\", static_models.join(\"\\n\"));\n }\n if resolved.requires_api_key {\n crate::env::remove_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-api-key\");\n } else {\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-no-auth\");\n }\n }\n}\n\nfn inline_key_env_name(profile_name: &str) -> String {\n let suffix = profile_name\n .chars()\n .map(|ch| {\n if ch.is_ascii_alphanumeric() {\n ch.to_ascii_uppercase()\n } else {\n '_'\n }\n })\n .collect::<String>();\n format!(\"JCODE_PROVIDER_{}_API_KEY\", suffix)\n}\n\npub fn apply_named_provider_profile_env(profile_name: &str) -> anyhow::Result<String> {\n let config = crate::config::Config::load_strict()?;\n apply_named_provider_profile_env_from_config(profile_name, &config)\n}\n\npub fn apply_named_provider_profile_env_from_config(\n profile_name: &str,\n config: &crate::config::Config,\n) -> anyhow::Result<String> {\n let Some(profile) = config.providers.get(profile_name) else {\n anyhow::bail!(\n \"Unknown provider profile '{}'. Add [providers.{}] to config.toml.\",\n profile_name,\n profile_name\n );\n };\n\n let api_base = normalize_api_base(&profile.base_url).ok_or_else(|| {\n anyhow::anyhow!(\n \"Provider profile '{}' has invalid base_url '{}'. Use https://... or http://localhost.\",\n profile_name,\n profile.base_url\n )\n })?;\n\n crate::env::remove_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\");\n crate::env::remove_var(\"JCODE_PROVIDER_PROFILE_NAME\");\n crate::env::remove_var(\"JCODE_NAMED_PROVIDER_PROFILE\");\n apply_openai_compatible_profile_env(None);\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", &api_base);\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", profile_name);\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", profile_name);\n let provider_is_openrouter = matches!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenRouter\n );\n\n let provider_features =\n provider_is_openrouter || profile.provider_routing || profile.allow_provider_pinning;\n crate::env::set_var(\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n if provider_features { \"1\" } else { \"0\" },\n );\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n if profile.model_catalog\n || matches!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenRouter\n )\n {\n \"1\"\n } else {\n \"0\"\n },\n );\n\n if let Some(model) = profile\n .default_model\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n {\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", model);\n }\n\n let static_models = profile\n .models\n .iter()\n .map(|model| model.id.trim())\n .filter(|id| !id.is_empty())\n .collect::<Vec<_>>();\n if !static_models.is_empty() {\n crate::env::set_var(\"JCODE_OPENROUTER_STATIC_MODELS\", static_models.join(\"\\n\"));\n }\n\n match profile.auth {\n crate::config::NamedProviderAuth::None => {\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-no-auth\");\n }\n crate::config::NamedProviderAuth::Bearer | crate::config::NamedProviderAuth::Header => {\n let key_env = profile\n .api_key_env\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n .map(ToString::to_string)\n .or_else(|| {\n profile.api_key.as_deref().map(str::trim).filter(|v| !v.is_empty()).map(|key| {\n let env_name = inline_key_env_name(profile_name);\n crate::env::set_var(&env_name, key);\n crate::logging::warn(&format!(\n \"Provider profile '{}' stores an inline API key in config.toml. Prefer api_key_env to avoid accidental leaks.\",\n profile_name\n ));\n env_name\n })\n });\n\n if let Some(key_env) = key_env {\n if !is_safe_env_key_name(&key_env) {\n anyhow::bail!(\n \"Provider profile '{}' has invalid api_key_env '{}'.\",\n profile_name,\n key_env\n );\n }\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", &key_env);\n }\n\n if let Some(env_file) = profile\n .env_file\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n {\n if !is_safe_env_file_name(env_file) {\n anyhow::bail!(\n \"Provider profile '{}' has invalid env_file '{}'.\",\n profile_name,\n env_file\n );\n }\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", env_file);\n }\n\n let requires_key = profile\n .requires_api_key\n .unwrap_or(!api_base_uses_localhost(&api_base));\n if !requires_key {\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-no-auth\");\n } else if provider_is_openrouter {\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"openrouter-api-key\");\n } else {\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"direct-api-key\");\n }\n\n match profile.auth {\n crate::config::NamedProviderAuth::Bearer => {\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"bearer\");\n }\n crate::config::NamedProviderAuth::Header => {\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"api-key\");\n if let Some(header) = profile\n .auth_header\n .as_deref()\n .map(str::trim)\n .filter(|v| !v.is_empty())\n {\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", header);\n }\n }\n crate::config::NamedProviderAuth::None => {}\n }\n }\n }\n\n Ok(profile_name.to_string())\n}\n\npub fn openrouter_like_api_key_sources() -> Vec<(String, String)> {\n let mut sources = Vec::with_capacity(10);\n sources.push((\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string(),\n ));\n\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n sources.push((\n profile.api_key_env.to_string(),\n profile.env_file.to_string(),\n ));\n }\n }\n\n if let Some(source) = configured_api_key_source(\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"OPENROUTER_API_KEY\",\n \"openrouter.env\",\n ) {\n sources.push(source);\n }\n\n if let Some(source) = configured_api_key_source(\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n OPENAI_COMPAT_PROFILE.api_key_env,\n OPENAI_COMPAT_PROFILE.env_file,\n ) {\n sources.push(source);\n }\n\n dedup_sources(sources)\n}\n\nfn parse_bool_like(value: &str) -> bool {\n matches!(\n value.trim().to_ascii_lowercase().as_str(),\n \"1\" | \"true\" | \"yes\" | \"on\"\n )\n}\n\npub fn openai_compatible_profile_is_configured(profile: OpenAiCompatibleProfile) -> bool {\n // When a named config profile (`[providers.<name>]`, selected via\n // `--provider-profile`) is active, its credentials live under the runtime\n // env vars set by `apply_named_provider_profile_env`, not the built-in\n // `openai-compatible.env`. Honor those first so auth-test does not report a\n // false `not_configured` for a correctly-configured named profile (#402).\n if let Some(configured) = active_named_provider_profile_is_configured() {\n return configured;\n }\n\n let resolved = resolve_openai_compatible_profile(profile);\n if load_api_key_from_env_or_config(&resolved.api_key_env, &resolved.env_file).is_some() {\n return true;\n }\n\n if resolved.requires_api_key {\n return false;\n }\n\n if profile.id == OPENAI_COMPAT_PROFILE.id && api_base_uses_localhost(&resolved.api_base) {\n return true;\n }\n\n load_env_value_from_env_or_config(OPENAI_COMPAT_LOCAL_ENABLED_ENV, &resolved.env_file)\n .map(|value| parse_bool_like(&value))\n .unwrap_or(false)\n}\n\n/// Resolve the active named provider profile's credential env var + env file,\n/// as set by [`apply_named_provider_profile_env`], if one is active.\n///\n/// Returns `(api_key_env, env_file)` describing where to look for the key.\npub fn active_named_provider_profile_credential_source() -> Option<(String, String)> {\n // Presence of this var marks an active named profile (set by\n // `apply_named_provider_profile_env`).\n let _profile_name = std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())?;\n\n let key_env = std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| \"OPENROUTER_API_KEY\".to_string());\n let env_file = std::env::var(\"JCODE_OPENROUTER_ENV_FILE\")\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| \"openrouter.env\".to_string());\n Some((key_env, env_file))\n}\n\n/// Whether the active named provider profile has credentials available.\n///\n/// Returns `None` when no named profile is active (so callers fall back to the\n/// built-in profile checks), `Some(true)` when the profile is no-auth or has a\n/// key, and `Some(false)` when a key is required but missing.\nfn active_named_provider_profile_is_configured() -> Option<bool> {\n let (key_env, env_file) = active_named_provider_profile_credential_source()?;\n\n // A no-auth profile (localhost or explicit allow-no-auth) is configured.\n if std::env::var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\")\n .map(|v| parse_bool_like(&v))\n .unwrap_or(false)\n {\n return Some(true);\n }\n\n Some(load_api_key_from_env_or_config(&key_env, &env_file).is_some())\n}\n\npub fn configured_api_key_source(\n key_var: &str,\n file_var: &str,\n default_key: &str,\n default_file: &str,\n) -> Option<(String, String)> {\n if std::env::var_os(key_var).is_none() && std::env::var_os(file_var).is_none() {\n return None;\n }\n\n let env_key = std::env::var(key_var)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| default_key.to_string());\n let file_name = std::env::var(file_var)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .unwrap_or_else(|| default_file.to_string());\n\n if !is_safe_env_key_name(&env_key) {\n crate::logging::warn(&format!(\n \"Ignoring invalid {}='{}' while probing auth status\",\n key_var, env_key\n ));\n return None;\n }\n if !is_safe_env_file_name(&file_name) {\n crate::logging::warn(&format!(\n \"Ignoring invalid {}='{}' while probing auth status\",\n file_var, file_name\n ));\n return None;\n }\n\n Some((env_key, file_name))\n}\n\nfn env_override(name: &str) -> Option<String> {\n std::env::var(name)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n .or_else(|| load_env_value_from_env_or_config(name, OPENAI_COMPAT_PROFILE.env_file))\n}\n\nfn dedup_sources(sources: Vec<(String, String)>) -> Vec<(String, String)> {\n let mut seen = HashSet::new();\n let mut deduped = Vec::with_capacity(sources.len());\n for (env_key, env_file) in sources {\n if seen.insert((env_key.clone(), env_file.clone())) {\n deduped.push((env_key, env_file));\n }\n }\n deduped\n}\n\n#[cfg(test)]\n#[path = \"provider_catalog_tests.rs\"]\nmod provider_catalog_tests;\n"}}, "crates/jcode-base/src/provider/catalog_routes.rs": {"d6c7c36d61fb266c4e4ccfac0d7b710d3f50f444": {"t": 1785595999.969926, "n": 59176, "h": "6e7d1b51308b1283", "c": "use crate::auth::{AuthState, AuthStatus};\n\nuse super::pricing::cheapness_for_route;\nuse super::{\n ALL_OPENAI_MODELS, AccountModelAvailabilityState, CHATGPT_WEB_MODEL, ModelRoute, MultiProvider,\n Provider, anthropic_api_key_route_availability, anthropic_oauth_route_availability, bedrock,\n build_anthropic_oauth_route, build_chatgpt_web_route, build_copilot_route,\n build_openai_api_key_route, build_openai_oauth_route, build_openrouter_auto_route,\n build_openrouter_endpoint_route, build_openrouter_fallback_provider_route,\n configured_standard_openrouter_profile_routes, copilot, dedupe_model_routes,\n direct_openai_compatible_profile_routes, format_account_model_availability_detail,\n is_listable_model_name, known_anthropic_model_ids, known_openai_model_ids,\n model_availability_for_account, openrouter, openrouter_catalog_model_id, provider_for_model,\n standard_openrouter_profile_configured,\n};\n\n/// Build the fast local route snapshot used by the TUI model picker while the\n/// full provider catalog is hydrating.\n///\n/// This intentionally lives in the provider layer rather than the TUI so auth,\n/// provider, and catalog policy have one source of truth. The TUI should only\n/// group, sort, and render the returned routes.\npub fn simplified_model_routes_for_picker(\n current_provider_name: &str,\n current_model: &str,\n display_models: impl IntoIterator<Item = String>,\n) -> Vec<ModelRoute> {\n let auth = AuthStatus::check_fast();\n let mut routes = Vec::new();\n\n for model in display_models {\n if model == CHATGPT_WEB_MODEL {\n routes.push(build_chatgpt_web_route());\n continue;\n }\n if !model.contains('/') && provider_for_model(&model) == Some(\"openai\") {\n // Platform-API-only GPT Pro models: never advertise an OAuth route.\n if jcode_provider_core::is_openai_api_only_pro_model(&model) {\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"OpenAI\".to_string(),\n api_method: \"openai-api-key\".to_string(),\n available: auth.openai_has_api_key,\n detail: if auth.openai_has_api_key {\n String::new()\n } else {\n \"requires OPENAI_API_KEY\".to_string()\n },\n cheapness: None,\n });\n continue;\n }\n if auth.openai_has_oauth {\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"OpenAI\".to_string(),\n api_method: \"openai-oauth\".to_string(),\n available: true,\n detail: String::new(),\n cheapness: None,\n });\n }\n if auth.openai_has_api_key {\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"OpenAI\".to_string(),\n api_method: \"openai-api-key\".to_string(),\n available: true,\n detail: String::new(),\n cheapness: None,\n });\n }\n if auth.openai == AuthState::NotConfigured {\n routes.push(ModelRoute {\n model,\n provider: \"OpenAI\".to_string(),\n api_method: \"openai-oauth\".to_string(),\n available: false,\n detail: \"no credentials\".to_string(),\n cheapness: None,\n });\n }\n continue;\n }\n\n let (provider, api_method, available, detail) =\n if super::bedrock::BedrockProvider::is_bedrock_model_id(&model) {\n (\n \"AWS Bedrock\".to_string(),\n \"bedrock\".to_string(),\n auth.bedrock != AuthState::NotConfigured,\n if auth.bedrock == AuthState::NotConfigured {\n \"no Bedrock credentials or region; run /login bedrock\".to_string()\n } else {\n String::new()\n },\n )\n } else if model.contains('/') {\n (\n \"auto\".to_string(),\n \"openrouter\".to_string(),\n auth.openrouter != AuthState::NotConfigured,\n \"simplified catalog\".to_string(),\n )\n } else {\n match provider_for_model(&model) {\n Some(\"claude\") => {\n append_simplified_anthropic_model_routes(&mut routes, model, &auth);\n continue;\n }\n Some(\"openai\") => unreachable!(\"OpenAI models are handled above\"),\n Some(\"gemini\") => (\n \"Gemini\".to_string(),\n \"code-assist-oauth\".to_string(),\n auth.gemini != AuthState::NotConfigured,\n String::new(),\n ),\n Some(\"cursor\") => (\n \"Cursor\".to_string(),\n \"cursor\".to_string(),\n auth.cursor != AuthState::NotConfigured,\n String::new(),\n ),\n Some(\"openrouter\") => (\n \"auto\".to_string(),\n \"openrouter\".to_string(),\n auth.openrouter != AuthState::NotConfigured,\n \"simplified catalog\".to_string(),\n ),\n Some(other) => (other.to_string(), other.to_string(), true, String::new()),\n None => (\n current_provider_name.to_string(),\n \"current\".to_string(),\n true,\n String::new(),\n ),\n }\n };\n\n routes.push(ModelRoute {\n model,\n provider,\n api_method,\n available,\n detail,\n cheapness: None,\n });\n }\n\n if routes.is_empty() && !current_model.is_empty() && current_model != \"unknown\" {\n routes.push(ModelRoute {\n model: current_model.to_string(),\n provider: current_provider_name.to_string(),\n api_method: \"current\".to_string(),\n available: true,\n detail: \"simplified catalog\".to_string(),\n cheapness: None,\n });\n }\n\n routes\n}\n\npub fn append_simplified_anthropic_model_routes(\n routes: &mut Vec<ModelRoute>,\n model: impl Into<String>,\n auth: &AuthStatus,\n) {\n let model = model.into();\n if auth.anthropic.has_oauth {\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"Anthropic\".to_string(),\n api_method: \"claude-oauth\".to_string(),\n available: true,\n detail: String::new(),\n cheapness: None,\n });\n }\n if auth.anthropic.has_api_key {\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"Anthropic\".to_string(),\n api_method: \"claude-api\".to_string(),\n available: true,\n detail: String::new(),\n cheapness: None,\n });\n }\n if !auth.anthropic.has_oauth && !auth.anthropic.has_api_key {\n routes.push(ModelRoute {\n model,\n provider: \"Anthropic\".to_string(),\n api_method: \"claude-oauth\".to_string(),\n available: false,\n detail: \"no credentials\".to_string(),\n cheapness: None,\n });\n }\n}\n\n/// Per-build statistics for the OpenRouter section of route construction,\n/// used only for the timing/summary log lines.\n#[derive(Default)]\nstruct OpenRouterRouteStats {\n models: usize,\n endpoint_cache_hits: usize,\n endpoint_routes: usize,\n scheduled_endpoint_refreshes: usize,\n}\n\n/// Build the full multi-provider route catalog.\n///\n/// Orchestration only: each provider family contributes routes through its\n/// own `append_*_routes` builder below, so provider-specific policy stays in\n/// one place per provider instead of one 400-line function.\npub(super) fn multiprovider_model_routes(provider: &MultiProvider) -> Vec<ModelRoute> {\n let routes_started = std::time::Instant::now();\n provider.spawn_anthropic_catalog_refresh_if_needed();\n provider.spawn_openai_catalog_refresh_if_needed();\n\n let mut routes = Vec::new();\n let mut openrouter_stats = OpenRouterRouteStats::default();\n\n let has_oauth = provider.has_claude_runtime();\n let has_api_key = crate::provider_catalog::load_api_key_from_env_or_config(\n \"ANTHROPIC_API_KEY\",\n \"anthropic.env\",\n )\n .is_some();\n let openai_auth = crate::auth::AuthStatus::check_fast();\n\n append_anthropic_routes(provider, &mut routes, has_oauth, has_api_key);\n append_openai_routes(provider, &mut routes, &openai_auth);\n let added_direct_openai_compatible_routes =\n append_openai_compatible_profile_routes(provider, &mut routes);\n append_copilot_routes(provider, &mut routes);\n append_gemini_routes(provider, &mut routes);\n append_antigravity_routes(provider, &mut routes);\n append_cursor_routes(provider, &mut routes);\n append_bedrock_routes(provider, &mut routes);\n\n let has_openrouter = provider.openrouter_provider().is_some();\n let has_openrouter_provider_features = provider\n .openrouter_provider()\n .map(|openrouter| openrouter.supports_provider_routing_features())\n .unwrap_or(false);\n append_openrouter_routes(provider, &mut routes, &mut openrouter_stats);\n\n if !has_openrouter && !added_direct_openai_compatible_routes {\n // OpenRouter not configured - show a placeholder as unavailable.\n routes.push(ModelRoute {\n model: \"openrouter models\".to_string(),\n provider: \"—\".to_string(),\n api_method: \"openrouter\".to_string(),\n available: false,\n detail: \"OPENROUTER_API_KEY not set\".to_string(),\n cheapness: None,\n });\n }\n\n if has_openrouter_provider_features {\n append_openrouter_alternative_routes(&mut routes, &mut openrouter_stats);\n }\n\n let total_ms = routes_started.elapsed().as_millis();\n if total_ms >= 250 || std::env::var(\"JCODE_LOG_MODEL_PICKER_TIMING\").is_ok() {\n crate::logging::info(&format!(\n \"[TIMING] model_routes: routes={}, openrouter_configured={}, openrouter_models={}, openrouter_endpoint_cache_hits={}, openrouter_endpoint_routes={}, openrouter_scheduled_endpoint_refreshes={}, total={}ms\",\n routes.len(),\n has_openrouter,\n openrouter_stats.models,\n openrouter_stats.endpoint_cache_hits,\n openrouter_stats.endpoint_routes,\n openrouter_stats.scheduled_endpoint_refreshes,\n total_ms,\n ));\n }\n\n let routes_before_filter = routes.len();\n\n // Drop obviously non-chat models (embeddings, speech, rerankers, etc.) that\n // some providers (Bedrock, OpenAI-compatible profiles like NVIDIA NIM / FPT\n // / Chutes) dump wholesale into their catalogs. Without this the picker is\n // flooded with hundreds of unusable entries.\n routes.retain(|route| is_listable_model_name(&route.model));\n\n let routes = dedupe_model_routes(routes);\n\n // Structured, always-on summary of catalog route building. This is the\n // single most useful line for the recurring \"model picker empty / only\n // OpenAI+Anthropic appear / configured provider's models missing\" reports\n // (issues #292, #268, #312, #304): it records which credentials were\n // detected and how many routes each provider contributed, so a shared log\n // explains exactly why a model was or was not offered. No secrets here.\n log_model_routes_summary(\n \"build\",\n &routes,\n routes_before_filter,\n has_oauth,\n has_api_key,\n openai_auth.openai_has_oauth,\n openai_auth.openai_has_api_key,\n has_openrouter,\n has_openrouter_provider_features,\n added_direct_openai_compatible_routes,\n total_ms,\n );\n\n routes\n}\n\n/// Anthropic models via OAuth and/or API key.\nfn append_anthropic_routes(\n provider: &MultiProvider,\n routes: &mut Vec<ModelRoute>,\n has_oauth: bool,\n has_api_key: bool,\n) {\n let anthropic_models = if let Some(anthropic) = provider.anthropic_provider() {\n anthropic.available_models_for_switching()\n } else if let Some(claude) = provider.claude_provider() {\n claude.available_models_for_switching()\n } else {\n known_anthropic_model_ids()\n };\n\n for model in anthropic_models {\n let (available, detail) = if has_oauth && !has_api_key {\n anthropic_oauth_route_availability(&model)\n } else {\n (true, String::new())\n };\n\n if has_oauth {\n routes.push(build_anthropic_oauth_route(\n &model,\n available,\n detail.clone(),\n ));\n }\n if has_api_key {\n let (ak_available, ak_detail) = anthropic_api_key_route_availability(&model);\n routes.push(ModelRoute {\n model: model.to_string(),\n provider: \"Anthropic\".to_string(),\n api_method: \"claude-api\".to_string(),\n available: ak_available,\n detail: ak_detail,\n cheapness: cheapness_for_route(&model, \"Anthropic\", \"claude-api\"),\n });\n }\n if !has_oauth && !has_api_key {\n routes.push(ModelRoute {\n model: model.to_string(),\n provider: \"Anthropic\".to_string(),\n api_method: \"claude-oauth\".to_string(),\n available: false,\n detail: \"no credentials\".to_string(),\n cheapness: cheapness_for_route(&model, \"Anthropic\", \"claude-oauth\"),\n });\n }\n }\n}\n\n/// OpenAI models via OAuth and/or API key, with per-account availability.\nfn append_openai_routes(\n provider: &MultiProvider,\n routes: &mut Vec<ModelRoute>,\n openai_auth: &crate::auth::AuthStatus,\n) {\n let openai_models = if let Some(openai) = provider.openai_provider() {\n openai.available_models_for_switching()\n } else {\n known_openai_model_ids()\n };\n\n for model in openai_models {\n if model == CHATGPT_WEB_MODEL {\n routes.push(build_chatgpt_web_route());\n continue;\n }\n let availability = model_availability_for_account(&model);\n let (available, detail) = if provider.openai_provider().is_none() {\n (false, \"no credentials\".to_string())\n } else {\n match availability.state {\n AccountModelAvailabilityState::Available => (true, String::new()),\n AccountModelAvailabilityState::Unavailable => (\n false,\n format_account_model_availability_detail(&availability)\n .unwrap_or_else(|| \"not available\".to_string()),\n ),\n AccountModelAvailabilityState::Unknown => {\n let detail = format_account_model_availability_detail(&availability)\n .unwrap_or_else(|| \"availability unknown\".to_string());\n (true, detail)\n }\n }\n };\n // GPT Pro models are platform-API-only: never offer an OAuth route\n // for them (the Codex backend rejects them for ChatGPT accounts).\n if jcode_provider_core::is_openai_api_only_pro_model(&model) {\n if openai_auth.openai_has_api_key {\n routes.push(build_openai_api_key_route(\n &model,\n provider.openai_provider().is_some(),\n String::new(),\n ));\n } else {\n routes.push(build_openai_api_key_route(\n &model,\n false,\n \"requires OPENAI_API_KEY\",\n ));\n }\n continue;\n }\n if openai_auth.openai_has_oauth {\n routes.push(build_openai_oauth_route(&model, available, detail.clone()));\n }\n if openai_auth.openai_has_api_key {\n routes.push(build_openai_api_key_route(\n &model,\n provider.openai_provider().is_some(),\n String::new(),\n ));\n }\n if !openai_auth.openai_has_oauth && !openai_auth.openai_has_api_key {\n routes.push(build_openai_oauth_route(&model, false, detail));\n }\n }\n}\n\n/// Configured OpenAI-compatible profiles (NVIDIA NIM, Groq, ...), excluding\n/// the active direct profile which contributes through the OpenRouter path.\n/// Returns whether any routes were added.\nfn append_openai_compatible_profile_routes(\n provider: &MultiProvider,\n routes: &mut Vec<ModelRoute>,\n) -> bool {\n let active_direct_openai_compatible_api_method = provider\n .openrouter_provider()\n .and_then(|openrouter| openrouter.direct_openai_compatible_route_parts())\n .map(|(_, api_method, _)| api_method);\n let mut added_any = false;\n for profile in crate::provider_catalog::openai_compatible_profiles()\n .iter()\n .copied()\n {\n if !crate::provider_catalog::openai_compatible_profile_is_configured(profile) {\n continue;\n }\n let resolved = crate::provider_catalog::resolve_openai_compatible_profile(profile);\n let api_method = format!(\"openai-compatible:{}\", resolved.id);\n\n // The active OpenRouter/OpenAI-compatible provider contributes its own\n // live memory/disk catalog below. Do not preempt it with the generic\n // configured-profile path, because its in-memory catalog may be newer\n // than the disk snapshot that this non-active profile path can read.\n if active_direct_openai_compatible_api_method.as_deref() == Some(api_method.as_str()) {\n continue;\n }\n\n let profile_routes = direct_openai_compatible_profile_routes(profile);\n added_any |= !profile_routes.is_empty();\n routes.extend(profile_routes);\n }\n\n // User-defined named provider profiles (`[providers.<name>]` in\n // config.toml). Their statically declared `[[providers.<name>.models]]`\n // entries (and `default_model`) must surface in the picker with a route\n // back to that profile, even when the profile is not the active provider\n // (issue #444).\n for (profile_name, profile_config) in &crate::config::config().providers {\n let api_method = format!(\"openai-compatible:{}\", profile_name);\n // The active runtime already contributes this profile's models (with\n // live-catalog freshness) via the OpenRouter slot path.\n if active_direct_openai_compatible_api_method.as_deref() == Some(api_method.as_str()) {\n continue;\n }\n let named_routes = named_provider_profile_routes(profile_name, profile_config);\n added_any |= !named_routes.is_empty();\n routes.extend(named_routes);\n }\n added_any\n}\n\n/// Picker routes for one user-defined named provider profile from config.\n///\n/// Text-capable static models plus the profile's `default_model` are offered;\n/// models declared image-only via `input = [\"image\"]` are excluded.\nfn named_provider_profile_routes(\n profile_name: &str,\n profile_config: &crate::config::NamedProviderConfig,\n) -> Vec<ModelRoute> {\n let mut models: Vec<String> = profile_config\n .models\n .iter()\n .filter(|model| {\n // `input` empty means unspecified (assume text-capable).\n model.input.is_empty() || model.input.iter().any(|input| input == \"text\")\n })\n .map(|model| model.id.trim().to_string())\n .filter(|id| !id.is_empty())\n .collect();\n if models.is_empty()\n && let Some(default_model) = profile_config\n .default_model\n .as_deref()\n .map(str::trim)\n .filter(|model| !model.is_empty())\n {\n models.push(default_model.to_string());\n }\n\n let api_method = format!(\"openai-compatible:{}\", profile_name);\n let detail = if profile_config.base_url.trim().is_empty() {\n \"configured provider profile\".to_string()\n } else {\n profile_config.base_url.trim().to_string()\n };\n\n let mut routes: Vec<ModelRoute> = Vec::new();\n for model in models {\n if !is_listable_model_name(&model) || routes.iter().any(|route| route.model == model) {\n continue;\n }\n routes.push(ModelRoute {\n model,\n provider: profile_name.to_string(),\n api_method: api_method.clone(),\n available: true,\n detail: detail.clone(),\n cheapness: None,\n });\n }\n routes\n}\n\n/// GitHub Copilot models, or a placeholder when credentials exist but the\n/// provider is not initialized.\nfn append_copilot_routes(provider: &MultiProvider, routes: &mut Vec<ModelRoute>) {\n if let Some(copilot) = provider.copilot_provider() {\n let copilot_models = copilot.available_models_display();\n let detail = copilot.model_catalog_detail();\n let copilot_models_empty = copilot_models.is_empty();\n for model in copilot_models {\n routes.push(build_copilot_route(&model, true, detail.clone()));\n }\n if copilot_models_empty && copilot::has_credentials() {\n routes.push(build_copilot_route(\"copilot models\", false, detail));\n }\n } else if copilot::has_credentials() {\n routes.push(build_copilot_route(\n \"copilot models\",\n false,\n \"not initialized yet\",\n ));\n }\n}\n\nfn append_gemini_routes(provider: &MultiProvider, routes: &mut Vec<ModelRoute>) {\n if let Some(gemini) = provider.gemini_provider() {\n for model in gemini.available_models_display() {\n routes.push(ModelRoute {\n model,\n provider: \"Gemini\".to_string(),\n api_method: \"code-assist-oauth\".to_string(),\n available: true,\n detail: String::new(),\n cheapness: None,\n });\n }\n }\n}\n\nfn append_antigravity_routes(provider: &MultiProvider, routes: &mut Vec<ModelRoute>) {\n if let Some(antigravity) = provider.antigravity_provider() {\n routes.extend(antigravity.model_routes());\n }\n}\n\nfn append_cursor_routes(provider: &MultiProvider, routes: &mut Vec<ModelRoute>) {\n if let Some(cursor) = provider.cursor_provider() {\n for model in cursor.available_models_display() {\n routes.push(ModelRoute {\n model,\n provider: \"Cursor\".to_string(),\n api_method: \"cursor\".to_string(),\n available: true,\n detail: String::new(),\n cheapness: None,\n });\n }\n }\n}\n\n/// AWS Bedrock models and inference profiles, including the\n/// credentials-configured-but-uninitialized case.\nfn append_bedrock_routes(provider: &MultiProvider, routes: &mut Vec<ModelRoute>) {\n if let Some(bedrock) = provider.bedrock_provider() {\n routes.extend(bedrock.model_routes());\n } else if bedrock::BedrockProvider::has_credentials() {\n let bedrock = bedrock::BedrockProvider::new();\n routes.extend(bedrock.model_routes().into_iter().map(|mut route| {\n if route.detail.trim().is_empty() {\n route.detail =\n \"credentials configured; provider will initialize on selection\".to_string();\n }\n route\n }));\n }\n}\n\n/// OpenRouter models with per-provider endpoint routes, plus the direct\n/// OpenAI-compatible runtime path that shares the OpenRouter transport.\nfn append_openrouter_routes(\n provider: &MultiProvider,\n routes: &mut Vec<ModelRoute>,\n stats: &mut OpenRouterRouteStats,\n) {\n let Some(openrouter) = provider.openrouter_provider() else {\n return;\n };\n let has_openrouter = true;\n let current_openrouter_model = openrouter.model();\n let supports_openrouter_provider_features = openrouter.supports_provider_routing_features();\n let mut scheduled_endpoint_refreshes = 0usize;\n for model in openrouter.available_models_display() {\n stats.models += 1;\n let cached = if supports_openrouter_provider_features {\n openrouter::load_endpoints_disk_cache_public(&model)\n } else {\n None\n };\n let cache_age = cached.as_ref().map(|(_, age)| *age);\n if supports_openrouter_provider_features\n && (model == current_openrouter_model || scheduled_endpoint_refreshes < 8)\n && openrouter.maybe_schedule_endpoint_refresh_for_display(\n &model,\n cache_age,\n \"model picker route hydration\",\n )\n {\n scheduled_endpoint_refreshes += 1;\n stats.scheduled_endpoint_refreshes += 1;\n }\n let age_str = cached.as_ref().map(|(_, age)| {\n if *age < 3600 {\n format!(\"{}m ago\", age / 60)\n } else if *age < 86400 {\n format!(\"{}h ago\", age / 3600)\n } else {\n format!(\"{}d ago\", age / 86400)\n }\n });\n // Auto route: hint which provider it would likely pick\n let auto_detail = cached\n .as_ref()\n .and_then(|(eps, _)| {\n eps.first().map(|ep| {\n let endpoint_detail = ep.detail_string();\n if endpoint_detail.trim().is_empty() {\n format!(\"→ {}\", ep.provider_name)\n } else {\n format!(\"→ {} · {}\", ep.provider_name, endpoint_detail)\n }\n })\n })\n .unwrap_or_default();\n if supports_openrouter_provider_features {\n routes.push(build_openrouter_auto_route(\n &model,\n has_openrouter,\n auto_detail,\n ));\n } else {\n let (provider, api_method, detail) = openrouter\n .direct_openai_compatible_route_parts()\n .unwrap_or_else(|| {\n (\n \"OpenAI-compatible\".to_string(),\n \"openai-compatible\".to_string(),\n \"custom endpoint\".to_string(),\n )\n });\n routes.push(ModelRoute {\n model: model.clone(),\n provider,\n api_method,\n available: has_openrouter,\n detail,\n cheapness: None,\n });\n }\n // Add per-provider routes from endpoints cache\n if supports_openrouter_provider_features && let Some((ref endpoints, _)) = cached {\n stats.endpoint_cache_hits += 1;\n let stale_suffix = age_str.as_deref().unwrap_or(\"\");\n for ep in endpoints {\n stats.endpoint_routes += 1;\n routes.push(build_openrouter_endpoint_route(\n &model,\n ep,\n has_openrouter,\n Some(stale_suffix),\n ));\n }\n }\n }\n\n // A direct OpenAI-compatible runtime (NVIDIA NIM, Groq, etc.) shares the\n // OpenRouter/OpenAI-compatible transport, but it is a distinct profile\n // from standard OpenRouter. Keep standard OpenRouter's catalog scoped to\n // the `openrouter` cache namespace so `/model` can switch back to it\n // without relabeling OpenRouter models as the active direct profile.\n if !supports_openrouter_provider_features && standard_openrouter_profile_configured() {\n // The shared OpenRouter/OpenAI-compatible slot is occupied by a direct\n // profile (e.g. NVIDIA NIM), so standard OpenRouter is never the active\n // provider and its `openrouter` namespace catalog is never refreshed by\n // the normal active-provider path. The background catalog scheduler\n // keeps that namespace fresh (issue #292); rendering only reads it.\n routes.extend(configured_standard_openrouter_profile_routes());\n }\n}\n\n/// Claude/OpenAI models reachable via OpenRouter as alternative routes.\nfn append_openrouter_alternative_routes(\n routes: &mut Vec<ModelRoute>,\n stats: &mut OpenRouterRouteStats,\n) {\n for model in known_anthropic_model_ids() {\n let or_model = format!(\"anthropic/{}\", model);\n if let Some((endpoints, _)) = openrouter::load_endpoints_disk_cache_public(&or_model) {\n stats.endpoint_cache_hits += 1;\n for ep in &endpoints {\n stats.endpoint_routes += 1;\n routes.push(build_openrouter_endpoint_route(&model, ep, true, None));\n }\n } else if openrouter::standard_catalog_lists_model(&or_model) != Some(false) {\n routes.push(build_openrouter_fallback_provider_route(\n &model,\n &or_model,\n \"Anthropic\",\n ));\n }\n }\n\n for model in ALL_OPENAI_MODELS {\n let or_model = format!(\"openai/{}\", model);\n if let Some((endpoints, _)) = openrouter::load_endpoints_disk_cache_public(&or_model) {\n stats.endpoint_cache_hits += 1;\n for ep in &endpoints {\n stats.endpoint_routes += 1;\n routes.push(build_openrouter_endpoint_route(model, ep, true, None));\n }\n } else if openrouter::standard_catalog_lists_model(&or_model) != Some(false) {\n // Skip fallback routes for models OpenRouter definitively does not\n // serve (e.g. openai/gpt-5.3-codex-spark) so the picker never\n // offers a route that would 400 at request time.\n routes.push(build_openrouter_fallback_provider_route(\n model, &or_model, \"OpenAI\",\n ));\n }\n }\n}\n\n/// Count routes per provider label (lowercased, spaces removed) so the catalog\n/// summary log shows where the picker entries came from.\nfn provider_route_counts(routes: &[ModelRoute]) -> std::collections::BTreeMap<String, usize> {\n let mut counts: std::collections::BTreeMap<String, usize> = std::collections::BTreeMap::new();\n for route in routes {\n let key = route.provider.trim().to_ascii_lowercase().replace(' ', \"_\");\n let key = if key.is_empty() {\n \"unknown\".to_string()\n } else {\n key\n };\n *counts.entry(key).or_insert(0) += 1;\n }\n counts\n}\n\n/// Emit a structured, non-secret summary of model-route building. Callers pass\n/// the credential-detection flags they already computed so the log explains why\n/// each provider's routes were or were not produced.\n#[allow(clippy::too_many_arguments)]\nfn log_model_routes_summary(\n phase: &str,\n routes: &[ModelRoute],\n routes_before_filter: usize,\n anthropic_oauth: bool,\n anthropic_api_key: bool,\n openai_oauth: bool,\n openai_api_key: bool,\n has_openrouter: bool,\n openrouter_provider_features: bool,\n direct_openai_compatible: bool,\n total_ms: u128,\n) {\n let available = routes.iter().filter(|route| route.available).count();\n let per_provider = provider_route_counts(routes)\n .into_iter()\n .map(|(provider, count)| format!(\"{provider}:{count}\"))\n .collect::<Vec<_>>()\n .join(\",\");\n\n crate::logging::event_info(\n \"model_routes_summary\",\n vec![\n (\"phase\", phase.to_string()),\n (\"routes_total\", routes.len().to_string()),\n (\"routes_available\", available.to_string()),\n (\"routes_before_filter\", routes_before_filter.to_string()),\n (\n \"routes_dropped\",\n routes_before_filter\n .saturating_sub(routes.len())\n .to_string(),\n ),\n (\"anthropic_oauth\", anthropic_oauth.to_string()),\n (\"anthropic_api\", anthropic_api_key.to_string()),\n (\"openai_oauth\", openai_oauth.to_string()),\n (\"openai_api\", openai_api_key.to_string()),\n (\"openrouter_configured\", has_openrouter.to_string()),\n (\n \"openrouter_provider_features\",\n openrouter_provider_features.to_string(),\n ),\n (\n \"direct_openai_compatible\",\n direct_openai_compatible.to_string(),\n ),\n (\"by_provider\", per_provider),\n (\"build_ms\", total_ms.to_string()),\n ],\n );\n}\n\npub fn remote_model_routes_fallback(\n remote_provider_name: Option<&str>,\n remote_available_entries: &[String],\n) -> Vec<ModelRoute> {\n if remote_provider_name.is_some_and(|name| {\n name.eq_ignore_ascii_case(crate::subscription_catalog::JCODE_PROVIDER_DISPLAY_NAME)\n }) {\n return remote_available_entries\n .iter()\n .filter(|model| is_listable_model_name(model))\n .map(|model| ModelRoute {\n model: model.clone(),\n provider: crate::subscription_catalog::JCODE_PROVIDER_DISPLAY_NAME.to_string(),\n api_method: crate::subscription_catalog::JCODE_ROUTE_API_METHOD.to_string(),\n available: true,\n detail: \"jcode subscription routing · managed server-side\".to_string(),\n cheapness: None,\n })\n .collect();\n }\n\n let auth = AuthStatus::check_fast();\n let mut routes = Vec::new();\n for model in remote_available_entries {\n if !is_listable_model_name(model) {\n continue;\n }\n\n let openrouter_catalog_model = openrouter_catalog_model_id(model);\n let openrouter_cached = openrouter_catalog_model\n .as_deref()\n .and_then(openrouter::load_endpoints_disk_cache_public);\n\n if super::bedrock::BedrockProvider::is_bedrock_model_id(model) {\n let available = auth.bedrock != AuthState::NotConfigured\n || super::bedrock::BedrockProvider::has_credentials();\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"AWS Bedrock\".to_string(),\n api_method: \"bedrock\".to_string(),\n available,\n detail: if available {\n String::new()\n } else {\n \"no Bedrock credentials or region; run /login bedrock\".to_string()\n },\n cheapness: None,\n });\n continue;\n }\n\n if model.contains('/') {\n let cached = openrouter_cached;\n let auto_detail = cached\n .as_ref()\n .and_then(|(eps, _)| eps.first().map(|ep| format!(\"→ {}\", ep.provider_name)))\n .unwrap_or_default();\n routes.push(build_openrouter_auto_route(\n model,\n auth.openrouter != AuthState::NotConfigured,\n auto_detail,\n ));\n if let Some((endpoints, age)) = cached {\n let age_str = if age < 3600 {\n format!(\"{}m ago\", age / 60)\n } else if age < 86400 {\n format!(\"{}h ago\", age / 3600)\n } else {\n format!(\"{}d ago\", age / 86400)\n };\n for ep in &endpoints {\n routes.push(build_openrouter_endpoint_route(\n model,\n ep,\n auth.openrouter != AuthState::NotConfigured,\n Some(&age_str),\n ));\n }\n }\n continue;\n }\n\n let mut added_any = false;\n\n if provider_for_model(model) == Some(\"claude\") {\n if auth.anthropic.has_oauth {\n let (available, detail) = anthropic_oauth_route_availability(model);\n routes.push(build_anthropic_oauth_route(model, available, detail));\n added_any = true;\n }\n // An Anthropic API key is an equally valid direct route. Without\n // this, a model that only reaches the picker via the names-only\n // fallback path (e.g. a newly released model whose detailed route\n // frame was oversized) shows an OAuth route but silently loses its\n // API-key route even though the key works.\n if auth.anthropic.has_api_key {\n let (available, detail) = anthropic_api_key_route_availability(model);\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"Anthropic\".to_string(),\n api_method: \"claude-api\".to_string(),\n available,\n detail,\n cheapness: cheapness_for_route(model, \"Anthropic\", \"claude-api\"),\n });\n added_any = true;\n }\n }\n\n if jcode_provider_core::model_id::matches_known_model(model, ALL_OPENAI_MODELS) {\n let availability = model_availability_for_account(model);\n let (available, detail) = if auth.openai == AuthState::NotConfigured {\n (false, \"no credentials\".to_string())\n } else {\n match availability.state {\n AccountModelAvailabilityState::Available => (true, String::new()),\n AccountModelAvailabilityState::Unavailable => (\n false,\n format_account_model_availability_detail(&availability)\n .unwrap_or_else(|| \"not available\".to_string()),\n ),\n AccountModelAvailabilityState::Unknown => (\n true,\n format_account_model_availability_detail(&availability)\n .unwrap_or_else(|| \"availability unknown\".to_string()),\n ),\n }\n };\n routes.push(build_openai_oauth_route(model, available, detail));\n added_any = true;\n }\n\n if auth.openrouter != AuthState::NotConfigured {\n let catalog_lists_model = openrouter_catalog_model\n .as_deref()\n .and_then(openrouter::standard_catalog_lists_model);\n match (provider_for_model(model), openrouter_cached.as_ref()) {\n (_, Some((endpoints, _age))) => {\n for ep in endpoints {\n routes.push(build_openrouter_endpoint_route(model, ep, true, None));\n }\n added_any = true;\n }\n // Skip fallback routes for models the OpenRouter catalog\n // definitively does not list (e.g. gpt-5.3-codex-spark).\n (Some(\"claude\"), None) if catalog_lists_model != Some(false) => {\n routes.push(build_openrouter_fallback_provider_route(\n model,\n openrouter_catalog_model.as_deref().unwrap_or(model),\n \"Anthropic\",\n ));\n added_any = true;\n }\n (Some(\"openai\"), None) if catalog_lists_model != Some(false) => {\n routes.push(build_openrouter_fallback_provider_route(\n model,\n openrouter_catalog_model.as_deref().unwrap_or(model),\n \"OpenAI\",\n ));\n added_any = true;\n }\n _ => {}\n }\n }\n\n if let Some(route) = remote_openai_compatible_route_for_model(model) {\n routes.push(route);\n added_any = true;\n }\n\n if !added_any\n && let Some(route) =\n remote_current_openai_compatible_route_for_model(remote_provider_name, model)\n {\n routes.push(route);\n added_any = true;\n }\n\n if !added_any && remote_model_should_offer_copilot_route(model) && !model.contains(\"[1m]\") {\n routes.push(build_copilot_route(\n model,\n auth.copilot == AuthState::Available || remote_model_is_server_copilot_only(model),\n String::new(),\n ));\n added_any = true;\n }\n\n if super::gemini::is_gemini_model_id(model) {\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"Gemini\".to_string(),\n api_method: \"code-assist-oauth\".to_string(),\n available: auth.gemini == AuthState::Available,\n detail: String::new(),\n cheapness: None,\n });\n added_any = true;\n }\n\n if !added_any {\n routes.push(ModelRoute {\n model: model.clone(),\n provider: \"unknown\".to_string(),\n api_method: \"unknown\".to_string(),\n available: false,\n detail: \"no matching configured provider route\".to_string(),\n cheapness: None,\n });\n }\n }\n routes\n}\n\npub fn remote_model_routes_lightweight_fallback(\n remote_provider_name: Option<&str>,\n remote_available_entries: &[String],\n current_model: &str,\n) -> Vec<ModelRoute> {\n let is_jcode_subscription = remote_provider_name.is_some_and(|name| {\n name.eq_ignore_ascii_case(crate::subscription_catalog::JCODE_PROVIDER_DISPLAY_NAME)\n });\n let provider = remote_provider_name\n .map(str::to_string)\n .unwrap_or_else(|| \"remote\".to_string());\n let mut routes = Vec::new();\n for model in remote_available_entries {\n if !is_listable_model_name(model) {\n continue;\n }\n routes.push(ModelRoute {\n model: model.clone(),\n provider: provider.clone(),\n api_method: if is_jcode_subscription {\n crate::subscription_catalog::JCODE_ROUTE_API_METHOD.to_string()\n } else {\n \"remote-catalog\".to_string()\n },\n available: true,\n detail: if is_jcode_subscription {\n \"jcode subscription routing · managed server-side\".to_string()\n } else {\n \"refreshing route details…\".to_string()\n },\n cheapness: None,\n });\n }\n\n if routes.is_empty() && !current_model.is_empty() && current_model != \"unknown\" {\n routes.push(ModelRoute {\n model: current_model.to_string(),\n provider,\n api_method: \"current\".to_string(),\n available: true,\n detail: \"refreshing model catalog…\".to_string(),\n cheapness: None,\n });\n }\n\n routes\n}\n\npub fn remote_current_openai_compatible_route_for_model(\n remote_provider_name: Option<&str>,\n model: &str,\n) -> Option<ModelRoute> {\n if model.trim().is_empty() || model.contains('/') || provider_for_model(model).is_some() {\n return None;\n }\n\n let provider_name = remote_provider_name?.trim();\n let profile_id =\n crate::provider_catalog::openai_compatible_profile_id_for_display_name(provider_name)?;\n let profile = crate::provider_catalog::openai_compatible_profile_by_id(profile_id)?;\n if !crate::provider_catalog::openai_compatible_profile_is_configured(profile) {\n return None;\n }\n let resolved = crate::provider_catalog::resolve_openai_compatible_profile(profile);\n\n Some(ModelRoute {\n model: model.to_string(),\n provider: resolved.display_name,\n api_method: format!(\"openai-compatible:{}\", resolved.id),\n available: true,\n detail: resolved.api_base,\n cheapness: None,\n })\n}\n\npub fn remote_model_should_offer_copilot_route(model: &str) -> bool {\n remote_openai_compatible_route_for_model(model).is_none()\n && (remote_model_is_server_copilot_only(model)\n || super::copilot::is_known_display_model(model))\n}\n\npub fn remote_openai_compatible_route_for_model(model: &str) -> Option<ModelRoute> {\n for profile in crate::provider_catalog::openai_compatible_profiles()\n .iter()\n .copied()\n {\n if !crate::provider_catalog::openai_compatible_profile_is_configured(profile) {\n continue;\n }\n let resolved = crate::provider_catalog::resolve_openai_compatible_profile(profile);\n let Some(from_live_catalog) = remote_openai_compatible_profile_models(&resolved, profile)\n .iter()\n .find_map(|candidate| (candidate.0 == model).then_some(candidate.1))\n else {\n continue;\n };\n let detail = if from_live_catalog {\n resolved.api_base.clone()\n } else if resolved.api_base.trim().is_empty() {\n \"fallback: static provider model list\".to_string()\n } else {\n format!(\n \"{}; fallback: static provider model list\",\n resolved.api_base\n )\n };\n return Some(ModelRoute {\n model: model.to_string(),\n provider: resolved.display_name,\n api_method: format!(\"openai-compatible:{}\", resolved.id),\n available: true,\n detail,\n cheapness: None,\n });\n }\n named_provider_profile_route_for_model(model)\n}\n\n/// Route for `model` when it belongs to a user-defined `[providers.<name>]`\n/// profile from config.toml.\n///\n/// Built-in OpenAI-compatible profiles are handled above; without this, a\n/// bare model id from a custom profile (e.g. a local MLX server) matches no\n/// known provider and falls through to the Copilot heuristic, which then\n/// labels it `Copilot` and builds a `copilot:<model>` id that no runtime can\n/// resolve (issue #694).\nfn named_provider_profile_route_for_model(model: &str) -> Option<ModelRoute> {\n named_provider_profile_route_for_model_in(model, &crate::config::config().providers)\n}\n\nfn named_provider_profile_route_for_model_in(\n model: &str,\n providers: &std::collections::BTreeMap<String, crate::config::NamedProviderConfig>,\n) -> Option<ModelRoute> {\n let model = model.trim();\n if model.is_empty() {\n return None;\n }\n for (profile_name, profile_config) in providers {\n if !named_provider_profile_routes(profile_name, profile_config)\n .iter()\n .any(|route| route.model == model)\n {\n continue;\n }\n let detail = if profile_config.base_url.trim().is_empty() {\n \"configured provider profile\".to_string()\n } else {\n profile_config.base_url.trim().to_string()\n };\n return Some(ModelRoute {\n model: model.to_string(),\n provider: profile_name.clone(),\n api_method: format!(\"openai-compatible:{}\", profile_name),\n available: true,\n detail,\n cheapness: None,\n });\n }\n None\n}\n\nfn remote_openai_compatible_profile_models(\n resolved: &crate::provider_catalog::ResolvedOpenAiCompatibleProfile,\n profile: crate::provider_catalog::OpenAiCompatibleProfile,\n) -> Vec<(String, bool)> {\n let mut models = Vec::new();\n let mut push = |model: String, from_live_catalog: bool| {\n let model = model.trim().to_string();\n if !model.is_empty() && !models.iter().any(|(existing, _)| existing == &model) {\n models.push((model, from_live_catalog));\n }\n };\n\n if let Some(cache) =\n jcode_provider_openrouter::load_disk_cache_entry_for_namespace(&resolved.id)\n {\n let source_matches = cache\n .source_api_base\n .as_deref()\n .and_then(crate::provider_catalog::normalize_api_base)\n == crate::provider_catalog::normalize_api_base(&resolved.api_base);\n if source_matches {\n for model in cache.models {\n push(model.id, true);\n }\n }\n }\n\n for model in crate::provider_catalog::openai_compatible_profile_static_models(profile) {\n push(model, false);\n }\n\n models\n}\n\npub fn remote_model_is_server_copilot_only(model: &str) -> bool {\n !model.is_empty()\n && !model.contains('/')\n && remote_openai_compatible_route_for_model(model).is_none()\n && !matches!(\n provider_for_model(model),\n Some(\"claude\" | \"openai\" | \"gemini\" | \"cursor\")\n )\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::auth::{AuthState, ProviderAuth};\n\n struct EnvGuard {\n vars: Vec<(&'static str, Option<std::ffi::OsString>)>,\n _temp: tempfile::TempDir,\n _lock: std::sync::MutexGuard<'static, ()>,\n }\n\n impl EnvGuard {\n fn new() -> Self {\n let lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let vars = vec![\n (\"JCODE_HOME\", std::env::var_os(\"JCODE_HOME\")),\n (\"OPENCODE_API_KEY\", std::env::var_os(\"OPENCODE_API_KEY\")),\n ];\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"sk-test-opencode\");\n Self {\n vars,\n _temp: temp,\n _lock: lock,\n }\n }\n\n fn save_opencode_cache(&self, source_api_base: &str, model_ids: &[&str]) {\n let jcode_home = std::env::var_os(\"JCODE_HOME\").expect(\"JCODE_HOME set\");\n let cache_dir = std::path::PathBuf::from(jcode_home).join(\"cache\");\n std::fs::create_dir_all(&cache_dir).expect(\"create cache dir\");\n let cache = jcode_provider_openrouter::DiskCache {\n cached_at: jcode_provider_openrouter::current_unix_secs()\n .expect(\"current unix time\"),\n source_api_base: Some(source_api_base.to_string()),\n models: model_ids\n .iter()\n .map(|id| jcode_provider_openrouter::ModelInfo {\n id: (*id).to_string(),\n name: String::new(),\n context_length: None,\n pricing: jcode_provider_openrouter::ModelPricing::default(),\n created: None,\n })\n .collect(),\n };\n std::fs::write(\n cache_dir.join(\"opencode_models.json\"),\n serde_json::to_string(&cache).expect(\"serialize cache\"),\n )\n .expect(\"write cache\");\n }\n }\n\n impl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in self.vars.drain(..) {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n }\n\n /// Issue #694: a bare model id from a user-defined `[providers.<name>]`\n /// profile must resolve to that profile, not fall through to the Copilot\n /// heuristic (which mislabels it and builds an unresolvable `copilot:` id).\n #[test]\n fn named_provider_profile_model_routes_to_its_own_profile() {\n let mut providers = std::collections::BTreeMap::new();\n providers.insert(\n \"omlx\".to_string(),\n crate::config::NamedProviderConfig {\n base_url: \"http://127.0.0.1:18000/v1\".to_string(),\n default_model: Some(\"KAT-Coder-V2.5-Dev-OptiQ-4bit\".to_string()),\n ..Default::default()\n },\n );\n\n let route =\n named_provider_profile_route_for_model_in(\"KAT-Coder-V2.5-Dev-OptiQ-4bit\", &providers)\n .expect(\"custom profile model must resolve to its profile\");\n assert_eq!(route.provider, \"omlx\");\n assert_eq!(route.api_method, \"openai-compatible:omlx\");\n assert_eq!(route.detail, \"http://127.0.0.1:18000/v1\");\n assert!(!route.api_method.starts_with(\"copilot\"));\n assert!(matches!(\n route.api_method_kind(),\n jcode_provider_core::ModelRouteApiMethod::OpenAiCompatible { .. }\n ));\n }\n\n #[test]\n fn unknown_model_does_not_match_named_provider_profiles() {\n let mut providers = std::collections::BTreeMap::new();\n providers.insert(\n \"omlx\".to_string(),\n crate::config::NamedProviderConfig {\n base_url: \"http://127.0.0.1:18000/v1\".to_string(),\n default_model: Some(\"KAT-Coder-V2.5-Dev-OptiQ-4bit\".to_string()),\n ..Default::default()\n },\n );\n\n assert!(\n named_provider_profile_route_for_model_in(\"some-other-model\", &providers).is_none()\n );\n assert!(named_provider_profile_route_for_model_in(\"\", &providers).is_none());\n }\n\n #[test]\n fn simplified_anthropic_routes_preserve_oauth_vs_api_key_state_space() {\n for (has_oauth, has_api_key, expected_methods) in [\n (true, false, vec![\"claude-oauth\"]),\n (false, true, vec![\"claude-api\"]),\n (true, true, vec![\"claude-oauth\", \"claude-api\"]),\n (false, false, vec![\"claude-oauth\"]),\n ] {\n let auth = AuthStatus {\n anthropic: ProviderAuth {\n state: if has_oauth || has_api_key {\n AuthState::Available\n } else {\n AuthState::NotConfigured\n },\n has_oauth,\n oauth_state: if has_oauth {\n AuthState::Available\n } else {\n AuthState::NotConfigured\n },\n has_api_key,\n },\n ..AuthStatus::default()\n };\n let mut routes = Vec::new();\n\n append_simplified_anthropic_model_routes(\n &mut routes,\n \"claude-opus-4-6\".to_string(),\n &auth,\n );\n\n let methods = routes\n .iter()\n .map(|route| route.api_method.as_str())\n .collect::<Vec<_>>();\n assert_eq!(\n methods, expected_methods,\n \"oauth={has_oauth} api={has_api_key}\"\n );\n assert!(routes.iter().all(|route| route.provider == \"Anthropic\"));\n assert_eq!(\n routes.iter().all(|route| route.available),\n has_oauth || has_api_key\n );\n }\n }\n\n #[test]\n fn remote_compatible_route_uses_live_cache_and_does_not_mark_fallback() {\n let guard = EnvGuard::new();\n guard.save_opencode_cache(\"https://opencode.ai/zen/v1\", &[\"qwen3.6-plus\"]);\n\n let route = remote_openai_compatible_route_for_model(\"qwen3.6-plus\")\n .expect(\"live-cache-only OpenCode model should be routed\");\n\n assert_eq!(route.provider, \"OpenCode Zen\");\n assert_eq!(route.api_method, \"openai-compatible:opencode\");\n assert_eq!(route.detail, \"https://opencode.ai/zen/v1\");\n assert!(!route.detail.contains(\"fallback\"));\n }\n\n #[test]\n fn remote_compatible_route_marks_static_model_list_fallback() {\n let _guard = EnvGuard::new();\n\n let route = remote_openai_compatible_route_for_model(\"glm-4.7\")\n .expect(\"static OpenCode fallback model should be routed\");\n\n assert_eq!(route.provider, \"OpenCode Zen\");\n assert!(\n route\n .detail\n .contains(\"fallback: static provider model list\")\n );\n }\n\n #[test]\n fn remote_compatible_route_ignores_live_cache_from_wrong_api_base() {\n let guard = EnvGuard::new();\n guard.save_opencode_cache(\"https://wrong.example.test/v1\", &[\"qwen3.6-plus\"]);\n\n assert!(remote_openai_compatible_route_for_model(\"qwen3.6-plus\").is_none());\n }\n\n fn save_openrouter_catalog_cache(model_ids: &[&str]) {\n let jcode_home = std::env::var_os(\"JCODE_HOME\").expect(\"JCODE_HOME set\");\n let cache_dir = std::path::PathBuf::from(jcode_home).join(\"cache\");\n std::fs::create_dir_all(&cache_dir).expect(\"create cache dir\");\n let cache = jcode_provider_openrouter::DiskCache {\n cached_at: jcode_provider_openrouter::current_unix_secs().expect(\"current unix time\"),\n source_api_base: None,\n models: model_ids\n .iter()\n .map(|id| jcode_provider_openrouter::ModelInfo {\n id: (*id).to_string(),\n name: String::new(),\n context_length: None,\n pricing: jcode_provider_openrouter::ModelPricing::default(),\n created: None,\n })\n .collect(),\n };\n std::fs::write(\n cache_dir.join(\"openrouter_models.json\"),\n serde_json::to_string(&cache).expect(\"serialize cache\"),\n )\n .expect(\"write cache\");\n }\n\n /// OpenRouter alternative routes must not be fabricated for models the\n /// OpenRouter catalog definitively does not list (e.g. the\n /// ChatGPT-exclusive `gpt-5.3-codex-spark`), while staying optimistic\n /// when no catalog cache exists yet.\n #[test]\n fn openrouter_alternative_routes_skip_models_absent_from_catalog() {\n let _guard = EnvGuard::new();\n\n // No catalog cache: optimistic, spark gets a fallback route.\n let mut routes = Vec::new();\n let mut stats = OpenRouterRouteStats::default();\n append_openrouter_alternative_routes(&mut routes, &mut stats);\n assert!(\n routes\n .iter()\n .any(|r| r.model == \"gpt-5.3-codex-spark\" && r.api_method == \"openrouter\"),\n \"without a catalog cache the fallback route stays optimistic\"\n );\n\n // Fresh catalog listing codex but not spark: spark route is dropped.\n save_openrouter_catalog_cache(&[\"openai/gpt-5.3-codex\", \"openai/gpt-5.5\"]);\n let mut routes = Vec::new();\n let mut stats = OpenRouterRouteStats::default();\n append_openrouter_alternative_routes(&mut routes, &mut stats);\n assert!(\n !routes\n .iter()\n .any(|r| r.model == \"gpt-5.3-codex-spark\" && r.api_method == \"openrouter\"),\n \"catalog-confirmed-absent model must not get an OpenRouter fallback route\"\n );\n assert!(\n routes\n .iter()\n .any(|r| r.model == \"gpt-5.3-codex\" && r.api_method == \"openrouter\"),\n \"catalog-listed model keeps its OpenRouter fallback route\"\n );\n }\n}\n"}}, "crates/jcode-base/src/message.rs": {"25a5cbb7266f31535ca7544b3ec13336abbf57f3": {"t": 1785745833.3269238, "n": 14930, "h": "b1821dc77abfd5a6", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, cache_relevant_message_hashes,\n cache_relevant_message_value, cache_relevant_messages, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown, strip_ansi_escape_sequences,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\n/// Persist the model's reasoning for an assistant turn.\n///\n/// This always keeps a readable, history-only copy of the reasoning in the\n/// transcript (`ContentBlock::ReasoningTrace`) so the thinking can be recalled\n/// or debugged later. When `store_replay_context` is set, it *additionally*\n/// stores the provider-specific replay block (`AnthropicThinking` /\n/// `Reasoning`) that the provider needs echoed back on subsequent turns. To\n/// avoid storing the same readable text twice, the history trace is skipped\n/// when the replay block already captured the identical readable reasoning.\npub fn push_reasoning_blocks(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n store_replay_context: bool,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n // Whether the replay block we stored already contains the readable text.\n let mut readable_replay_stored = false;\n if store_replay_context {\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n readable_replay_stored = true;\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI native reasoning items carry encrypted content, not readable\n // text, so a separate history trace is still required below.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n readable_replay_stored = true;\n }\n }\n\n if !readable_replay_stored {\n blocks.push(ContentBlock::ReasoningTrace {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let (media_type, data_b64) = generated_image_payload(path, output_format)?;\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\n/// Convert a provider-native generated image into the same rendered-image\n/// representation used by image-producing tools. The tool-call anchor keeps\n/// the image beside the synthetic `image_generation` row in the transcript.\npub fn generated_image_rendered_image(\n id: &str,\n path: &str,\n output_format: &str,\n) -> Option<jcode_session_types::RenderedImage> {\n let (media_type, data) = generated_image_payload(path, output_format)?;\n Some(jcode_session_types::RenderedImage {\n media_type,\n data,\n label: Some(path.to_string()),\n source: jcode_session_types::RenderedImageSource::ToolResult {\n tool_name: GENERATED_IMAGE_TOOL_NAME.to_string(),\n },\n anchor: Some(jcode_session_types::RenderedImageAnchor::ToolCall { id: id.to_string() }),\n })\n}\n\nfn generated_image_payload(path: &str, output_format: &str) -> Option<(String, String)> {\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image payload path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n Some((media_type, data_b64))\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "0e5cc8445cddc7eb1bfcceab3fc88e1681852136": {"t": 1785756982.4152634, "n": 14930, "h": "b1821dc77abfd5a6", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, cache_relevant_message_hashes,\n cache_relevant_message_value, cache_relevant_messages, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown, strip_ansi_escape_sequences,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\n/// Persist the model's reasoning for an assistant turn.\n///\n/// This always keeps a readable, history-only copy of the reasoning in the\n/// transcript (`ContentBlock::ReasoningTrace`) so the thinking can be recalled\n/// or debugged later. When `store_replay_context` is set, it *additionally*\n/// stores the provider-specific replay block (`AnthropicThinking` /\n/// `Reasoning`) that the provider needs echoed back on subsequent turns. To\n/// avoid storing the same readable text twice, the history trace is skipped\n/// when the replay block already captured the identical readable reasoning.\npub fn push_reasoning_blocks(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n store_replay_context: bool,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n // Whether the replay block we stored already contains the readable text.\n let mut readable_replay_stored = false;\n if store_replay_context {\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n readable_replay_stored = true;\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI native reasoning items carry encrypted content, not readable\n // text, so a separate history trace is still required below.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n readable_replay_stored = true;\n }\n }\n\n if !readable_replay_stored {\n blocks.push(ContentBlock::ReasoningTrace {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let (media_type, data_b64) = generated_image_payload(path, output_format)?;\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\n/// Convert a provider-native generated image into the same rendered-image\n/// representation used by image-producing tools. The tool-call anchor keeps\n/// the image beside the synthetic `image_generation` row in the transcript.\npub fn generated_image_rendered_image(\n id: &str,\n path: &str,\n output_format: &str,\n) -> Option<jcode_session_types::RenderedImage> {\n let (media_type, data) = generated_image_payload(path, output_format)?;\n Some(jcode_session_types::RenderedImage {\n media_type,\n data,\n label: Some(path.to_string()),\n source: jcode_session_types::RenderedImageSource::ToolResult {\n tool_name: GENERATED_IMAGE_TOOL_NAME.to_string(),\n },\n anchor: Some(jcode_session_types::RenderedImageAnchor::ToolCall { id: id.to_string() }),\n })\n}\n\nfn generated_image_payload(path: &str, output_format: &str) -> Option<(String, String)> {\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image payload path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n Some((media_type, data_b64))\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "8afc4843ed3503d452a05e93846182614c93447a": {"t": 1785756983.9331126, "n": 14901, "h": "e34e37ff6198929b", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, cache_relevant_message_hashes,\n cache_relevant_message_value, cache_relevant_messages, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\n/// Persist the model's reasoning for an assistant turn.\n///\n/// This always keeps a readable, history-only copy of the reasoning in the\n/// transcript (`ContentBlock::ReasoningTrace`) so the thinking can be recalled\n/// or debugged later. When `store_replay_context` is set, it *additionally*\n/// stores the provider-specific replay block (`AnthropicThinking` /\n/// `Reasoning`) that the provider needs echoed back on subsequent turns. To\n/// avoid storing the same readable text twice, the history trace is skipped\n/// when the replay block already captured the identical readable reasoning.\npub fn push_reasoning_blocks(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n store_replay_context: bool,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n // Whether the replay block we stored already contains the readable text.\n let mut readable_replay_stored = false;\n if store_replay_context {\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n readable_replay_stored = true;\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI native reasoning items carry encrypted content, not readable\n // text, so a separate history trace is still required below.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n readable_replay_stored = true;\n }\n }\n\n if !readable_replay_stored {\n blocks.push(ContentBlock::ReasoningTrace {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let (media_type, data_b64) = generated_image_payload(path, output_format)?;\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\n/// Convert a provider-native generated image into the same rendered-image\n/// representation used by image-producing tools. The tool-call anchor keeps\n/// the image beside the synthetic `image_generation` row in the transcript.\npub fn generated_image_rendered_image(\n id: &str,\n path: &str,\n output_format: &str,\n) -> Option<jcode_session_types::RenderedImage> {\n let (media_type, data) = generated_image_payload(path, output_format)?;\n Some(jcode_session_types::RenderedImage {\n media_type,\n data,\n label: Some(path.to_string()),\n source: jcode_session_types::RenderedImageSource::ToolResult {\n tool_name: GENERATED_IMAGE_TOOL_NAME.to_string(),\n },\n anchor: Some(jcode_session_types::RenderedImageAnchor::ToolCall { id: id.to_string() }),\n })\n}\n\nfn generated_image_payload(path: &str, output_format: &str) -> Option<(String, String)> {\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image payload path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n Some((media_type, data_b64))\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "444fc358e66d3d90996ff382229213f0247103b4": {"t": 1785756985.1617446, "n": 13898, "h": "3f9da17c8042941d", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, cache_relevant_message_hashes,\n cache_relevant_message_value, cache_relevant_messages, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\n/// Persist the model's reasoning for an assistant turn.\n///\n/// This always keeps a readable, history-only copy of the reasoning in the\n/// transcript (`ContentBlock::ReasoningTrace`) so the thinking can be recalled\n/// or debugged later. When `store_replay_context` is set, it *additionally*\n/// stores the provider-specific replay block (`AnthropicThinking` /\n/// `Reasoning`) that the provider needs echoed back on subsequent turns. To\n/// avoid storing the same readable text twice, the history trace is skipped\n/// when the replay block already captured the identical readable reasoning.\npub fn push_reasoning_blocks(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n store_replay_context: bool,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n // Whether the replay block we stored already contains the readable text.\n let mut readable_replay_stored = false;\n if store_replay_context {\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n readable_replay_stored = true;\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI native reasoning items carry encrypted content, not readable\n // text, so a separate history trace is still required below.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n readable_replay_stored = true;\n }\n }\n\n if !readable_replay_stored {\n blocks.push(ContentBlock::ReasoningTrace {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image visual context path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image visual context path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "7f6f3aa1dd1a800ccdb30234d3f5d7b4431c66e2": {"t": 1785756986.8484175, "n": 13808, "h": "5488b0159e8de1be", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\n/// Persist the model's reasoning for an assistant turn.\n///\n/// This always keeps a readable, history-only copy of the reasoning in the\n/// transcript (`ContentBlock::ReasoningTrace`) so the thinking can be recalled\n/// or debugged later. When `store_replay_context` is set, it *additionally*\n/// stores the provider-specific replay block (`AnthropicThinking` /\n/// `Reasoning`) that the provider needs echoed back on subsequent turns. To\n/// avoid storing the same readable text twice, the history trace is skipped\n/// when the replay block already captured the identical readable reasoning.\npub fn push_reasoning_blocks(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n store_replay_context: bool,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n // Whether the replay block we stored already contains the readable text.\n let mut readable_replay_stored = false;\n if store_replay_context {\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n readable_replay_stored = true;\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI native reasoning items carry encrypted content, not readable\n // text, so a separate history trace is still required below.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n readable_replay_stored = true;\n }\n }\n\n if !readable_replay_stored {\n blocks.push(ContentBlock::ReasoningTrace {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image visual context path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image visual context path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "04bdb4276e8a70a37ba1705a12afbccc61b69acf": {"t": 1785756988.3202562, "n": 14717, "h": "8745dfe31631b0fc", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\npub fn push_reasoning_content_block(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI reasoning is stored as native `OpenAIReasoning` output items\n // when available, so avoid adding a duplicate generic reasoning block.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n }\n}\n\n/// Persist the model's reasoning for an assistant turn.\n///\n/// This always keeps a readable, history-only copy of the reasoning in the\n/// transcript (`ContentBlock::ReasoningTrace`) so the thinking can be recalled\n/// or debugged later. When `store_replay_context` is set, it *additionally*\n/// stores the provider-specific replay block (`AnthropicThinking` /\n/// `Reasoning`) that the provider needs echoed back on subsequent turns. To\n/// avoid storing the same readable text twice, the history trace is skipped\n/// when the replay block already captured the identical readable reasoning.\npub fn push_reasoning_blocks(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n store_replay_context: bool,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n // Whether the replay block we stored already contains the readable text.\n let mut readable_replay_stored = false;\n if store_replay_context {\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n readable_replay_stored = true;\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI native reasoning items carry encrypted content, not readable\n // text, so a separate history trace is still required below.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n readable_replay_stored = true;\n }\n }\n\n if !readable_replay_stored {\n blocks.push(ContentBlock::ReasoningTrace {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image visual context path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image visual context path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "8c93c0537814e074409bf0d15bf8d5039c208c59": {"t": 1785756989.9145017, "n": 12739, "h": "e2c8dbcf4323a215", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\npub fn push_reasoning_content_block(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI reasoning is stored as native `OpenAIReasoning` output items\n // when available, so avoid adding a duplicate generic reasoning block.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image visual context path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image visual context path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "4aec863e2481c33ce7eb1beed98a935f92c90d08": {"t": 1785756991.1216288, "n": 12739, "h": "57a5289d1485afa3", "c": "use jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse crate::logging;\nuse base64::Engine as _;\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\npub fn push_reasoning_content_block(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI reasoning is stored as native `OpenAIReasoning` output items\n // when available, so avoid adding a duplicate generic reasoning block.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image visual context path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image visual context path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}, "eca1793180e9f2afaccac22b296d0eee84cc3577": {"t": 1785846942.7213466, "n": 14930, "h": "b1821dc77abfd5a6", "c": "use crate::logging;\nuse base64::Engine as _;\nuse jcode_background_types::{\n BackgroundTaskCompleted, BackgroundTaskProgressEvent, BackgroundTaskStatus,\n};\nuse regex::Regex;\nuse std::collections::HashSet;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\npub use jcode_message_types::{\n CacheControl, ConnectionPhase, ContentBlock, InputShellResult, Message, Role, StreamEvent,\n TOOL_OUTPUT_MISSING_TEXT, ToolCall, ToolDefinition, cache_relevant_message_hashes,\n cache_relevant_message_value, cache_relevant_messages, ends_with_fresh_user_turn,\n extend_stable_hash, messages_with_dynamic_system_context, sanitize_tool_id,\n stable_message_hash,\n};\n\nmod notifications;\n\npub use notifications::{\n ParsedBackgroundTaskNotification, ParsedBackgroundTaskProgressNotification,\n background_task_display_label, background_task_status_notice,\n format_background_task_notification_markdown, format_background_task_progress_markdown,\n format_input_shell_result_markdown, format_model_refresh_progress_markdown,\n input_shell_status_notice, parse_background_task_notification_markdown,\n parse_background_task_progress_notification_markdown, strip_ansi_escape_sequences,\n};\n\nfn compile_static_regex(pattern: &str) -> Option<Regex> {\n match Regex::new(pattern) {\n Ok(regex) => Some(regex),\n Err(err) => {\n logging::error(&format!(\"failed to compile static message regex: {err}\"));\n eprintln!(\"jcode: failed to compile static regex: {err}\");\n None\n }\n }\n}\n\nfn compile_static_regexes(patterns: &[&str]) -> Vec<Regex> {\n patterns\n .iter()\n .filter_map(|pattern| compile_static_regex(pattern))\n .collect()\n}\n\n/// Redact likely secrets from persisted tool output.\n///\n/// This is a best-effort safeguard for local session history files. It targets\n/// high-confidence token/key patterns and common `KEY=VALUE` assignments used by\n/// auth flows.\npub fn redact_secrets(text: &str) -> String {\n // Fast path to avoid regex work for most tool outputs.\n let lower = text.to_ascii_lowercase();\n\n if !text.contains(\"sk-\")\n && !text.contains(\"ghp_\")\n && !text.contains(\"github_pat_\")\n && !text.contains(\"AIza\")\n && !text.contains(\"ya29.\")\n && !text.contains(\"xox\")\n && !lower.contains(\"api_key\")\n && !lower.contains(\"token\")\n {\n logging::debug(\"secret redaction fast path skipped regex scan\");\n return text.to_string();\n }\n\n logging::debug(&format!(\n \"running secret redaction scan bytes={}\",\n text.len()\n ));\n\n static DIRECT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n static ASSIGNMENT_PATTERNS: OnceLock<Vec<Regex>> = OnceLock::new();\n\n let direct_patterns = DIRECT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"sk-ant-(?:oat|ort)01-[A-Za-z0-9_-]{20,}\",\n r\"sk-or-v1-[A-Za-z0-9_-]{20,}\",\n r\"ghp_[A-Za-z0-9]{20,}\",\n r\"github_pat_[A-Za-z0-9_]{20,}\",\n r\"ya29\\.[A-Za-z0-9._-]{20,}\",\n r\"AIza[0-9A-Za-z_-]{20,}\",\n r\"xox[baprs]-[A-Za-z0-9-]{10,}\",\n ])\n });\n\n let assignment_patterns = ASSIGNMENT_PATTERNS.get_or_init(|| {\n compile_static_regexes(&[\n r\"(?m)^\\s*(OPENROUTER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENCODE_GO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZHIPU_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ZAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(302AI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(BASETEN_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CORTECS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPSEEK_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(FIRMWARE_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(HF_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MOONSHOT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(NEBIUS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(SCALEWAY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(STACKIT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GROQ_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(MISTRAL_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(PERPLEXITY_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(TOGETHER_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(DEEPINFRA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(XAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(LMSTUDIO_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OLLAMA_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CHUTES_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CEREBRAS_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_COMPAT_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(ANTHROPIC_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(AZURE_OPENAI_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(CURSOR_API_KEY\\s*=\\s*)[^\\r\\n]+\",\n r\"(?m)^\\s*(GITHUB_TOKEN\\s*=\\s*)[^\\r\\n]+\",\n ])\n });\n\n let mut redacted = text.to_string();\n let mut redacted_keys: HashSet<String> = [\n \"OPENROUTER_API_KEY\",\n \"OPENCODE_API_KEY\",\n \"OPENCODE_GO_API_KEY\",\n \"ZHIPU_API_KEY\",\n \"ZAI_API_KEY\",\n \"302AI_API_KEY\",\n \"BASETEN_API_KEY\",\n \"CORTECS_API_KEY\",\n \"DEEPSEEK_API_KEY\",\n \"FIRMWARE_API_KEY\",\n \"HF_TOKEN\",\n \"MOONSHOT_API_KEY\",\n \"NEBIUS_API_KEY\",\n \"SCALEWAY_API_KEY\",\n \"STACKIT_API_KEY\",\n \"GROQ_API_KEY\",\n \"MISTRAL_API_KEY\",\n \"PERPLEXITY_API_KEY\",\n \"TOGETHER_API_KEY\",\n \"DEEPINFRA_API_KEY\",\n \"XAI_API_KEY\",\n \"LMSTUDIO_API_KEY\",\n \"OLLAMA_API_KEY\",\n \"CHUTES_API_KEY\",\n \"CEREBRAS_API_KEY\",\n \"OPENAI_COMPAT_API_KEY\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AZURE_OPENAI_API_KEY\",\n \"CURSOR_API_KEY\",\n \"GITHUB_TOKEN\",\n ]\n .iter()\n .map(|k| (*k).to_string())\n .collect();\n\n for re in direct_patterns {\n redacted = re.replace_all(&redacted, \"[REDACTED_SECRET]\").into_owned();\n }\n\n for re in assignment_patterns {\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n\n // Also redact custom API key variable names configured at runtime.\n for source in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n ] {\n let Some(key_name) = std::env::var(source)\n .ok()\n .map(|v| v.trim().to_string())\n .filter(|v| !v.is_empty())\n else {\n continue;\n };\n\n if !key_name\n .chars()\n .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')\n {\n logging::warn(&format!(\n \"ignoring invalid custom secret key name from {source}\"\n ));\n continue;\n }\n if !redacted_keys.insert(key_name.clone()) {\n continue;\n }\n\n let pattern = format!(r\"(?m)^\\s*({}\\s*=\\s*)[^\\r\\n]+\", regex::escape(&key_name));\n if let Ok(re) = Regex::new(&pattern) {\n logging::debug(&format!(\n \"adding custom secret redaction pattern for key={key_name}\"\n ));\n redacted = re\n .replace_all(&redacted, \"${1}[REDACTED_SECRET]\")\n .into_owned();\n }\n }\n\n if redacted != text {\n logging::info(\"redacted secrets from message text\");\n }\n\n redacted\n}\n\npub const GENERATED_IMAGE_TOOL_NAME: &str = \"image_generation\";\npub const GENERATED_IMAGE_MAX_AUTO_VISION_BYTES: u64 = 20 * 1024 * 1024;\n\n/// Persist the model's reasoning for an assistant turn.\n///\n/// This always keeps a readable, history-only copy of the reasoning in the\n/// transcript (`ContentBlock::ReasoningTrace`) so the thinking can be recalled\n/// or debugged later. When `store_replay_context` is set, it *additionally*\n/// stores the provider-specific replay block (`AnthropicThinking` /\n/// `Reasoning`) that the provider needs echoed back on subsequent turns. To\n/// avoid storing the same readable text twice, the history trace is skipped\n/// when the replay block already captured the identical readable reasoning.\npub fn push_reasoning_blocks(\n blocks: &mut Vec<ContentBlock>,\n provider_name: &str,\n reasoning_content: &str,\n reasoning_signature: Option<&str>,\n store_replay_context: bool,\n) {\n if reasoning_content.is_empty() {\n return;\n }\n\n // Whether the replay block we stored already contains the readable text.\n let mut readable_replay_stored = false;\n if store_replay_context {\n if provider_name.eq_ignore_ascii_case(\"anthropic\") {\n if let Some(signature) = reasoning_signature.filter(|s| !s.is_empty()) {\n blocks.push(ContentBlock::AnthropicThinking {\n thinking: reasoning_content.to_string(),\n signature: signature.to_string(),\n });\n readable_replay_stored = true;\n }\n } else if provider_name.eq_ignore_ascii_case(\"openai\") {\n // OpenAI native reasoning items carry encrypted content, not readable\n // text, so a separate history trace is still required below.\n } else {\n blocks.push(ContentBlock::Reasoning {\n text: reasoning_content.to_string(),\n });\n readable_replay_stored = true;\n }\n }\n\n if !readable_replay_stored {\n blocks.push(ContentBlock::ReasoningTrace {\n text: reasoning_content.to_string(),\n });\n }\n}\n\npub fn generated_image_tool_input(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> serde_json::Value {\n logging::debug(&format!(\n \"building generated image tool input path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n serde_json::json!({\n \"path\": path,\n \"metadata_path\": metadata_path,\n \"output_format\": output_format,\n \"revised_prompt\": revised_prompt,\n })\n}\n\npub fn generated_image_summary(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> String {\n logging::debug(&format!(\n \"building generated image summary path={path} format={output_format} has_metadata={} has_revised_prompt={}\",\n metadata_path.is_some(),\n revised_prompt.is_some()\n ));\n let mut summary = format!(\"Generated image ({}) saved to `{}`.\", output_format, path);\n if let Some(metadata_path) = metadata_path {\n summary.push_str(&format!(\"\\nMetadata saved to `{}`.\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|prompt| !prompt.trim().is_empty()) {\n summary.push_str(\"\\n\\nRevised prompt:\\n\");\n summary.push_str(revised_prompt.trim());\n }\n summary\n}\n\npub fn generated_image_visual_context_blocks(\n path: &str,\n metadata_path: Option<&str>,\n output_format: &str,\n revised_prompt: Option<&str>,\n) -> Option<Vec<ContentBlock>> {\n logging::debug(&format!(\n \"building generated image visual context path={path} format={output_format}\"\n ));\n let (media_type, data_b64) = generated_image_payload(path, output_format)?;\n let mut reminder = format!(\n \"<system-reminder>\\nA provider-native image generation call created `{}`. Jcode attached the image pixels as visual context for future turns because the active provider supports image input and the file is under the safe {} MB limit.\\nFormat: {}\",\n path,\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES / 1024 / 1024,\n output_format,\n );\n if let Some(metadata_path) = metadata_path.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(&format!(\"\\nMetadata: {}\", metadata_path));\n }\n if let Some(revised_prompt) = revised_prompt.filter(|value| !value.trim().is_empty()) {\n reminder.push_str(\"\\nRevised prompt:\\n\");\n reminder.push_str(revised_prompt.trim());\n }\n reminder.push_str(\"\\n</system-reminder>\");\n\n Some(vec![\n ContentBlock::Text {\n text: reminder,\n cache_control: None,\n },\n ContentBlock::Image {\n media_type,\n data: data_b64,\n },\n ])\n}\n\n/// Convert a provider-native generated image into the same rendered-image\n/// representation used by image-producing tools. The tool-call anchor keeps\n/// the image beside the synthetic `image_generation` row in the transcript.\npub fn generated_image_rendered_image(\n id: &str,\n path: &str,\n output_format: &str,\n) -> Option<jcode_session_types::RenderedImage> {\n let (media_type, data) = generated_image_payload(path, output_format)?;\n Some(jcode_session_types::RenderedImage {\n media_type,\n data,\n label: Some(path.to_string()),\n source: jcode_session_types::RenderedImageSource::ToolResult {\n tool_name: GENERATED_IMAGE_TOOL_NAME.to_string(),\n },\n anchor: Some(jcode_session_types::RenderedImageAnchor::ToolCall { id: id.to_string() }),\n })\n}\n\nfn generated_image_payload(path: &str, output_format: &str) -> Option<(String, String)> {\n let path_ref = Path::new(path);\n let metadata = std::fs::metadata(path_ref).ok()?;\n if !metadata.is_file() || metadata.len() > GENERATED_IMAGE_MAX_AUTO_VISION_BYTES {\n logging::warn(&format!(\n \"skipping generated image payload path={path} is_file={} bytes={} limit={}\",\n metadata.is_file(),\n metadata.len(),\n GENERATED_IMAGE_MAX_AUTO_VISION_BYTES\n ));\n return None;\n }\n\n let data = match std::fs::read(path_ref) {\n Ok(data) => data,\n Err(err) => {\n logging::error(&format!(\n \"failed to read generated image path={path}: {err}\"\n ));\n return None;\n }\n };\n let media_type = generated_image_media_type(path_ref, output_format).to_string();\n let data_b64 = base64::engine::general_purpose::STANDARD.encode(data);\n Some((media_type, data_b64))\n}\n\nfn generated_image_media_type(path: &Path, output_format: &str) -> &'static str {\n logging::debug(&format!(\n \"resolving generated image media type path={} format={output_format}\",\n path.display()\n ));\n let ext = path\n .extension()\n .and_then(|value| value.to_str())\n .unwrap_or(output_format)\n .to_ascii_lowercase();\n match ext.as_str() {\n \"jpg\" | \"jpeg\" => \"image/jpeg\",\n \"webp\" => \"image/webp\",\n \"gif\" => \"image/gif\",\n \"bmp\" => \"image/bmp\",\n \"ico\" => \"image/x-icon\",\n _ => \"image/png\",\n }\n}\n\n#[cfg(test)]\nmod tests;\n"}}, "src/cli/args/tests.rs": {"25a5cbb7266f31535ca7544b3ec13336abbf57f3": {"t": 1785745869.456552, "n": 22390, "h": "7f978aed1f97bd18", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn server_start_and_internal_keepalive_parse() {\n let args = Args::try_parse_from([\"jcode\", \"server\", \"start\", \"--json\"])\n .expect(\"server start should parse\");\n assert!(matches!(\n args.command,\n Some(Command::Server {\n action: ServerCommand::Start { json: true }\n })\n ));\n\n let keepalive = Args::try_parse_from([\"jcode\", \"server\", \"keepalive\"])\n .expect(\"internal server keepalive should parse\");\n assert!(matches!(\n keepalive.command,\n Some(Command::Server {\n action: ServerCommand::Keepalive\n })\n ));\n}\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn serve_server_name_option_parses() {\n let args =\n Args::try_parse_from([\"jcode\", \"serve\", \"--server-name\", \"mount-cloud/fabian\"]).unwrap();\n match args.command {\n Some(Command::Serve { server_name, .. }) => {\n assert_eq!(server_name.as_deref(), Some(\"mount-cloud/fabian\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn remote_working_dir_option_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--socket\",\n \"/tmp/jcode.sock\",\n \"--remote-working-dir\",\n \"/home/agent/project\",\n ])\n .unwrap();\n\n assert_eq!(\n args.remote_working_dir.as_deref(),\n Some(\"/home/agent/project\")\n );\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn account_subcommands_parse() {\n let login =\n Args::try_parse_from([\"jcode\", \"account\", \"login\", \"--no-browser\"]).expect(\"account login\");\n assert!(matches!(\n login.command,\n Some(Command::Account {\n action: AccountCommand::Login { no_browser: true }\n })\n ));\n\n let status =\n Args::try_parse_from([\"jcode\", \"account\", \"status\", \"--json\"]).expect(\"account status\");\n assert!(matches!(\n status.command,\n Some(Command::Account {\n action: AccountCommand::Status { json: true }\n })\n ));\n\n let manage = Args::try_parse_from([\"jcode\", \"account\", \"manage\"]).expect(\"account manage\");\n assert!(matches!(\n manage.command,\n Some(Command::Account {\n action: AccountCommand::Manage\n })\n ));\n\n let logout = Args::try_parse_from([\"jcode\", \"account\", \"logout\"]).expect(\"account logout\");\n assert!(matches!(\n logout.command,\n Some(Command::Account {\n action: AccountCommand::Logout\n })\n ));\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n/// Contract test for the onboarding agent-repair brief (see\n/// `jcode-tui::tui::app::onboarding_repair::build_repair_brief`). The brief\n/// tells a coding agent to run these exact commands to diagnose and fix a\n/// failed login. If any flag here stops parsing, the brief would hand the agent\n/// a broken command, so this guards the agent-facing CLI contract.\n#[test]\nfn onboarding_repair_brief_commands_are_valid_cli() {\n // Diagnose.\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--provider\", \"openai\", \"--json\"])\n .expect(\"auth-test --provider --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--all-configured\", \"--json\"])\n .expect(\"auth-test --all-configured --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth\", \"doctor\"]).expect(\"auth doctor must parse\");\n\n // Fix: OAuth and API-key logins.\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\"])\n .expect(\"login --provider must parse\");\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\", \"--api-key\", \"k\"])\n .expect(\"login --provider --api-key must parse\");\n\n // Fix: custom OpenAI-compatible endpoint via provider add + key on stdin.\n Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-endpoint\",\n \"--base-url\",\n \"https://api.example.com/v1\",\n \"--model\",\n \"some-model\",\n \"--api-key-stdin\",\n ])\n .expect(\"provider add --base-url --model --api-key-stdin must parse\");\n}\n"}, "d72ff9f910ac927b95a5f45f9feaf1817090c772": {"t": 1785756982.8270571, "n": 24029, "h": "25559e4be778ca05", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn server_start_and_internal_keepalive_parse() {\n let args = Args::try_parse_from([\"jcode\", \"server\", \"start\", \"--json\"])\n .expect(\"server start should parse\");\n assert!(matches!(\n args.command,\n Some(Command::Server {\n action: ServerCommand::Start { json: true }\n })\n ));\n\n let keepalive = Args::try_parse_from([\"jcode\", \"server\", \"keepalive\"])\n .expect(\"internal server keepalive should parse\");\n assert!(matches!(\n keepalive.command,\n Some(Command::Server {\n action: ServerCommand::Keepalive\n })\n ));\n}\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn serve_server_name_option_parses() {\n let args =\n Args::try_parse_from([\"jcode\", \"serve\", \"--server-name\", \"mount-cloud/fabian\"]).unwrap();\n match args.command {\n Some(Command::Serve { server_name, .. }) => {\n assert_eq!(server_name.as_deref(), Some(\"mount-cloud/fabian\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn remote_working_dir_option_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--socket\",\n \"/tmp/jcode.sock\",\n \"--remote-working-dir\",\n \"/home/agent/project\",\n ])\n .unwrap();\n\n assert_eq!(\n args.remote_working_dir.as_deref(),\n Some(\"/home/agent/project\")\n );\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn account_subcommands_parse() {\n let login =\n Args::try_parse_from([\"jcode\", \"account\", \"login\", \"--no-browser\"]).expect(\"account login\");\n assert!(matches!(\n login.command,\n Some(Command::Account {\n action: AccountCommand::Login { no_browser: true }\n })\n ));\n\n let status =\n Args::try_parse_from([\"jcode\", \"account\", \"status\", \"--json\"]).expect(\"account status\");\n assert!(matches!(\n status.command,\n Some(Command::Account {\n action: AccountCommand::Status { json: true }\n })\n ));\n\n let manage = Args::try_parse_from([\"jcode\", \"account\", \"manage\"]).expect(\"account manage\");\n assert!(matches!(\n manage.command,\n Some(Command::Account {\n action: AccountCommand::Manage\n })\n ));\n\n let logout = Args::try_parse_from([\"jcode\", \"account\", \"logout\"]).expect(\"account logout\");\n assert!(matches!(\n logout.command,\n Some(Command::Account {\n action: AccountCommand::Logout\n })\n ));\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n/// Contract test for the onboarding agent-repair brief (see\n/// `jcode-tui::tui::app::onboarding_repair::build_repair_brief`). The brief\n/// tells a coding agent to run these exact commands to diagnose and fix a\n/// failed login. If any flag here stops parsing, the brief would hand the agent\n/// a broken command, so this guards the agent-facing CLI contract.\n#[test]\nfn onboarding_repair_brief_commands_are_valid_cli() {\n // Diagnose.\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--provider\", \"openai\", \"--json\"])\n .expect(\"auth-test --provider --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--all-configured\", \"--json\"])\n .expect(\"auth-test --all-configured --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth\", \"doctor\"]).expect(\"auth doctor must parse\");\n\n // Fix: OAuth and API-key logins.\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\"])\n .expect(\"login --provider must parse\");\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\", \"--api-key\", \"k\"])\n .expect(\"login --provider --api-key must parse\");\n\n // Fix: custom OpenAI-compatible endpoint via provider add + key on stdin.\n Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-endpoint\",\n \"--base-url\",\n \"https://api.example.com/v1\",\n \"--model\",\n \"some-model\",\n \"--api-key-stdin\",\n ])\n .expect(\"provider add --base-url --model --api-key-stdin must parse\");\n}\n\n/// The bridge has two sockets and the global `--socket` already owns one of\n/// them. When the subcommand flag was also `--socket`, clap bound the global\n/// and the bridge listened on, and dialled, the same path: it came up looking\n/// healthy and every SDK client got ECONNREFUSED. Keep the names distinct.\n#[cfg(unix)]\n#[test]\nfn api_bridge_socket_flags_do_not_collide() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--socket\",\n \"/tmp/daemon.sock\",\n \"api-bridge\",\n \"--api-socket\",\n \"/tmp/api.sock\",\n ])\n .expect(\"api-bridge should accept both socket flags\");\n assert_eq!(args.socket.as_deref(), Some(\"/tmp/daemon.sock\"));\n assert!(matches!(\n args.command,\n Some(Command::ApiBridge { api_socket: Some(ref path) }) if path == \"/tmp/api.sock\"\n ));\n\n // The bare form must resolve both paths from the environment.\n let bare = Args::try_parse_from([\"jcode\", \"api-bridge\"]).expect(\"bare api-bridge should parse\");\n assert!(matches!(\n bare.command,\n Some(Command::ApiBridge { api_socket: None })\n ));\n\n // `--socket` after the subcommand must not be silently accepted as the\n // API socket, which is the exact confusion this test exists to prevent.\n let ambiguous = Args::try_parse_from([\"jcode\", \"api-bridge\", \"--socket\", \"/tmp/x.sock\"]).ok();\n assert!(\n matches!(\n ambiguous.map(|args| (args.socket, args.command)),\n None | Some((Some(_), Some(Command::ApiBridge { api_socket: None })))\n ),\n \"`--socket` after api-bridge must bind the daemon socket, never the API socket\"\n );\n}\n"}, "31344d70fcc76af141f74118fbddb8338afead83": {"t": 1785756984.5134847, "n": 22390, "h": "7f978aed1f97bd18", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn server_start_and_internal_keepalive_parse() {\n let args = Args::try_parse_from([\"jcode\", \"server\", \"start\", \"--json\"])\n .expect(\"server start should parse\");\n assert!(matches!(\n args.command,\n Some(Command::Server {\n action: ServerCommand::Start { json: true }\n })\n ));\n\n let keepalive = Args::try_parse_from([\"jcode\", \"server\", \"keepalive\"])\n .expect(\"internal server keepalive should parse\");\n assert!(matches!(\n keepalive.command,\n Some(Command::Server {\n action: ServerCommand::Keepalive\n })\n ));\n}\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn serve_server_name_option_parses() {\n let args =\n Args::try_parse_from([\"jcode\", \"serve\", \"--server-name\", \"mount-cloud/fabian\"]).unwrap();\n match args.command {\n Some(Command::Serve { server_name, .. }) => {\n assert_eq!(server_name.as_deref(), Some(\"mount-cloud/fabian\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn remote_working_dir_option_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--socket\",\n \"/tmp/jcode.sock\",\n \"--remote-working-dir\",\n \"/home/agent/project\",\n ])\n .unwrap();\n\n assert_eq!(\n args.remote_working_dir.as_deref(),\n Some(\"/home/agent/project\")\n );\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn account_subcommands_parse() {\n let login =\n Args::try_parse_from([\"jcode\", \"account\", \"login\", \"--no-browser\"]).expect(\"account login\");\n assert!(matches!(\n login.command,\n Some(Command::Account {\n action: AccountCommand::Login { no_browser: true }\n })\n ));\n\n let status =\n Args::try_parse_from([\"jcode\", \"account\", \"status\", \"--json\"]).expect(\"account status\");\n assert!(matches!(\n status.command,\n Some(Command::Account {\n action: AccountCommand::Status { json: true }\n })\n ));\n\n let manage = Args::try_parse_from([\"jcode\", \"account\", \"manage\"]).expect(\"account manage\");\n assert!(matches!(\n manage.command,\n Some(Command::Account {\n action: AccountCommand::Manage\n })\n ));\n\n let logout = Args::try_parse_from([\"jcode\", \"account\", \"logout\"]).expect(\"account logout\");\n assert!(matches!(\n logout.command,\n Some(Command::Account {\n action: AccountCommand::Logout\n })\n ));\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n/// Contract test for the onboarding agent-repair brief (see\n/// `jcode-tui::tui::app::onboarding_repair::build_repair_brief`). The brief\n/// tells a coding agent to run these exact commands to diagnose and fix a\n/// failed login. If any flag here stops parsing, the brief would hand the agent\n/// a broken command, so this guards the agent-facing CLI contract.\n#[test]\nfn onboarding_repair_brief_commands_are_valid_cli() {\n // Diagnose.\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--provider\", \"openai\", \"--json\"])\n .expect(\"auth-test --provider --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--all-configured\", \"--json\"])\n .expect(\"auth-test --all-configured --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth\", \"doctor\"]).expect(\"auth doctor must parse\");\n\n // Fix: OAuth and API-key logins.\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\"])\n .expect(\"login --provider must parse\");\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\", \"--api-key\", \"k\"])\n .expect(\"login --provider --api-key must parse\");\n\n // Fix: custom OpenAI-compatible endpoint via provider add + key on stdin.\n Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-endpoint\",\n \"--base-url\",\n \"https://api.example.com/v1\",\n \"--model\",\n \"some-model\",\n \"--api-key-stdin\",\n ])\n .expect(\"provider add --base-url --model --api-key-stdin must parse\");\n}\n"}, "606198bf626f3804cf475ce5e77053d9ea431eaf": {"t": 1785756986.104158, "n": 21779, "h": "cdef906e5cc67f03", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn serve_server_name_option_parses() {\n let args =\n Args::try_parse_from([\"jcode\", \"serve\", \"--server-name\", \"mount-cloud/fabian\"]).unwrap();\n match args.command {\n Some(Command::Serve { server_name, .. }) => {\n assert_eq!(server_name.as_deref(), Some(\"mount-cloud/fabian\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn remote_working_dir_option_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--socket\",\n \"/tmp/jcode.sock\",\n \"--remote-working-dir\",\n \"/home/agent/project\",\n ])\n .unwrap();\n\n assert_eq!(\n args.remote_working_dir.as_deref(),\n Some(\"/home/agent/project\")\n );\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn account_subcommands_parse() {\n let login =\n Args::try_parse_from([\"jcode\", \"account\", \"login\", \"--no-browser\"]).expect(\"account login\");\n assert!(matches!(\n login.command,\n Some(Command::Account {\n action: AccountCommand::Login { no_browser: true }\n })\n ));\n\n let status =\n Args::try_parse_from([\"jcode\", \"account\", \"status\", \"--json\"]).expect(\"account status\");\n assert!(matches!(\n status.command,\n Some(Command::Account {\n action: AccountCommand::Status { json: true }\n })\n ));\n\n let manage = Args::try_parse_from([\"jcode\", \"account\", \"manage\"]).expect(\"account manage\");\n assert!(matches!(\n manage.command,\n Some(Command::Account {\n action: AccountCommand::Manage\n })\n ));\n\n let logout = Args::try_parse_from([\"jcode\", \"account\", \"logout\"]).expect(\"account logout\");\n assert!(matches!(\n logout.command,\n Some(Command::Account {\n action: AccountCommand::Logout\n })\n ));\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n/// Contract test for the onboarding agent-repair brief (see\n/// `jcode-tui::tui::app::onboarding_repair::build_repair_brief`). The brief\n/// tells a coding agent to run these exact commands to diagnose and fix a\n/// failed login. If any flag here stops parsing, the brief would hand the agent\n/// a broken command, so this guards the agent-facing CLI contract.\n#[test]\nfn onboarding_repair_brief_commands_are_valid_cli() {\n // Diagnose.\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--provider\", \"openai\", \"--json\"])\n .expect(\"auth-test --provider --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--all-configured\", \"--json\"])\n .expect(\"auth-test --all-configured --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth\", \"doctor\"]).expect(\"auth doctor must parse\");\n\n // Fix: OAuth and API-key logins.\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\"])\n .expect(\"login --provider must parse\");\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\", \"--api-key\", \"k\"])\n .expect(\"login --provider --api-key must parse\");\n\n // Fix: custom OpenAI-compatible endpoint via provider add + key on stdin.\n Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-endpoint\",\n \"--base-url\",\n \"https://api.example.com/v1\",\n \"--model\",\n \"some-model\",\n \"--api-key-stdin\",\n ])\n .expect(\"provider add --base-url --model --api-key-stdin must parse\");\n}\n"}, "1e06025949693b928898362389ac48678597e546": {"t": 1785756987.3165774, "n": 20715, "h": "c3c38db090dcc771", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn serve_server_name_option_parses() {\n let args =\n Args::try_parse_from([\"jcode\", \"serve\", \"--server-name\", \"mount-cloud/fabian\"]).unwrap();\n match args.command {\n Some(Command::Serve { server_name, .. }) => {\n assert_eq!(server_name.as_deref(), Some(\"mount-cloud/fabian\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn remote_working_dir_option_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--socket\",\n \"/tmp/jcode.sock\",\n \"--remote-working-dir\",\n \"/home/agent/project\",\n ])\n .unwrap();\n\n assert_eq!(\n args.remote_working_dir.as_deref(),\n Some(\"/home/agent/project\")\n );\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n/// Contract test for the onboarding agent-repair brief (see\n/// `jcode-tui::tui::app::onboarding_repair::build_repair_brief`). The brief\n/// tells a coding agent to run these exact commands to diagnose and fix a\n/// failed login. If any flag here stops parsing, the brief would hand the agent\n/// a broken command, so this guards the agent-facing CLI contract.\n#[test]\nfn onboarding_repair_brief_commands_are_valid_cli() {\n // Diagnose.\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--provider\", \"openai\", \"--json\"])\n .expect(\"auth-test --provider --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--all-configured\", \"--json\"])\n .expect(\"auth-test --all-configured --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth\", \"doctor\"]).expect(\"auth doctor must parse\");\n\n // Fix: OAuth and API-key logins.\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\"])\n .expect(\"login --provider must parse\");\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\", \"--api-key\", \"k\"])\n .expect(\"login --provider --api-key must parse\");\n\n // Fix: custom OpenAI-compatible endpoint via provider add + key on stdin.\n Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-endpoint\",\n \"--base-url\",\n \"https://api.example.com/v1\",\n \"--model\",\n \"some-model\",\n \"--api-key-stdin\",\n ])\n .expect(\"provider add --base-url --model --api-key-stdin must parse\");\n}\n"}, "9f6be03d3e6e35a9b5aec26b5d0b6820db235baa": {"t": 1785756988.9913578, "n": 20373, "h": "40d82f56d1f3d522", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn serve_server_name_option_parses() {\n let args =\n Args::try_parse_from([\"jcode\", \"serve\", \"--server-name\", \"mount-cloud/fabian\"]).unwrap();\n match args.command {\n Some(Command::Serve { server_name, .. }) => {\n assert_eq!(server_name.as_deref(), Some(\"mount-cloud/fabian\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n/// Contract test for the onboarding agent-repair brief (see\n/// `jcode-tui::tui::app::onboarding_repair::build_repair_brief`). The brief\n/// tells a coding agent to run these exact commands to diagnose and fix a\n/// failed login. If any flag here stops parsing, the brief would hand the agent\n/// a broken command, so this guards the agent-facing CLI contract.\n#[test]\nfn onboarding_repair_brief_commands_are_valid_cli() {\n // Diagnose.\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--provider\", \"openai\", \"--json\"])\n .expect(\"auth-test --provider --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--all-configured\", \"--json\"])\n .expect(\"auth-test --all-configured --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth\", \"doctor\"]).expect(\"auth doctor must parse\");\n\n // Fix: OAuth and API-key logins.\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\"])\n .expect(\"login --provider must parse\");\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\", \"--api-key\", \"k\"])\n .expect(\"login --provider --api-key must parse\");\n\n // Fix: custom OpenAI-compatible endpoint via provider add + key on stdin.\n Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-endpoint\",\n \"--base-url\",\n \"https://api.example.com/v1\",\n \"--model\",\n \"some-model\",\n \"--api-key-stdin\",\n ])\n .expect(\"provider add --base-url --model --api-key-stdin must parse\");\n}\n"}, "c29c5c8175952c489286635fc24172963021e80c": {"t": 1785756990.3233495, "n": 19979, "h": "423880e8bb93d3d8", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n/// Contract test for the onboarding agent-repair brief (see\n/// `jcode-tui::tui::app::onboarding_repair::build_repair_brief`). The brief\n/// tells a coding agent to run these exact commands to diagnose and fix a\n/// failed login. If any flag here stops parsing, the brief would hand the agent\n/// a broken command, so this guards the agent-facing CLI contract.\n#[test]\nfn onboarding_repair_brief_commands_are_valid_cli() {\n // Diagnose.\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--provider\", \"openai\", \"--json\"])\n .expect(\"auth-test --provider --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth-test\", \"--all-configured\", \"--json\"])\n .expect(\"auth-test --all-configured --json must parse\");\n Args::try_parse_from([\"jcode\", \"auth\", \"doctor\"]).expect(\"auth doctor must parse\");\n\n // Fix: OAuth and API-key logins.\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\"])\n .expect(\"login --provider must parse\");\n Args::try_parse_from([\"jcode\", \"login\", \"--provider\", \"openai\", \"--api-key\", \"k\"])\n .expect(\"login --provider --api-key must parse\");\n\n // Fix: custom OpenAI-compatible endpoint via provider add + key on stdin.\n Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-endpoint\",\n \"--base-url\",\n \"https://api.example.com/v1\",\n \"--model\",\n \"some-model\",\n \"--api-key-stdin\",\n ])\n .expect(\"provider add --base-url --model --api-key-stdin must parse\");\n}\n"}, "f7290fdfd7d1259c64e8291dd1b243cbfce87624": {"t": 1785756991.9891155, "n": 18468, "h": "589cda655990f721", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--with-view\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n with_view,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert!(with_view);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "5e39d13e32b6ab3da011f757ae6526c8c3b5bffa": {"t": 1785756993.4209301, "n": 18382, "h": "2170a7406ec5322d", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"dashboard\",\n \"--limit\",\n \"10\",\n \"--open\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Dashboard {\n limit,\n output,\n open,\n jade,\n },\n })) => {\n assert_eq!(limit, 10);\n assert!(output.is_none());\n assert!(open);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "07911265e05b27b26ea91faab715a1ea006e8852": {"t": 1785756994.9391685, "n": 17639, "h": "66b4196a95df8333", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n min_interval_mins,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(min_interval_mins.is_none());\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "a8809c978bf1ef0c3aed635df9b27da3701f4606": {"t": 1785756996.4004533, "n": 17550, "h": "a5b9a81fe8dc1a3b", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"sync\",\n \"--all\",\n \"--max\",\n \"5\",\n \"--dry-run\",\n \"--json\",\n \"--user-id\",\n \"jeremy\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Sync {\n sessions_dir,\n since_days,\n all,\n max,\n raw,\n dry_run,\n force,\n json,\n jade,\n },\n })) => {\n assert!(sessions_dir.is_none());\n assert!(since_days.is_none());\n assert!(all);\n assert_eq!(max, 5);\n assert!(!raw);\n assert!(dry_run);\n assert!(!force);\n assert!(json);\n assert_eq!(jade.user_id, \"jeremy\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "0de72d789774121ed758765b2a95bcba50285f08": {"t": 1785756998.3356605, "n": 16482, "h": "a6bbfa0796ac0aa3", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"configure\",\n \"--api-base\",\n \"https://jade.example\",\n \"--api-token-env\",\n \"JADE_TOKEN\",\n \"--api-token-id\",\n \"dev-admin\",\n \"--user-id\",\n \"jeremy\",\n \"--helper\",\n \"/tmp/jade_sessions.py\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::Configure {\n api_base,\n api_token_env,\n api_token_id,\n user_id,\n helper,\n clear,\n ..\n },\n })) => {\n assert_eq!(api_base.as_deref(), Some(\"https://jade.example\"));\n assert_eq!(api_token_env.as_deref(), Some(\"JADE_TOKEN\"));\n assert_eq!(api_token_id.as_deref(), Some(\"dev-admin\"));\n assert_eq!(user_id.as_deref(), Some(\"jeremy\"));\n assert_eq!(helper.as_deref(), Some(\"/tmp/jade_sessions.py\"));\n assert!(!clear);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"cloud\", \"sessions\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action: CloudSessionsCommand::Status { json },\n })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "ba80a32c659bd588b1aac81400bf45e0ad996924": {"t": 1785756999.8757908, "n": 14929, "h": "aacb44cd2f7091fe", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn cloud_sessions_subcommands_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"upload-latest\",\n \"--sessions-dir\",\n \"/tmp/sessions\",\n \"--user-id\",\n \"jeremy\",\n \"--profile\",\n \"test-profile\",\n \"--region\",\n \"us-east-1\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::UploadLatest {\n sessions_dir,\n raw,\n jade,\n },\n })) => {\n assert_eq!(sessions_dir, \"/tmp/sessions\");\n assert!(!raw);\n assert_eq!(jade.user_id, \"jeremy\");\n assert_eq!(jade.profile.as_deref(), Some(\"test-profile\"));\n assert_eq!(jade.region.as_deref(), Some(\"us-east-1\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"cloud\",\n \"sessions\",\n \"view\",\n \"session_123\",\n \"--format\",\n \"html\",\n \"--open\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Cloud(CloudCommand::Sessions {\n action:\n CloudSessionsCommand::View {\n session_id,\n format,\n open,\n ..\n },\n })) => {\n assert_eq!(session_id, \"session_123\");\n assert!(matches!(format, CloudSessionViewFormat::Html));\n assert!(open);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "f0266100afb5fd822c69cb2f5875d4e03bde8dec": {"t": 1785757002.3243623, "n": 13226, "h": "40c9f67299367200", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn acp_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"acp\"]).unwrap();\n match args.command {\n Some(Command::Acp) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "b64920f62796aff59c5629a8b3d102797cd13612": {"t": 1785757003.7787936, "n": 12998, "h": "5c5aac7330acd467", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n provider,\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(provider.is_none());\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_accepts_provider_positional() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"google\"]).unwrap();\n match args.command {\n Some(Command::Login { provider, .. }) => {\n assert_eq!(provider, Some(ProviderChoice::Google));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "6327b1f24c044a49acb074e893dbfb1fccf5a62a": {"t": 1785757005.2822638, "n": 12591, "h": "2964b52300998fe9", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n no_validate,\n }) => {\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n assert!(!no_validate);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "e48894603c6459e0d52df43c69861463b1941e4b": {"t": 1785757006.7633567, "n": 12531, "h": "515483e567b7668b", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn session_rename_subcommand_parses() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"session\",\n \"rename\",\n \"fox\",\n \"release planning\",\n \"--json\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert_eq!(name.as_deref(), Some(\"release planning\"));\n assert!(!clear);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"session\", \"rename\", \"fox\", \"--clear\"]).unwrap();\n match args.command {\n Some(Command::Session(SessionCommand::Rename {\n session,\n name,\n clear,\n json,\n })) => {\n assert_eq!(session, \"fox\");\n assert!(name.is_none());\n assert!(clear);\n assert!(!json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n }) => {\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "f7dd546e85c39e96499852a5bdeda35ae4497946": {"t": 1785757008.020672, "n": 11414, "h": "390224dc69fd0157", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"cgc\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Comtegra);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n }) => {\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "da6d314a46f4d447ff91d66cd338243b5672e0a7": {"t": 1785757009.4709768, "n": 11262, "h": "f224486768483412", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n }) => {\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_add_subcommand_parses_agent_friendly_flags() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"provider\",\n \"add\",\n \"my-api\",\n \"--base-url\",\n \"https://llm.example.com/v1\",\n \"--model\",\n \"model-a\",\n \"--context-window\",\n \"128000\",\n \"--api-key-stdin\",\n \"--auth\",\n \"bearer\",\n \"--set-default\",\n \"--json\",\n ])\n .unwrap();\n\n match args.command {\n Some(Command::Provider(ProviderCommand::Add {\n name,\n base_url,\n model,\n context_window,\n api_key_stdin,\n auth,\n set_default,\n json,\n ..\n })) => {\n assert_eq!(name, \"my-api\");\n assert_eq!(base_url, \"https://llm.example.com/v1\");\n assert_eq!(model, \"model-a\");\n assert_eq!(context_window, Some(128000));\n assert!(api_key_stdin);\n assert_eq!(auth, Some(ProviderAuthArg::Bearer));\n assert!(set_default);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "af78b620d648efdda3a5e41da617ffd2d49f302b": {"t": 1785757010.7749896, "n": 10091, "h": "500e71ae819facb7", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n }) => {\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_accepts_global_provider_and_model_after_subcommand() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--provider\",\n \"openai-compatible\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--model\",\n \"deepseek-v4-flash\",\n ])\n .unwrap();\n\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login { api_base, .. }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "50619a00d59b6916ece8e135e345c5d15a654980": {"t": 1785757012.2432013, "n": 9384, "h": "6f9dd452730e7e09", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n api_base,\n api_key,\n api_key_env,\n }) => {\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n assert!(api_base.is_none());\n assert!(api_key.is_none());\n assert!(api_key_env.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_openai_compatible_scriptable_flags_parse() {\n let args = Args::try_parse_from([\n \"jcode\",\n \"--provider\",\n \"openai-compatible\",\n \"--model\",\n \"deepseek-v4-flash\",\n \"login\",\n \"--api-base\",\n \"https://api.deepseek.com\",\n \"--api-key-env\",\n \"DEEPSEEK_API_KEY\",\n ])\n .unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n assert_eq!(args.model.as_deref(), Some(\"deepseek-v4-flash\"));\n match args.command {\n Some(Command::Login {\n api_base,\n api_key_env,\n ..\n }) => {\n assert_eq!(api_base.as_deref(), Some(\"https://api.deepseek.com\"));\n assert_eq!(api_key_env.as_deref(), Some(\"DEEPSEEK_API_KEY\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}, "c4bc4a54a8478aa6c1a9ca93dbc107ccaa9ed86e": {"t": 1785757014.061964, "n": 8329, "h": "0c1d41b574bd773d", "c": "use super::*;\nuse crate::cli::provider_init::ProviderChoice;\n\n#[test]\nfn test_provider_choice_aliases_parse() {\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"z.ai\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Zai);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"kimi-for-coding\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Kimi);\n\n let args =\n Args::try_parse_from([\"jcode\", \"--provider\", \"cerebrascode\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Cerebras);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"compat\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::OpenaiCompatible);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"bailian\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::AlibabaCodingPlan);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"together\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::TogetherAi);\n\n let args = Args::try_parse_from([\"jcode\", \"--provider\", \"grok\", \"run\", \"smoke\"]).unwrap();\n assert_eq!(args.provider, ProviderChoice::Xai);\n}\n\n#[test]\nfn model_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"model\", \"list\", \"--json\", \"--verbose\"]).unwrap();\n match args.command {\n Some(Command::Model(ModelCommand::List { json, verbose })) => {\n assert!(json);\n assert!(verbose);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_no_browser_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--no-browser\"]).unwrap();\n match args.command {\n Some(Command::Login {\n account,\n no_browser,\n print_auth_url,\n callback_url,\n auth_code,\n json,\n complete,\n google_access_tier,\n }) => {\n assert!(account.is_none());\n assert!(no_browser);\n assert!(!print_auth_url);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!json);\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--headless\"]).unwrap();\n match args.command {\n Some(Command::Login { no_browser, .. }) => assert!(no_browser),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn login_scriptable_flags_parse() {\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--print-auth-url\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Login {\n print_auth_url,\n json,\n callback_url,\n auth_code,\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(print_auth_url);\n assert!(json);\n assert!(callback_url.is_none());\n assert!(auth_code.is_none());\n assert!(!complete);\n assert!(google_access_tier.is_none());\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--callback-url\",\n \"http://localhost:1455/auth/callback?code=x&state=y\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login { callback_url, .. }) => {\n assert_eq!(\n callback_url.as_deref(),\n Some(\"http://localhost:1455/auth/callback?code=x&state=y\")\n );\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\"jcode\", \"login\", \"--auth-code\", \"abc123\"]).unwrap();\n match args.command {\n Some(Command::Login { auth_code, .. }) => {\n assert_eq!(auth_code.as_deref(), Some(\"abc123\"));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n\n let args = Args::try_parse_from([\n \"jcode\",\n \"login\",\n \"--complete\",\n \"--google-access-tier\",\n \"readonly\",\n ])\n .unwrap();\n match args.command {\n Some(Command::Login {\n complete,\n google_access_tier,\n ..\n }) => {\n assert!(complete);\n assert_eq!(google_access_tier, Some(GoogleAccessTierArg::Readonly));\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn quiet_global_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"--quiet\", \"model\", \"list\"]).unwrap();\n assert!(args.quiet);\n}\n\n#[test]\nfn run_json_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--json\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(json);\n assert!(!ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn run_ndjson_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"run\", \"--ndjson\", \"hello\"]).unwrap();\n match args.command {\n Some(Command::Run {\n json,\n ndjson,\n message,\n }) => {\n assert!(!json);\n assert!(ndjson);\n assert_eq!(message, \"hello\");\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn version_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"version\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Version { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn usage_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"usage\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Usage { json }) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_status_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"status\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Status { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn auth_doctor_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"auth\", \"doctor\", \"openai\", \"--validate\", \"--json\"])\n .unwrap();\n match args.command {\n Some(Command::Auth(AuthCommand::Doctor {\n provider,\n validate,\n json,\n })) => {\n assert_eq!(provider.as_deref(), Some(\"openai\"));\n assert!(validate);\n assert!(json);\n }\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_list_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"list\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::List { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn provider_current_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"provider\", \"current\", \"--json\"]).unwrap();\n match args.command {\n Some(Command::Provider(ProviderCommand::Current { json })) => assert!(json),\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_subcommand_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save {\n auto_restore: false,\n },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n\n#[test]\nfn restart_save_auto_restore_flag_parses() {\n let args = Args::try_parse_from([\"jcode\", \"restart\", \"save\", \"--auto-restore\"]).unwrap();\n match args.command {\n Some(Command::Restart {\n action: RestartCommand::Save { auto_restore: true },\n }) => {}\n other => panic!(\"unexpected command: {:?}\", other),\n }\n}\n"}}, "crates/jcode-base/src/provider_catalog_tests.rs": {"25a5cbb7266f31535ca7544b3ec13336abbf57f3": {"t": 1785746259.556413, "n": 41576, "h": "ef223e055b9b30d7", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n\n#[test]\nfn quality_tier_ranks_flagship_above_bare_above_cheap() {\n // Flagship-marked ids (max/pro/opus/coder/large/huge-param) -> tier 2.\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-max\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"claude-opus-4-8\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-coder-480b\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-pro\"), 2);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-405b-instruct\"),\n 2\n );\n\n // Bare frontier ids (no tier marker) -> tier 1.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"kimi-k2.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6\"), 1);\n\n // Cheap/small/fast-marked ids -> tier 0.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5-mini\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"deepseek-v4-flash\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-air\"), 0);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-8b-instant\"),\n 0\n );\n assert_eq!(openai_compatible_model_quality_tier(\"claude-haiku-4-5\"), 0);\n\n // Brand names that merely *contain* a marker substring must NOT trip the\n // whole-token matcher: `minimax` is not `mini`/`max`.\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n\n // Flagship marker beats a co-occurring size token.\n assert_eq!(\n openai_compatible_model_quality_tier(\"qwen3-coder-30b-a3b\"),\n 2\n );\n}\n\n#[test]\nfn newest_release_picker_prefers_strongest_tier_over_newest_cheap() {\n use jcode_provider_openrouter::ModelInfo;\n let _lock = crate::storage::lock_test_env();\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // A heterogeneous proxy catalog (like OpenCode Zen): the NEWEST model is a\n // cheap `*-flash`, but a slightly older flagship-marked model exists. The\n // picker must choose the flagship, not the newest-cheap.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v4-flash\", 1_900_000_000), // newest, but cheap tier\n mk(\"deepseek-v4\", 1_850_000_000), // bare frontier\n mk(\"deepseek-v4-coder\", 1_800_000_000), // flagship tier, oldest\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4-coder\"),\n \"a flagship-marked model must win over a newer cheap/flash sibling\"\n );\n}\n\n#[test]\nfn newest_release_picker_uses_recency_within_a_tier() {\n use jcode_provider_openrouter::ModelInfo;\n let _lock = crate::storage::lock_test_env();\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // All same (bare frontier) tier: recency decides.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v3\", 1_700_000_000),\n mk(\"deepseek-v4\", 1_900_000_000), // newest within the same tier\n mk(\"deepseek-v3.1\", 1_800_000_000),\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4\"),\n \"within one quality tier the newest release should win\"\n );\n}\n\n/// Exhaustiveness guard: every model shipped in a profile's static catalog must\n/// resolve to a concrete context window. Open-weight gateways frequently omit\n/// `context_length` from `/v1/models`, so a missing entry here means that model\n/// would silently fall back to the generic 200K default. First-party\n/// OpenAI/Claude/Gemini ids are resolved by their own providers (not this static\n/// table) and are exempted.\n#[test]\nfn every_static_profile_model_has_a_known_context_limit() {\n use jcode_provider_core::models::context_limit_for_model_with_provider;\n\n // Ids handled by dedicated first-party providers rather than the\n // OpenAI-compatible static table.\n fn is_first_party(model: &str) -> bool {\n let m = model.to_ascii_lowercase();\n m.starts_with(\"claude-\")\n || m.starts_with(\"gpt-\")\n || m.starts_with(\"gemini-\")\n || m.starts_with(\"o3\")\n || m.starts_with(\"o4\")\n }\n\n let mut missing: Vec<(String, String)> = Vec::new();\n for profile in jcode_provider_metadata::openai_compatible_profiles()\n .iter()\n .copied()\n {\n for model in openai_compatible_profile_static_models(profile) {\n if is_first_party(&model) {\n continue;\n }\n\n let via_profile = openai_compatible_profile_context_limit(profile.id, &model);\n let via_global = context_limit_for_model_with_provider(&model, Some(\"openrouter\"));\n\n if via_profile.is_none() && via_global.is_none() {\n missing.push((profile.id.to_string(), model));\n }\n }\n }\n\n assert!(\n missing.is_empty(),\n \"static profile models without a known context limit (would fall back to the \\\n generic default); add them to open_weight_family_context_limit: {missing:?}\"\n );\n}\n\n#[test]\nfn open_weight_family_context_limits_match_published_windows() {\n use jcode_provider_core::models::open_weight_family_context_limit as f;\n\n // GLM family spelling variants across gateways.\n assert_eq!(f(\"glm-4.5\"), Some(128_000));\n assert_eq!(f(\"glm-4.7\"), Some(200_000));\n assert_eq!(f(\"zai-org/glm-4.7\"), Some(200_000));\n assert_eq!(f(\"accounts/fireworks/models/glm-4p7\"), Some(200_000));\n assert_eq!(f(\"glm-5\"), Some(200_000));\n assert_eq!(f(\"glm-5.1\"), Some(200_000));\n assert_eq!(f(\"zai-glm-5-1\"), Some(200_000));\n assert_eq!(f(\"glm-5.2\"), Some(1_000_000));\n\n // Other open-weight families.\n assert_eq!(f(\"kimi-k2.5\"), Some(262_144));\n assert_eq!(f(\"minimax-m2.7\"), Some(204_800));\n assert_eq!(f(\"mimo-v2.5\"), Some(262_144));\n assert_eq!(f(\"deepseek-v3.2\"), Some(163_840));\n assert_eq!(f(\"deepseek-v4-pro\"), Some(1_000_000));\n assert_eq!(f(\"qwen3-235b-a22b-instruct-2507\"), Some(262_144));\n assert_eq!(f(\"gpt-oss-120b\"), Some(131_072));\n assert_eq!(f(\"llama-3.3-70b-instruct\"), Some(131_072));\n assert_eq!(f(\"sonar-pro\"), Some(128_000));\n\n // Unknown families stay unresolved so the dynamic cache/default can act.\n assert_eq!(f(\"some-unknown-model\"), None);\n}\n\n#[test]\nfn minimax_default_provider_applies_openai_api_key_env_not_openrouter() {\n // Regression for #407: `default_provider = \"minimax\"` (the built-in MiniMax\n // profile) must resolve credentials from the profile's documented\n // OPENAI_API_KEY / minimax.env, not the generic OPENROUTER_API_KEY /\n // openrouter.env. The earlier bug surfaced as\n // \"OPENROUTER_API_KEY not found ...\" when applying the configured\n // default_model.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ]);\n for v in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ] {\n crate::env::remove_var(v);\n }\n\n let selection = resolve_openai_compatible_profile_selection(\"minimax\");\n assert_eq!(\n selection.map(|profile| profile.id),\n Some(\"minimax\"),\n \"default_provider=minimax must resolve the built-in MiniMax profile\"\n );\n\n apply_openai_compatible_profile_env(selection);\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"OPENAI_API_KEY\"),\n \"MiniMax profile must use OPENAI_API_KEY, not OPENROUTER_API_KEY\"\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").ok().as_deref(),\n Some(\"minimax.env\"),\n \"MiniMax profile must use minimax.env, not openrouter.env\"\n );\n}\n"}, "0701bc43763ad16f9edd472443f448ee8a946887": {"t": 1785756982.9725804, "n": 41576, "h": "ef223e055b9b30d7", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n\n#[test]\nfn quality_tier_ranks_flagship_above_bare_above_cheap() {\n // Flagship-marked ids (max/pro/opus/coder/large/huge-param) -> tier 2.\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-max\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"claude-opus-4-8\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-coder-480b\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-pro\"), 2);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-405b-instruct\"),\n 2\n );\n\n // Bare frontier ids (no tier marker) -> tier 1.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"kimi-k2.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6\"), 1);\n\n // Cheap/small/fast-marked ids -> tier 0.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5-mini\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"deepseek-v4-flash\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-air\"), 0);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-8b-instant\"),\n 0\n );\n assert_eq!(openai_compatible_model_quality_tier(\"claude-haiku-4-5\"), 0);\n\n // Brand names that merely *contain* a marker substring must NOT trip the\n // whole-token matcher: `minimax` is not `mini`/`max`.\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n\n // Flagship marker beats a co-occurring size token.\n assert_eq!(\n openai_compatible_model_quality_tier(\"qwen3-coder-30b-a3b\"),\n 2\n );\n}\n\n#[test]\nfn newest_release_picker_prefers_strongest_tier_over_newest_cheap() {\n use jcode_provider_openrouter::ModelInfo;\n let _lock = crate::storage::lock_test_env();\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // A heterogeneous proxy catalog (like OpenCode Zen): the NEWEST model is a\n // cheap `*-flash`, but a slightly older flagship-marked model exists. The\n // picker must choose the flagship, not the newest-cheap.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v4-flash\", 1_900_000_000), // newest, but cheap tier\n mk(\"deepseek-v4\", 1_850_000_000), // bare frontier\n mk(\"deepseek-v4-coder\", 1_800_000_000), // flagship tier, oldest\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4-coder\"),\n \"a flagship-marked model must win over a newer cheap/flash sibling\"\n );\n}\n\n#[test]\nfn newest_release_picker_uses_recency_within_a_tier() {\n use jcode_provider_openrouter::ModelInfo;\n let _lock = crate::storage::lock_test_env();\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // All same (bare frontier) tier: recency decides.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v3\", 1_700_000_000),\n mk(\"deepseek-v4\", 1_900_000_000), // newest within the same tier\n mk(\"deepseek-v3.1\", 1_800_000_000),\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4\"),\n \"within one quality tier the newest release should win\"\n );\n}\n\n/// Exhaustiveness guard: every model shipped in a profile's static catalog must\n/// resolve to a concrete context window. Open-weight gateways frequently omit\n/// `context_length` from `/v1/models`, so a missing entry here means that model\n/// would silently fall back to the generic 200K default. First-party\n/// OpenAI/Claude/Gemini ids are resolved by their own providers (not this static\n/// table) and are exempted.\n#[test]\nfn every_static_profile_model_has_a_known_context_limit() {\n use jcode_provider_core::models::context_limit_for_model_with_provider;\n\n // Ids handled by dedicated first-party providers rather than the\n // OpenAI-compatible static table.\n fn is_first_party(model: &str) -> bool {\n let m = model.to_ascii_lowercase();\n m.starts_with(\"claude-\")\n || m.starts_with(\"gpt-\")\n || m.starts_with(\"gemini-\")\n || m.starts_with(\"o3\")\n || m.starts_with(\"o4\")\n }\n\n let mut missing: Vec<(String, String)> = Vec::new();\n for profile in jcode_provider_metadata::openai_compatible_profiles()\n .iter()\n .copied()\n {\n for model in openai_compatible_profile_static_models(profile) {\n if is_first_party(&model) {\n continue;\n }\n\n let via_profile = openai_compatible_profile_context_limit(profile.id, &model);\n let via_global = context_limit_for_model_with_provider(&model, Some(\"openrouter\"));\n\n if via_profile.is_none() && via_global.is_none() {\n missing.push((profile.id.to_string(), model));\n }\n }\n }\n\n assert!(\n missing.is_empty(),\n \"static profile models without a known context limit (would fall back to the \\\n generic default); add them to open_weight_family_context_limit: {missing:?}\"\n );\n}\n\n#[test]\nfn open_weight_family_context_limits_match_published_windows() {\n use jcode_provider_core::models::open_weight_family_context_limit as f;\n\n // GLM family spelling variants across gateways.\n assert_eq!(f(\"glm-4.5\"), Some(128_000));\n assert_eq!(f(\"glm-4.7\"), Some(200_000));\n assert_eq!(f(\"zai-org/glm-4.7\"), Some(200_000));\n assert_eq!(f(\"accounts/fireworks/models/glm-4p7\"), Some(200_000));\n assert_eq!(f(\"glm-5\"), Some(200_000));\n assert_eq!(f(\"glm-5.1\"), Some(200_000));\n assert_eq!(f(\"zai-glm-5-1\"), Some(200_000));\n assert_eq!(f(\"glm-5.2\"), Some(1_000_000));\n\n // Other open-weight families.\n assert_eq!(f(\"kimi-k2.5\"), Some(262_144));\n assert_eq!(f(\"minimax-m2.7\"), Some(204_800));\n assert_eq!(f(\"mimo-v2.5\"), Some(262_144));\n assert_eq!(f(\"deepseek-v3.2\"), Some(163_840));\n assert_eq!(f(\"deepseek-v4-pro\"), Some(1_000_000));\n assert_eq!(f(\"qwen3-235b-a22b-instruct-2507\"), Some(262_144));\n assert_eq!(f(\"gpt-oss-120b\"), Some(131_072));\n assert_eq!(f(\"llama-3.3-70b-instruct\"), Some(131_072));\n assert_eq!(f(\"sonar-pro\"), Some(128_000));\n\n // Unknown families stay unresolved so the dynamic cache/default can act.\n assert_eq!(f(\"some-unknown-model\"), None);\n}\n\n#[test]\nfn minimax_default_provider_applies_openai_api_key_env_not_openrouter() {\n // Regression for #407: `default_provider = \"minimax\"` (the built-in MiniMax\n // profile) must resolve credentials from the profile's documented\n // OPENAI_API_KEY / minimax.env, not the generic OPENROUTER_API_KEY /\n // openrouter.env. The earlier bug surfaced as\n // \"OPENROUTER_API_KEY not found ...\" when applying the configured\n // default_model.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ]);\n for v in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ] {\n crate::env::remove_var(v);\n }\n\n let selection = resolve_openai_compatible_profile_selection(\"minimax\");\n assert_eq!(\n selection.map(|profile| profile.id),\n Some(\"minimax\"),\n \"default_provider=minimax must resolve the built-in MiniMax profile\"\n );\n\n apply_openai_compatible_profile_env(selection);\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"OPENAI_API_KEY\"),\n \"MiniMax profile must use OPENAI_API_KEY, not OPENROUTER_API_KEY\"\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").ok().as_deref(),\n Some(\"minimax.env\"),\n \"MiniMax profile must use minimax.env, not openrouter.env\"\n );\n}\n"}, "5737ab3ddf218a16024bd10dc3e017da22689d11": {"t": 1785756984.5321238, "n": 41478, "h": "65201892a8a79261", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n\n#[test]\nfn quality_tier_ranks_flagship_above_bare_above_cheap() {\n // Flagship-marked ids (max/pro/opus/coder/large/huge-param) -> tier 2.\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-max\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"claude-opus-4-8\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-coder-480b\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-pro\"), 2);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-405b-instruct\"),\n 2\n );\n\n // Bare frontier ids (no tier marker) -> tier 1.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"kimi-k2.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6\"), 1);\n\n // Cheap/small/fast-marked ids -> tier 0.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5-mini\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"deepseek-v4-flash\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-air\"), 0);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-8b-instant\"),\n 0\n );\n assert_eq!(openai_compatible_model_quality_tier(\"claude-haiku-4-5\"), 0);\n\n // Brand names that merely *contain* a marker substring must NOT trip the\n // whole-token matcher: `minimax` is not `mini`/`max`.\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n\n // Flagship marker beats a co-occurring size token.\n assert_eq!(\n openai_compatible_model_quality_tier(\"qwen3-coder-30b-a3b\"),\n 2\n );\n}\n\n#[test]\nfn newest_release_picker_prefers_strongest_tier_over_newest_cheap() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // A heterogeneous proxy catalog (like OpenCode Zen): the NEWEST model is a\n // cheap `*-flash`, but a slightly older flagship-marked model exists. The\n // picker must choose the flagship, not the newest-cheap.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v4-flash\", 1_900_000_000), // newest, but cheap tier\n mk(\"deepseek-v4\", 1_850_000_000), // bare frontier\n mk(\"deepseek-v4-coder\", 1_800_000_000), // flagship tier, oldest\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4-coder\"),\n \"a flagship-marked model must win over a newer cheap/flash sibling\"\n );\n}\n\n#[test]\nfn newest_release_picker_uses_recency_within_a_tier() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // All same (bare frontier) tier: recency decides.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v3\", 1_700_000_000),\n mk(\"deepseek-v4\", 1_900_000_000), // newest within the same tier\n mk(\"deepseek-v3.1\", 1_800_000_000),\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4\"),\n \"within one quality tier the newest release should win\"\n );\n}\n\n/// Exhaustiveness guard: every model shipped in a profile's static catalog must\n/// resolve to a concrete context window. Open-weight gateways frequently omit\n/// `context_length` from `/v1/models`, so a missing entry here means that model\n/// would silently fall back to the generic 200K default. First-party\n/// OpenAI/Claude/Gemini ids are resolved by their own providers (not this static\n/// table) and are exempted.\n#[test]\nfn every_static_profile_model_has_a_known_context_limit() {\n use jcode_provider_core::models::context_limit_for_model_with_provider;\n\n // Ids handled by dedicated first-party providers rather than the\n // OpenAI-compatible static table.\n fn is_first_party(model: &str) -> bool {\n let m = model.to_ascii_lowercase();\n m.starts_with(\"claude-\")\n || m.starts_with(\"gpt-\")\n || m.starts_with(\"gemini-\")\n || m.starts_with(\"o3\")\n || m.starts_with(\"o4\")\n }\n\n let mut missing: Vec<(String, String)> = Vec::new();\n for profile in jcode_provider_metadata::openai_compatible_profiles()\n .iter()\n .copied()\n {\n for model in openai_compatible_profile_static_models(profile) {\n if is_first_party(&model) {\n continue;\n }\n\n let via_profile = openai_compatible_profile_context_limit(profile.id, &model);\n let via_global = context_limit_for_model_with_provider(&model, Some(\"openrouter\"));\n\n if via_profile.is_none() && via_global.is_none() {\n missing.push((profile.id.to_string(), model));\n }\n }\n }\n\n assert!(\n missing.is_empty(),\n \"static profile models without a known context limit (would fall back to the \\\n generic default); add them to open_weight_family_context_limit: {missing:?}\"\n );\n}\n\n#[test]\nfn open_weight_family_context_limits_match_published_windows() {\n use jcode_provider_core::models::open_weight_family_context_limit as f;\n\n // GLM family spelling variants across gateways.\n assert_eq!(f(\"glm-4.5\"), Some(128_000));\n assert_eq!(f(\"glm-4.7\"), Some(200_000));\n assert_eq!(f(\"zai-org/glm-4.7\"), Some(200_000));\n assert_eq!(f(\"accounts/fireworks/models/glm-4p7\"), Some(200_000));\n assert_eq!(f(\"glm-5\"), Some(200_000));\n assert_eq!(f(\"glm-5.1\"), Some(200_000));\n assert_eq!(f(\"zai-glm-5-1\"), Some(200_000));\n assert_eq!(f(\"glm-5.2\"), Some(1_000_000));\n\n // Other open-weight families.\n assert_eq!(f(\"kimi-k2.5\"), Some(262_144));\n assert_eq!(f(\"minimax-m2.7\"), Some(204_800));\n assert_eq!(f(\"mimo-v2.5\"), Some(262_144));\n assert_eq!(f(\"deepseek-v3.2\"), Some(163_840));\n assert_eq!(f(\"deepseek-v4-pro\"), Some(1_000_000));\n assert_eq!(f(\"qwen3-235b-a22b-instruct-2507\"), Some(262_144));\n assert_eq!(f(\"gpt-oss-120b\"), Some(131_072));\n assert_eq!(f(\"llama-3.3-70b-instruct\"), Some(131_072));\n assert_eq!(f(\"sonar-pro\"), Some(128_000));\n\n // Unknown families stay unresolved so the dynamic cache/default can act.\n assert_eq!(f(\"some-unknown-model\"), None);\n}\n\n#[test]\nfn minimax_default_provider_applies_openai_api_key_env_not_openrouter() {\n // Regression for #407: `default_provider = \"minimax\"` (the built-in MiniMax\n // profile) must resolve credentials from the profile's documented\n // OPENAI_API_KEY / minimax.env, not the generic OPENROUTER_API_KEY /\n // openrouter.env. The earlier bug surfaced as\n // \"OPENROUTER_API_KEY not found ...\" when applying the configured\n // default_model.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ]);\n for v in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ] {\n crate::env::remove_var(v);\n }\n\n let selection = resolve_openai_compatible_profile_selection(\"minimax\");\n assert_eq!(\n selection.map(|profile| profile.id),\n Some(\"minimax\"),\n \"default_provider=minimax must resolve the built-in MiniMax profile\"\n );\n\n apply_openai_compatible_profile_env(selection);\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"OPENAI_API_KEY\"),\n \"MiniMax profile must use OPENAI_API_KEY, not OPENROUTER_API_KEY\"\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").ok().as_deref(),\n Some(\"minimax.env\"),\n \"MiniMax profile must use minimax.env, not openrouter.env\"\n );\n}\n"}, "1d516b9575237e96b5fc3fe90d74b9704ccdff46": {"t": 1785756986.1128569, "n": 41452, "h": "485d0ceb987c9157", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n\n#[test]\nfn quality_tier_ranks_flagship_above_bare_above_cheap() {\n // Flagship-marked ids (max/pro/opus/coder/large/huge-param) -> tier 2.\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-max\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"claude-opus-4-8\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-coder-480b\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-pro\"), 2);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-405b-instruct\"),\n 2\n );\n\n // Bare frontier ids (no tier marker) -> tier 1.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"kimi-k2.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6\"), 1);\n\n // Cheap/small/fast-marked ids -> tier 0.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5-mini\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"deepseek-v4-flash\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-air\"), 0);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-8b-instant\"),\n 0\n );\n assert_eq!(openai_compatible_model_quality_tier(\"claude-haiku-4-5\"), 0);\n\n // Brand names that merely *contain* a marker substring must NOT trip the\n // whole-token matcher: `minimax` is not `mini`/`max`.\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n\n // Flagship marker beats a co-occurring size token.\n assert_eq!(\n openai_compatible_model_quality_tier(\"qwen3-coder-30b-a3b\"),\n 2\n );\n}\n\n#[test]\nfn newest_release_picker_prefers_strongest_tier_over_newest_cheap() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // A heterogeneous proxy catalog (like OpenCode Zen): the NEWEST model is a\n // cheap `*-flash`, but a slightly older flagship-marked model exists. The\n // picker must choose the flagship, not the newest-cheap.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v4-flash\", 1_900_000_000), // newest, but cheap tier\n mk(\"deepseek-v4\", 1_850_000_000), // bare frontier\n mk(\"deepseek-v4-coder\", 1_800_000_000), // flagship tier, oldest\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4-coder\"),\n \"a flagship-marked model must win over a newer cheap/flash sibling\"\n );\n}\n\n#[test]\nfn newest_release_picker_uses_recency_within_a_tier() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // All same (bare frontier) tier: recency decides.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v3\", 1_700_000_000),\n mk(\"deepseek-v4\", 1_900_000_000), // newest within the same tier\n mk(\"deepseek-v3.1\", 1_800_000_000),\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4\"),\n \"within one quality tier the newest release should win\"\n );\n}\n\n/// Exhaustiveness guard: every model shipped in a profile's static catalog must\n/// resolve to a concrete context window. Open-weight gateways frequently omit\n/// `context_length` from `/v1/models`, so a missing entry here means that model\n/// would silently fall back to the generic 200K default. First-party\n/// OpenAI/Claude/Gemini ids are resolved by their own providers (not this static\n/// table) and are exempted.\n#[test]\nfn every_static_profile_model_has_a_known_context_limit() {\n use jcode_provider_core::models::context_limit_for_model_with_provider;\n\n // Ids handled by dedicated first-party providers rather than the\n // OpenAI-compatible static table.\n fn is_first_party(model: &str) -> bool {\n let m = model.to_ascii_lowercase();\n m.starts_with(\"claude-\")\n || m.starts_with(\"gpt-\")\n || m.starts_with(\"gemini-\")\n || m.starts_with(\"o3\")\n || m.starts_with(\"o4\")\n }\n\n let mut missing: Vec<(String, String)> = Vec::new();\n for profile in jcode_provider_metadata::openai_compatible_profiles()\n .iter()\n .copied()\n {\n for model in openai_compatible_profile_static_models(profile) {\n if is_first_party(&model) {\n continue;\n }\n\n let via_profile = openai_compatible_profile_context_limit(profile.id, &model);\n let via_global = context_limit_for_model_with_provider(&model, Some(\"openrouter\"));\n\n if via_profile.is_none() && via_global.is_none() {\n missing.push((profile.id.to_string(), model));\n }\n }\n }\n\n assert!(\n missing.is_empty(),\n \"static profile models without a known context limit (would fall back to the \\\n generic default); add them to open_weight_family_context_limit: {missing:?}\"\n );\n}\n\n#[test]\nfn open_weight_family_context_limits_match_published_windows() {\n use jcode_provider_core::models::open_weight_family_context_limit as f;\n\n // GLM family spelling variants across gateways.\n assert_eq!(f(\"glm-4.5\"), Some(128_000));\n assert_eq!(f(\"glm-4.7\"), Some(200_000));\n assert_eq!(f(\"zai-org/glm-4.7\"), Some(200_000));\n assert_eq!(f(\"accounts/fireworks/models/glm-4p7\"), Some(200_000));\n assert_eq!(f(\"glm-5\"), Some(200_000));\n assert_eq!(f(\"glm-5.1\"), Some(200_000));\n assert_eq!(f(\"zai-glm-5-1\"), Some(200_000));\n assert_eq!(f(\"glm-5.2\"), Some(1_000_000));\n\n // Other open-weight families.\n assert_eq!(f(\"kimi-k2.5\"), Some(262_144));\n assert_eq!(f(\"minimax-m2.7\"), Some(204_800));\n assert_eq!(f(\"mimo-v2.5\"), Some(262_144));\n assert_eq!(f(\"deepseek-v3.2\"), Some(163_840));\n assert_eq!(f(\"deepseek-v4-pro\"), Some(1_000_000));\n assert_eq!(f(\"qwen3-235b-a22b-instruct-2507\"), Some(262_144));\n assert_eq!(f(\"gpt-oss-120b\"), Some(131_072));\n assert_eq!(f(\"llama-3.3-70b-instruct\"), Some(131_072));\n assert_eq!(f(\"sonar-pro\"), Some(128_000));\n\n // Unknown families stay unresolved so the dynamic cache/default can act.\n assert_eq!(f(\"some-unknown-model\"), None);\n}\n\n#[test]\nfn minimax_default_provider_applies_openai_api_key_env_not_openrouter() {\n // Regression for #407: `default_provider = \"minimax\"` (the built-in MiniMax\n // profile) must resolve credentials from the profile's documented\n // OPENAI_API_KEY / minimax.env, not the generic OPENROUTER_API_KEY /\n // openrouter.env. The earlier bug surfaced as\n // \"OPENROUTER_API_KEY not found ...\" when applying the configured\n // default_model.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ]);\n for v in [\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n ] {\n crate::env::remove_var(v);\n }\n\n let selection = resolve_openai_compatible_profile_selection(\"minimax\");\n assert_eq!(\n selection.map(|profile| profile.id),\n Some(\"minimax\"),\n \"default_provider=minimax must resolve the built-in MiniMax profile\"\n );\n\n apply_openai_compatible_profile_env(selection);\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").ok().as_deref(),\n Some(\"OPENAI_API_KEY\"),\n \"MiniMax profile must use OPENAI_API_KEY, not OPENROUTER_API_KEY\"\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").ok().as_deref(),\n Some(\"minimax.env\"),\n \"MiniMax profile must use minimax.env, not openrouter.env\"\n );\n}\n"}, "105ca09cc23f38ecd7b5532c4fcc9cad61d4effc": {"t": 1785756987.5544243, "n": 39672, "h": "1b0aa702854dcf02", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n\n#[test]\nfn quality_tier_ranks_flagship_above_bare_above_cheap() {\n // Flagship-marked ids (max/pro/opus/coder/large/huge-param) -> tier 2.\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-max\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"claude-opus-4-8\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-coder-480b\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-pro\"), 2);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-405b-instruct\"),\n 2\n );\n\n // Bare frontier ids (no tier marker) -> tier 1.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"kimi-k2.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6\"), 1);\n\n // Cheap/small/fast-marked ids -> tier 0.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5-mini\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"deepseek-v4-flash\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-air\"), 0);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-8b-instant\"),\n 0\n );\n assert_eq!(openai_compatible_model_quality_tier(\"claude-haiku-4-5\"), 0);\n\n // Brand names that merely *contain* a marker substring must NOT trip the\n // whole-token matcher: `minimax` is not `mini`/`max`.\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n\n // Flagship marker beats a co-occurring size token.\n assert_eq!(\n openai_compatible_model_quality_tier(\"qwen3-coder-30b-a3b\"),\n 2\n );\n}\n\n#[test]\nfn newest_release_picker_prefers_strongest_tier_over_newest_cheap() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // A heterogeneous proxy catalog (like OpenCode Zen): the NEWEST model is a\n // cheap `*-flash`, but a slightly older flagship-marked model exists. The\n // picker must choose the flagship, not the newest-cheap.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v4-flash\", 1_900_000_000), // newest, but cheap tier\n mk(\"deepseek-v4\", 1_850_000_000), // bare frontier\n mk(\"deepseek-v4-coder\", 1_800_000_000), // flagship tier, oldest\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4-coder\"),\n \"a flagship-marked model must win over a newer cheap/flash sibling\"\n );\n}\n\n#[test]\nfn newest_release_picker_uses_recency_within_a_tier() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // All same (bare frontier) tier: recency decides.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v3\", 1_700_000_000),\n mk(\"deepseek-v4\", 1_900_000_000), // newest within the same tier\n mk(\"deepseek-v3.1\", 1_800_000_000),\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4\"),\n \"within one quality tier the newest release should win\"\n );\n}\n\n/// Exhaustiveness guard: every model shipped in a profile's static catalog must\n/// resolve to a concrete context window. Open-weight gateways frequently omit\n/// `context_length` from `/v1/models`, so a missing entry here means that model\n/// would silently fall back to the generic 200K default. First-party\n/// OpenAI/Claude/Gemini ids are resolved by their own providers (not this static\n/// table) and are exempted.\n#[test]\nfn every_static_profile_model_has_a_known_context_limit() {\n use jcode_provider_core::models::context_limit_for_model_with_provider;\n\n // Ids handled by dedicated first-party providers rather than the\n // OpenAI-compatible static table.\n fn is_first_party(model: &str) -> bool {\n let m = model.to_ascii_lowercase();\n m.starts_with(\"claude-\")\n || m.starts_with(\"gpt-\")\n || m.starts_with(\"gemini-\")\n || m.starts_with(\"o3\")\n || m.starts_with(\"o4\")\n }\n\n let mut missing: Vec<(String, String)> = Vec::new();\n for profile in jcode_provider_metadata::openai_compatible_profiles()\n .iter()\n .copied()\n {\n for model in openai_compatible_profile_static_models(profile) {\n if is_first_party(&model) {\n continue;\n }\n\n let via_profile = openai_compatible_profile_context_limit(profile.id, &model);\n let via_global = context_limit_for_model_with_provider(&model, Some(\"openrouter\"));\n\n if via_profile.is_none() && via_global.is_none() {\n missing.push((profile.id.to_string(), model));\n }\n }\n }\n\n assert!(\n missing.is_empty(),\n \"static profile models without a known context limit (would fall back to the \\\n generic default); add them to open_weight_family_context_limit: {missing:?}\"\n );\n}\n\n#[test]\nfn open_weight_family_context_limits_match_published_windows() {\n use jcode_provider_core::models::open_weight_family_context_limit as f;\n\n // GLM family spelling variants across gateways.\n assert_eq!(f(\"glm-4.5\"), Some(128_000));\n assert_eq!(f(\"glm-4.7\"), Some(200_000));\n assert_eq!(f(\"zai-org/glm-4.7\"), Some(200_000));\n assert_eq!(f(\"accounts/fireworks/models/glm-4p7\"), Some(200_000));\n assert_eq!(f(\"glm-5\"), Some(200_000));\n assert_eq!(f(\"glm-5.1\"), Some(200_000));\n assert_eq!(f(\"zai-glm-5-1\"), Some(200_000));\n assert_eq!(f(\"glm-5.2\"), Some(1_000_000));\n\n // Other open-weight families.\n assert_eq!(f(\"kimi-k2.5\"), Some(262_144));\n assert_eq!(f(\"minimax-m2.7\"), Some(204_800));\n assert_eq!(f(\"mimo-v2.5\"), Some(262_144));\n assert_eq!(f(\"deepseek-v3.2\"), Some(163_840));\n assert_eq!(f(\"deepseek-v4-pro\"), Some(1_000_000));\n assert_eq!(f(\"qwen3-235b-a22b-instruct-2507\"), Some(262_144));\n assert_eq!(f(\"gpt-oss-120b\"), Some(131_072));\n assert_eq!(f(\"llama-3.3-70b-instruct\"), Some(131_072));\n assert_eq!(f(\"sonar-pro\"), Some(128_000));\n\n // Unknown families stay unresolved so the dynamic cache/default can act.\n assert_eq!(f(\"some-unknown-model\"), None);\n}\n"}, "5e7ab427edf8ccfa884cdab061e7f2fef6461368": {"t": 1785756989.2627485, "n": 39688, "h": "c059a828694a4333", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n\n#[test]\nfn quality_tier_ranks_flagship_above_bare_above_cheap() {\n // Flagship-marked ids (max/pro/opus/coder/large/huge-param) -> tier 2.\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-max\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"claude-opus-4-8\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-coder-480b\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-pro\"), 2);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-405b-instruct\"),\n 2\n );\n\n // Bare frontier ids (no tier marker) -> tier 1.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"kimi-k2.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6\"), 1);\n\n // Cheap/small/fast-marked ids -> tier 0.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5-mini\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"deepseek-v4-flash\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-air\"), 0);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-8b-instant\"),\n 0\n );\n assert_eq!(openai_compatible_model_quality_tier(\"claude-haiku-4-5\"), 0);\n\n // Brand names that merely *contain* a marker substring must NOT trip the\n // whole-token matcher: `minimax` is not `mini`/`max`.\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n\n // Flagship marker beats a co-occurring size token.\n assert_eq!(\n openai_compatible_model_quality_tier(\"qwen3-coder-30b-a3b\"),\n 2\n );\n}\n\n#[test]\nfn newest_release_picker_prefers_strongest_tier_over_newest_cheap() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // A heterogeneous proxy catalog (like OpenCode Zen): the NEWEST model is a\n // cheap `*-flash`, but a slightly older flagship-marked model exists. The\n // picker must choose the flagship, not the newest-cheap.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v4-flash\", 1_900_000_000), // newest, but cheap tier\n mk(\"deepseek-v4\", 1_850_000_000), // bare frontier\n mk(\"deepseek-v4-coder\", 1_800_000_000), // flagship tier, oldest\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4-coder\"),\n \"a flagship-marked model must win over a newer cheap/flash sibling\"\n );\n}\n\n#[test]\nfn newest_release_picker_uses_recency_within_a_tier() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // All same (bare frontier) tier: recency decides.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v3\", 1_700_000_000),\n mk(\"deepseek-v4\", 1_900_000_000), // newest within the same tier\n mk(\"deepseek-v3.1\", 1_800_000_000),\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4\"),\n \"within one quality tier the newest release should win\"\n );\n}\n\n/// Exhaustiveness guard: every model shipped in a profile's static catalog must\n/// resolve to a concrete context window. Open-weight gateways frequently omit\n/// `context_length` from `/v1/models`, so a missing entry here means that model\n/// would silently fall back to the generic 200K default. First-party\n/// OpenAI/Claude/Gemini ids are resolved by their own providers (not this static\n/// table) and are exempted.\n#[test]\nfn every_static_profile_model_has_a_known_context_limit() {\n use jcode_provider_core::models::context_limit_for_model_with_provider;\n\n // Ids handled by dedicated first-party providers rather than the\n // OpenAI-compatible static table.\n fn is_first_party(model: &str) -> bool {\n let m = model.to_ascii_lowercase();\n m.starts_with(\"claude-\")\n || m.starts_with(\"gpt-\")\n || m.starts_with(\"gemini-\")\n || m.starts_with(\"o3\")\n || m.starts_with(\"o4\")\n }\n\n let mut missing: Vec<(String, String)> = Vec::new();\n for profile in jcode_provider_metadata::openai_compatible_profiles()\n .iter()\n .copied()\n {\n for model in openai_compatible_profile_static_models(profile) {\n if is_first_party(&model) {\n continue;\n }\n\n let via_profile = openai_compatible_profile_context_limit(profile.id, &model);\n let via_global =\n context_limit_for_model_with_provider(&model, Some(\"openrouter\"));\n\n if via_profile.is_none() && via_global.is_none() {\n missing.push((profile.id.to_string(), model));\n }\n }\n }\n\n assert!(\n missing.is_empty(),\n \"static profile models without a known context limit (would fall back to the \\\n generic default); add them to open_weight_family_context_limit: {missing:?}\"\n );\n}\n\n#[test]\nfn open_weight_family_context_limits_match_published_windows() {\n use jcode_provider_core::models::open_weight_family_context_limit as f;\n\n // GLM family spelling variants across gateways.\n assert_eq!(f(\"glm-4.5\"), Some(128_000));\n assert_eq!(f(\"glm-4.7\"), Some(200_000));\n assert_eq!(f(\"zai-org/glm-4.7\"), Some(200_000));\n assert_eq!(f(\"accounts/fireworks/models/glm-4p7\"), Some(200_000));\n assert_eq!(f(\"glm-5\"), Some(200_000));\n assert_eq!(f(\"glm-5.1\"), Some(200_000));\n assert_eq!(f(\"zai-glm-5-1\"), Some(200_000));\n assert_eq!(f(\"glm-5.2\"), Some(1_000_000));\n\n // Other open-weight families.\n assert_eq!(f(\"kimi-k2.5\"), Some(262_144));\n assert_eq!(f(\"minimax-m2.7\"), Some(204_800));\n assert_eq!(f(\"mimo-v2.5\"), Some(262_144));\n assert_eq!(f(\"deepseek-v3.2\"), Some(163_840));\n assert_eq!(f(\"deepseek-v4-pro\"), Some(1_000_000));\n assert_eq!(f(\"qwen3-235b-a22b-instruct-2507\"), Some(262_144));\n assert_eq!(f(\"gpt-oss-120b\"), Some(131_072));\n assert_eq!(f(\"llama-3.3-70b-instruct\"), Some(131_072));\n assert_eq!(f(\"sonar-pro\"), Some(128_000));\n\n // Unknown families stay unresolved so the dynamic cache/default can act.\n assert_eq!(f(\"some-unknown-model\"), None);\n}\n"}, "394920b35589c40308db0ace84f683a8240e7104": {"t": 1785756990.984149, "n": 36588, "h": "85cbce3a4581cc05", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n\n#[test]\nfn quality_tier_ranks_flagship_above_bare_above_cheap() {\n // Flagship-marked ids (max/pro/opus/coder/large/huge-param) -> tier 2.\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-max\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"claude-opus-4-8\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"qwen3-coder-480b\"), 2);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-pro\"), 2);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-405b-instruct\"),\n 2\n );\n\n // Bare frontier ids (no tier marker) -> tier 1.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"kimi-k2.5\"), 1);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6\"), 1);\n\n // Cheap/small/fast-marked ids -> tier 0.\n assert_eq!(openai_compatible_model_quality_tier(\"gpt-5.5-mini\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"deepseek-v4-flash\"), 0);\n assert_eq!(openai_compatible_model_quality_tier(\"glm-4.6-air\"), 0);\n assert_eq!(\n openai_compatible_model_quality_tier(\"llama-3.1-8b-instant\"),\n 0\n );\n assert_eq!(openai_compatible_model_quality_tier(\"claude-haiku-4-5\"), 0);\n\n // Brand names that merely *contain* a marker substring must NOT trip the\n // whole-token matcher: `minimax` is not `mini`/`max`.\n assert_eq!(openai_compatible_model_quality_tier(\"minimax-m2.7\"), 1);\n\n // Flagship marker beats a co-occurring size token.\n assert_eq!(\n openai_compatible_model_quality_tier(\"qwen3-coder-30b-a3b\"),\n 2\n );\n}\n\n#[test]\nfn newest_release_picker_prefers_strongest_tier_over_newest_cheap() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // A heterogeneous proxy catalog (like OpenCode Zen): the NEWEST model is a\n // cheap `*-flash`, but a slightly older flagship-marked model exists. The\n // picker must choose the flagship, not the newest-cheap.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v4-flash\", 1_900_000_000), // newest, but cheap tier\n mk(\"deepseek-v4\", 1_850_000_000), // bare frontier\n mk(\"deepseek-v4-coder\", 1_800_000_000), // flagship tier, oldest\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4-coder\"),\n \"a flagship-marked model must win over a newer cheap/flash sibling\"\n );\n}\n\n#[test]\nfn newest_release_picker_uses_recency_within_a_tier() {\n use jcode_provider_openrouter::ModelInfo;\n let _env = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n\n let mk = |id: &str, created: u64| ModelInfo {\n id: id.to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(created),\n };\n\n // All same (bare frontier) tier: recency decides.\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"deepseek\",\n &[\n mk(\"deepseek-v3\", 1_700_000_000),\n mk(\"deepseek-v4\", 1_900_000_000), // newest within the same tier\n mk(\"deepseek-v3.1\", 1_800_000_000),\n ],\n Some(\"https://api.deepseek.com\"),\n );\n\n assert_eq!(\n newest_released_model_for_openai_compatible_profile(\"deepseek\").as_deref(),\n Some(\"deepseek-v4\"),\n \"within one quality tier the newest release should win\"\n );\n}\n"}, "19f8626d8b36408d131c253b676e2aab0ff66220": {"t": 1785756992.481181, "n": 32490, "h": "95276460e308e88d", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n // `anthropic-api` sits at 3 (between claude and openai), shifting the\n // rest of the list down one slot relative to the pre-May-2026 order.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(7)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n // `anthropic-api` at 3 shifted everything after it down one slot.\n assert_eq!(\n resolve_login_selection(\"3\", &providers).map(|provider| provider.id),\n Some(\"anthropic-api\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"9\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n"}, "0a67a15243cf46fa40f934a14db7621902eb15cf": {"t": 1785756993.9323103, "n": 32008, "h": "e7fbc76cc8ed7224", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn resolved_named_profile_skips_non_chat_models_when_picking_newest_default() {\n // Regression: a profile's auto-selected default must never be a non-chat\n // model (TTS/speech/embeddings/image/etc.). Catalogs such as Groq expose\n // their entire model list, and the newest-released entry is frequently a\n // non-chat model (e.g. `canopylabs/orpheus-*` TTS) which previously won the\n // newest-by-created tiebreak and became the chat default.\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-chat-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n // Newest of all, but a non-chat (TTS) model that must be skipped.\n jcode_provider_openrouter::ModelInfo {\n id: \"canopylabs/orpheus-v1-english\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_900_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"whisper-large-v3\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_950_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"newer-chat-model\"),\n \"newest *chat* model must win; non-chat models (orpheus TTS, whisper STT) are skipped\"\n );\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(6)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"4\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n"}, "bf0d77c0340e9dc15479fc131ba915009e7eb34f": {"t": 1785756995.7840545, "n": 29673, "h": "670b1cc9968fc371", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn resolved_named_profile_suggests_newest_cached_live_release() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"JCODE_HOME\"]);\n let temp = tempfile::tempdir().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n jcode_provider_openrouter::save_disk_cache_with_source_for_namespace(\n \"cerebras\",\n &[\n jcode_provider_openrouter::ModelInfo {\n id: \"older-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_700_000_000),\n },\n jcode_provider_openrouter::ModelInfo {\n id: \"newer-model\".to_string(),\n name: String::new(),\n context_length: None,\n pricing: Default::default(),\n created: Some(1_800_000_000),\n },\n ],\n Some(CEREBRAS_PROFILE.api_base),\n );\n\n let resolved = resolve_openai_compatible_profile(CEREBRAS_PROFILE);\n\n assert_eq!(resolved.default_model.as_deref(), Some(\"newer-model\"));\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(6)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"4\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n"}, "4aec863e2481c33ce7eb1beed98a935f92c90d08": {"t": 1785756997.6305459, "n": 28523, "h": "f3ff52e3f4e8d4ad", "c": "use super::*;\n\nstruct EnvGuard {\n vars: Vec<(String, Option<String>)>,\n}\n\nimpl EnvGuard {\n fn save(keys: &[&str]) -> Self {\n let vars = keys\n .iter()\n .map(|key| (key.to_string(), std::env::var(key).ok()))\n .collect();\n Self { vars }\n }\n}\n\nimpl Drop for EnvGuard {\n fn drop(&mut self) {\n for (key, value) in &self.vars {\n if let Some(value) = value {\n crate::env::set_var(key, value);\n } else {\n crate::env::remove_var(key);\n }\n }\n }\n}\n\n#[test]\nfn matrix_profiles_have_unique_ids_and_safe_metadata() {\n let mut ids = HashSet::new();\n for profile in openai_compatible_profiles() {\n assert!(\n ids.insert(profile.id),\n \"duplicate provider profile id: {}\",\n profile.id\n );\n assert!(is_safe_env_key_name(profile.api_key_env));\n assert!(is_safe_env_file_name(profile.env_file));\n assert_eq!(\n normalize_api_base(profile.api_base).as_deref(),\n Some(profile.api_base)\n );\n }\n}\n\n#[test]\nfn matrix_login_provider_aliases_resolve_to_canonical_ids() {\n assert_eq!(\n resolve_login_provider(\"subscription\").map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_provider(\"anthropic\").map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_provider(\"opencodego\").map(|provider| provider.id),\n Some(\"opencode-go\")\n );\n assert_eq!(\n resolve_login_provider(\"z.ai\").map(|provider| provider.id),\n Some(\"zai\")\n );\n assert_eq!(\n resolve_login_provider(\"compat\").map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_provider(\"aoai\").map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_provider(\"cerberascode\").map(|provider| provider.id),\n Some(\"cerebras\")\n );\n assert_eq!(\n resolve_login_provider(\"bailian\").map(|provider| provider.id),\n Some(\"alibaba-coding-plan\")\n );\n assert_eq!(\n resolve_login_provider(\"gmail\").map(|provider| provider.id),\n Some(\"google\")\n );\n}\n\n#[test]\nfn auth_issue_profile_metadata_matches_direct_provider_endpoints() {\n assert_eq!(ZAI_PROFILE.api_base, \"https://api.z.ai/api/coding/paas/v4\");\n assert_eq!(ZAI_PROFILE.default_model, Some(\"glm-4.5\"));\n assert_eq!(DEEPSEEK_PROFILE.api_base, \"https://api.deepseek.com\");\n assert_eq!(DEEPSEEK_PROFILE.default_model, Some(\"deepseek-v4-flash\"));\n assert_eq!(DEEPSEEK_PROFILE.setup_url, \"https://api-docs.deepseek.com/\");\n assert_eq!(MINIMAX_PROFILE.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(MINIMAX_PROFILE.api_key_env, \"OPENAI_API_KEY\");\n assert_eq!(\n ALIBABA_CODING_PLAN_PROFILE.api_base,\n \"https://coding-intl.dashscope.aliyuncs.com/v1\"\n );\n assert_eq!(COMTEGRA_PROFILE.api_base, \"https://llm.comtegra.cloud/v1\");\n assert_eq!(COMTEGRA_PROFILE.default_model, Some(\"glm-51-nvfp4\"));\n assert_eq!(COMTEGRA_PROFILE.api_key_env, \"COMTEGRA_API_KEY\");\n assert_eq!(CEREBRAS_PROFILE.api_base, \"https://api.cerebras.ai/v1\");\n assert_eq!(CEREBRAS_PROFILE.default_model, Some(\"gpt-oss-120b\"));\n assert!(!OPENAI_COMPAT_PROFILE.setup_url.contains(\"opencode.ai\"));\n}\n\n#[test]\nfn minimax_token_plan_keys_resolve_to_china_endpoint_without_changing_international_default() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\"OPENAI_API_KEY\"]);\n crate::env::remove_var(\"OPENAI_API_KEY\");\n\n let international = resolve_openai_compatible_profile(MINIMAX_PROFILE);\n assert_eq!(international.api_base, \"https://api.minimax.io/v1\");\n assert_eq!(\n international.setup_url,\n \"https://platform.minimax.io/docs/guides/text-generation\"\n );\n\n let china = resolve_openai_compatible_profile_with_api_key_hint(\n MINIMAX_PROFILE,\n Some(\"sk-cp-test-token\"),\n );\n assert_eq!(china.api_base, MINIMAX_CHINA_API_BASE);\n assert_eq!(china.setup_url, MINIMAX_CHINA_SETUP_URL);\n}\n\n#[test]\nfn auth_issue_lan_openai_compatible_bases_are_valid_for_local_model_servers() {\n assert_eq!(\n normalize_api_base(\"http://100.103.78.84:11434/v1\").as_deref(),\n Some(\"http://100.103.78.84:11434/v1\")\n );\n assert_eq!(\n normalize_api_base(\"http://hsv.local:11434/v1\").as_deref(),\n Some(\"http://hsv.local:11434/v1\")\n );\n assert_eq!(normalize_api_base(\"http://example.com/v1\"), None);\n}\n\n#[test]\nfn auth_issue_runtime_display_name_tracks_direct_compatible_profiles() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_RUNTIME_PROVIDER\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n ]);\n\n crate::env::set_var(\"JCODE_RUNTIME_PROVIDER\", \"azure-openai\");\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Azure OpenAI\");\n crate::env::remove_var(\"JCODE_RUNTIME_PROVIDER\");\n\n apply_openai_compatible_profile_env(Some(DEEPSEEK_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"DeepSeek\");\n\n apply_openai_compatible_profile_env(Some(ZAI_PROFILE));\n assert_eq!(runtime_provider_display_name(\"openrouter\"), \"Z.AI\");\n}\n\n#[test]\nfn auth_profile_env_application_flushes_stale_openrouter_catalog_state() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\",\n \"JCODE_OPENROUTER_PROVIDER\",\n \"JCODE_OPENROUTER_NO_FALLBACK\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_PROFILE_ACTIVE\",\n \"JCODE_PROVIDER_PROFILE_NAME\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_BASE\", \"https://openrouter.ai/api/v1\");\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"OPENROUTER_API_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"openrouter.env\");\n crate::env::set_var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\", \"1\");\n crate::env::set_var(\"JCODE_OPENROUTER_TRANSPORT_STATE\", \"stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\", \"1\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"stale-openrouter-catalog.json\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_MODEL\", \"gpt-5.5\");\n crate::env::set_var(\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"stale-openrouter-only-model\",\n );\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER\", \"Bearer stale\");\n crate::env::set_var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\", \"Authorization\");\n crate::env::set_var(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_PROVIDER\", \"openrouter\");\n crate::env::set_var(\"JCODE_OPENROUTER_NO_FALLBACK\", \"1\");\n crate::env::set_var(\"JCODE_NAMED_PROVIDER_PROFILE\", \"openrouter\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_ACTIVE\", \"1\");\n crate::env::set_var(\"JCODE_PROVIDER_PROFILE_NAME\", \"openrouter\");\n\n force_apply_openai_compatible_profile_env(Some(CEREBRAS_PROFILE));\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").as_deref(),\n Ok(\"https://api.cerebras.ai/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\").as_deref(),\n Ok(\"CEREBRAS_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_ENV_FILE\").as_deref(),\n Ok(\"cerebras.env\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_CACHE_NAMESPACE\").as_deref(),\n Ok(\"cerebras\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\").as_deref(),\n Ok(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\").as_deref(),\n Ok(\"direct-api-key\")\n );\n assert!(std::env::var_os(\"JCODE_OPENROUTER_ALLOW_NO_AUTH\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL_CATALOG\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_MODEL\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_DYNAMIC_BEARER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_PROVIDER\").is_none());\n assert!(std::env::var_os(\"JCODE_OPENROUTER_NO_FALLBACK\").is_none());\n assert!(std::env::var_os(\"JCODE_NAMED_PROVIDER_PROFILE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_ACTIVE\").is_none());\n assert!(std::env::var_os(\"JCODE_PROVIDER_PROFILE_NAME\").is_none());\n assert_ne!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"stale-openrouter-only-model\")\n );\n}\n\n#[test]\nfn matrix_login_provider_ids_and_aliases_are_unique() {\n let mut seen = std::collections::HashSet::new();\n for provider in login_providers() {\n assert!(\n seen.insert(provider.id),\n \"duplicate login provider identifier: {}\",\n provider.id\n );\n for alias in provider.aliases {\n assert!(\n seen.insert(*alias),\n \"duplicate login provider alias: {}\",\n alias\n );\n }\n }\n}\n\n#[test]\nfn matrix_tui_login_selection_supports_numbers_and_names() {\n let providers = tui_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"2\", &providers).map(|provider| provider.id),\n Some(\"claude\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"compat\", &providers).map(|provider| provider.id),\n Some(\"openai-compatible\")\n );\n assert_eq!(\n resolve_login_selection(\"cgc\", &providers).map(|provider| provider.id),\n Some(\"comtegra\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .take(6)\n .any(|provider| provider.id == \"bedrock\")\n );\n assert!(resolve_login_selection(\"google\", &providers).is_none());\n}\n\n#[test]\nfn matrix_cli_login_selection_preserves_existing_order() {\n let providers = cli_login_providers();\n assert_eq!(\n resolve_login_selection(\"1\", &providers).map(|provider| provider.id),\n Some(\"auto-import\")\n );\n assert_eq!(\n resolve_login_selection(\"4\", &providers).map(|provider| provider.id),\n Some(\"jcode\")\n );\n assert_eq!(\n resolve_login_selection(\"5\", &providers).map(|provider| provider.id),\n Some(\"copilot\")\n );\n assert_eq!(\n resolve_login_selection(\"6\", &providers).map(|provider| provider.id),\n Some(\"openrouter\")\n );\n assert_eq!(\n resolve_login_selection(\"7\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert_eq!(\n resolve_login_selection(\"8\", &providers).map(|provider| provider.id),\n Some(\"azure\")\n );\n assert_eq!(\n resolve_login_selection(\"bedrock\", &providers).map(|provider| provider.id),\n Some(\"bedrock\")\n );\n assert!(\n providers\n .iter()\n .position(|provider| provider.id == \"bedrock\")\n < providers.iter().position(|provider| provider.id == \"azure\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_include_all_static_profiles() {\n let _lock = crate::storage::lock_test_env();\n let guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n crate::env::remove_var(\"JCODE_OPENROUTER_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENROUTER_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n\n let sources = openrouter_like_api_key_sources();\n drop(guard);\n\n assert!(sources.contains(&(\n \"OPENROUTER_API_KEY\".to_string(),\n \"openrouter.env\".to_string()\n )));\n for profile in openai_compatible_profiles() {\n if profile.requires_api_key {\n assert!(sources.contains(&(\n profile.api_key_env.to_string(),\n profile.env_file.to_string()\n )));\n }\n }\n}\n\n#[test]\nfn matrix_openrouter_like_sources_accept_valid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"ALT_OPENROUTER_KEY\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"alt-openrouter.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"ALT_COMPAT_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"alt-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(sources.contains(&(\n \"ALT_OPENROUTER_KEY\".to_string(),\n \"alt-openrouter.env\".to_string()\n )));\n assert!(sources.contains(&(\"ALT_COMPAT_KEY\".to_string(), \"alt-compat.env\".to_string())));\n}\n\n#[test]\nfn named_provider_config_accepts_openai_compatible_spelling() {\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n auth = \"bearer\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n input = [\"text\"]\n \"#,\n )\n .expect(\"config should parse\");\n\n let profile = cfg.providers.get(\"my-gateway\").expect(\"profile\");\n assert_eq!(\n profile.provider_type,\n crate::config::NamedProviderType::OpenAiCompatible\n );\n assert_eq!(profile.base_url, \"https://llm.example.com/v1\");\n assert_eq!(profile.default_model.as_deref(), Some(\"opaque/model@id\"));\n assert_eq!(profile.models[0].id, \"opaque/model@id\");\n}\n\n#[test]\nfn named_provider_profile_reports_malformed_config_instead_of_unknown_profile() {\n let _lock = crate::storage::lock_test_env();\n let previous_home = std::env::var_os(\"JCODE_HOME\");\n let temp = tempfile::TempDir::new().expect(\"tempdir\");\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::config::Config::invalidate_cache();\n\n let config_path = crate::config::Config::path().expect(\"config path\");\n std::fs::create_dir_all(config_path.parent().expect(\"config parent\"))\n .expect(\"create config dir\");\n std::fs::write(\n &config_path,\n r#\"\n [providers.antigravity]\n type = \"anthropic-compatible\"\n base_url = \"http://192.168.1.202:8080\"\n api_key_env = \"ANTIGRAVITY_API_KEY\"\n default_model = \"gemini-3.1-pro-low\"\n\n [[providers.antigravity.models]]\n id = \"gemini-3.1-pro-low\"\n context_window = 128000\n \"#,\n )\n .expect(\"write config\");\n\n let err = apply_named_provider_profile_env(\"antigravity\").expect_err(\"malformed config\");\n let message = err.to_string();\n assert!(\n message.contains(\"Failed to parse config file\"),\n \"unexpected error: {message}\"\n );\n assert!(\n message.contains(\"anthropic-compatible\"),\n \"unexpected error: {message}\"\n );\n assert!(\n !message.contains(\"Unknown provider profile\"),\n \"unexpected error: {message}\"\n );\n\n if let Some(previous_home) = previous_home {\n crate::env::set_var(\"JCODE_HOME\", previous_home);\n } else {\n crate::env::remove_var(\"JCODE_HOME\");\n }\n crate::config::Config::invalidate_cache();\n}\n\n#[test]\nfn named_provider_profile_maps_to_openai_compatible_runtime_env() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_ALLOW_NO_AUTH\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_OPENROUTER_MODEL\",\n \"JCODE_OPENROUTER_STATIC_MODELS\",\n \"JCODE_OPENROUTER_AUTH_HEADER\",\n \"JCODE_OPENROUTER_AUTH_HEADER_NAME\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1/\"\n auth = \"header\"\n auth_header = \"x-api-key\"\n api_key_env = \"MY_GATEWAY_API_KEY\"\n default_model = \"opaque/model@id\"\n model_catalog = false\n\n [[providers.my-gateway.models]]\n id = \"opaque/model@id\"\n\n [[providers.my-gateway.models]]\n id = \"another-local-id\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_BASE\").ok().as_deref(),\n Some(\"https://llm.example.com/v1\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_PROVIDER_FEATURES\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_TRANSPORT_STATE\")\n .ok()\n .as_deref(),\n Some(\"direct-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL_CATALOG\")\n .ok()\n .as_deref(),\n Some(\"0\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_MODEL\").ok().as_deref(),\n Some(\"opaque/model@id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_STATIC_MODELS\")\n .ok()\n .as_deref(),\n Some(\"opaque/model@id\\nanother-local-id\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER\")\n .ok()\n .as_deref(),\n Some(\"api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_AUTH_HEADER_NAME\")\n .ok()\n .as_deref(),\n Some(\"x-api-key\")\n );\n assert_eq!(\n std::env::var(\"JCODE_NAMED_PROVIDER_PROFILE\")\n .ok()\n .as_deref(),\n Some(\"my-gateway\")\n );\n}\n\n#[test]\nfn named_provider_inline_api_key_is_private_runtime_fallback() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_BASE\",\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_CACHE_NAMESPACE\",\n \"JCODE_OPENROUTER_PROVIDER_FEATURES\",\n \"JCODE_OPENROUTER_TRANSPORT_STATE\",\n \"JCODE_OPENROUTER_MODEL_CATALOG\",\n \"JCODE_NAMED_PROVIDER_PROFILE\",\n \"JCODE_PROVIDER_MY_GATEWAY_API_KEY\",\n ]);\n\n let cfg: crate::config::Config = toml::from_str(\n r#\"\n [providers.my-gateway]\n type = \"openai-compatible\"\n base_url = \"https://llm.example.com/v1\"\n api_key = \"inline-secret\"\n \"#,\n )\n .expect(\"config should parse\");\n\n apply_named_provider_profile_env_from_config(\"my-gateway\", &cfg).expect(\"apply profile\");\n\n assert_eq!(\n std::env::var(\"JCODE_OPENROUTER_API_KEY_NAME\")\n .ok()\n .as_deref(),\n Some(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n );\n assert_eq!(\n std::env::var(\"JCODE_PROVIDER_MY_GATEWAY_API_KEY\")\n .ok()\n .as_deref(),\n Some(\"inline-secret\")\n );\n}\n\n#[test]\nfn matrix_openrouter_like_sources_reject_invalid_overrides() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENROUTER_API_KEY_NAME\",\n \"JCODE_OPENROUTER_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENROUTER_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENROUTER_ENV_FILE\", \"../bad.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad key\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad-compat.env\");\n\n let sources = openrouter_like_api_key_sources();\n assert!(\n !sources\n .iter()\n .any(|(key, _)| key == \"bad-key-name\" || key == \"bad key\")\n );\n assert!(\n !sources\n .iter()\n .any(|(_, file)| file == \"../bad.env\" || file == \"../bad-compat.env\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_apply_when_valid() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n\n crate::env::set_var(\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"https://api.groq.com/openai/v1/\",\n );\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"GROQ_API_KEY\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"groq.env\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\", \"openai/gpt-oss-120b\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.groq.com/openai/v1\");\n assert_eq!(resolved.api_key_env, \"GROQ_API_KEY\");\n assert_eq!(resolved.env_file, \"groq.env\");\n assert_eq!(\n resolved.default_model.as_deref(),\n Some(\"openai/gpt-oss-120b\")\n );\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_reject_invalid_values() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://example.com/v1\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\", \"bad-key-name\");\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\", \"../bad.env\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, OPENAI_COMPAT_PROFILE.api_base);\n assert_eq!(resolved.api_key_env, OPENAI_COMPAT_PROFILE.api_key_env);\n assert_eq!(resolved.env_file, OPENAI_COMPAT_PROFILE.env_file);\n}\n\n#[test]\nfn matrix_openai_compatible_profile_overrides_read_from_env_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\n \"JCODE_HOME\",\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\",\n ]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_BASE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_DEFAULT_MODEL\");\n std::fs::write(\n config_root.join(OPENAI_COMPAT_PROFILE.env_file),\n concat!(\n \"JCODE_OPENAI_COMPAT_API_BASE=https://api.example.com/v1\\n\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME=EXAMPLE_API_KEY\\n\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE=example.env\\n\",\n \"JCODE_OPENAI_COMPAT_DEFAULT_MODEL=example/model\\n\",\n ),\n )\n .expect(\"env file\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"https://api.example.com/v1\");\n assert_eq!(resolved.api_key_env, \"EXAMPLE_API_KEY\");\n assert_eq!(resolved.env_file, \"example.env\");\n assert_eq!(resolved.default_model.as_deref(), Some(\"example/model\"));\n}\n\n#[test]\nfn matrix_openai_compatible_localhost_override_allows_no_auth() {\n let _lock = crate::storage::lock_test_env();\n let _guard = EnvGuard::save(&[\n \"JCODE_OPENAI_COMPAT_API_BASE\",\n \"JCODE_OPENAI_COMPAT_API_KEY_NAME\",\n \"JCODE_OPENAI_COMPAT_ENV_FILE\",\n \"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\",\n ]);\n\n crate::env::set_var(\"JCODE_OPENAI_COMPAT_API_BASE\", \"http://localhost:11434/v1\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_API_KEY_NAME\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_ENV_FILE\");\n crate::env::remove_var(\"JCODE_OPENAI_COMPAT_LOCAL_ENABLED\");\n\n let resolved = resolve_openai_compatible_profile(OPENAI_COMPAT_PROFILE);\n assert_eq!(resolved.api_base, \"http://localhost:11434/v1\");\n assert!(!resolved.requires_api_key);\n assert!(openai_compatible_profile_is_configured(\n OPENAI_COMPAT_PROFILE\n ));\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_prefers_env() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::set_var(\"OPENCODE_API_KEY\", \"env-secret\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"env-secret\")\n );\n}\n\n#[test]\nfn matrix_load_api_key_from_env_or_config_reads_config_file() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"OPENCODE_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"OPENCODE_API_KEY\");\n std::fs::write(\n config_root.join(\"opencode.env\"),\n \"OPENCODE_API_KEY=file-secret\\n\",\n )\n .expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"OPENCODE_API_KEY\", \"opencode.env\").as_deref(),\n Some(\"file-secret\")\n );\n}\n\n#[test]\nfn load_api_key_accepts_legacy_zai_key_name() {\n let _lock = crate::storage::lock_test_env();\n let temp = tempfile::tempdir().expect(\"tempdir\");\n let config_root = temp.path().join(\"config\").join(\"jcode\");\n std::fs::create_dir_all(&config_root).expect(\"config dir\");\n\n let _guard = EnvGuard::save(&[\"JCODE_HOME\", \"ZHIPU_API_KEY\", \"ZAI_API_KEY\"]);\n crate::env::set_var(\"JCODE_HOME\", temp.path());\n crate::env::remove_var(\"ZHIPU_API_KEY\");\n crate::env::remove_var(\"ZAI_API_KEY\");\n std::fs::write(config_root.join(\"zai.env\"), \"ZAI_API_KEY=legacy-secret\\n\").expect(\"env file\");\n\n assert_eq!(\n load_api_key_from_env_or_config(\"ZHIPU_API_KEY\", \"zai.env\").as_deref(),\n Some(\"legacy-secret\")\n );\n}\n"}}}}