fix(kimi): support API-key setup for Kimi Code
This commit is contained in:
+9
-3
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Kimi Code API-key setup.** `codewhale config set providers.moonshot.*`
|
||||
now writes the Moonshot/Kimi provider table, and Kimi Code API-key
|
||||
endpoints default to `kimi-for-coding` without using the Kimi CLI OAuth path.
|
||||
|
||||
## [0.8.45] - 2026-05-25
|
||||
|
||||
### Added
|
||||
@@ -17,9 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- **Command palette voice input.** The command palette can launch a configured
|
||||
speech-to-text helper and show footer status while transcription runs
|
||||
(#2047).
|
||||
- **Moonshot/Kimi OAuth provider.** Moonshot/Kimi is now a first-class
|
||||
provider, including Kimi CLI OAuth reuse, secure refresh writes, model
|
||||
completion, CLI auth, and secret-store integration.
|
||||
- **Moonshot/Kimi provider.** Moonshot/Kimi is now a first-class provider,
|
||||
including API-key auth, model completion, CLI auth, secret-store
|
||||
integration, and optional Kimi CLI credential reuse.
|
||||
- **Deterministic whale-species sub-agent names.** Sub-agents now get stable,
|
||||
human-readable whale-species nicknames (e.g. "Beluga", "Orca") while
|
||||
preserving the raw agent ID in the popup (#2035, #2016).
|
||||
|
||||
@@ -314,15 +314,23 @@ codewhale --provider novita --model deepseek/deepseek-v4-pro
|
||||
codewhale auth set --provider fireworks --api-key "YOUR_FIREWORKS_API_KEY"
|
||||
codewhale --provider fireworks --model deepseek-v4-pro
|
||||
|
||||
# Moonshot/Kimi
|
||||
codewhale auth set --provider moonshot --api-key "YOUR_MOONSHOT_OR_KIMI_API_KEY"
|
||||
codewhale --provider moonshot --model kimi-k2.6
|
||||
# Kimi Code plan API key
|
||||
codewhale auth set --provider moonshot --api-key "YOUR_KIMI_CODE_API_KEY"
|
||||
codewhale config set providers.moonshot.auth_mode api_key
|
||||
codewhale config set providers.moonshot.base_url https://api.kimi.com/coding/v1
|
||||
codewhale config set providers.moonshot.model kimi-for-coding
|
||||
codewhale --provider moonshot
|
||||
|
||||
# Moonshot/Kimi with Kimi CLI OAuth
|
||||
kimi login
|
||||
mkdir -p ~/.deepseek
|
||||
printf 'provider = "moonshot"\n\n[providers.moonshot]\nauth_mode = "kimi_oauth"\n' >> ~/.deepseek/config.toml
|
||||
codewhale --provider moonshot --model kimi-for-coding
|
||||
# Kimi/Moonshot Platform API key
|
||||
codewhale auth set --provider moonshot --api-key "YOUR_MOONSHOT_OR_KIMI_API_KEY"
|
||||
codewhale config set providers.moonshot.auth_mode api_key
|
||||
codewhale config set providers.moonshot.base_url https://api.moonshot.ai/v1
|
||||
codewhale config set providers.moonshot.model kimi-k2.6
|
||||
codewhale --provider moonshot
|
||||
|
||||
# Kimi through OpenRouter's catalog
|
||||
codewhale auth set --provider openrouter --api-key "YOUR_OPENROUTER_API_KEY"
|
||||
codewhale --provider openrouter --model moonshotai/kimi-k2.6
|
||||
|
||||
# Self-hosted SGLang
|
||||
SGLANG_BASE_URL="http://localhost:30000/v1" codewhale --provider sglang --model deepseek-v4-flash
|
||||
@@ -512,7 +520,7 @@ Key environment variables:
|
||||
| `OPENAI_BASE_URL` / `OPENAI_MODEL` | Generic OpenAI-compatible endpoint and model ID |
|
||||
| `ATLASCLOUD_BASE_URL` / `ATLASCLOUD_MODEL` | AtlasCloud endpoint and model override |
|
||||
| `WANJIE_ARK_BASE_URL` / `WANJIE_BASE_URL` / `WANJIE_MAAS_BASE_URL` / `WANJIE_ARK_MODEL` / `WANJIE_MODEL` / `WANJIE_MAAS_MODEL` | Wanjie Ark endpoint and model override |
|
||||
| `MOONSHOT_BASE_URL` / `KIMI_BASE_URL` / `MOONSHOT_MODEL` / `KIMI_MODEL_NAME` / `KIMI_MODEL` | Moonshot/Kimi endpoint and model override |
|
||||
| `MOONSHOT_BASE_URL` / `KIMI_BASE_URL` / `MOONSHOT_MODEL` / `KIMI_MODEL_NAME` / `KIMI_MODEL` | Moonshot/Kimi endpoint and model override. For a Kimi Code plan API key, use `KIMI_BASE_URL=https://api.kimi.com/coding/v1` and `KIMI_MODEL=kimi-for-coding`. |
|
||||
| `OPENROUTER_BASE_URL` | OpenRouter endpoint override |
|
||||
| `NOVITA_BASE_URL` | Novita endpoint override |
|
||||
| `FIREWORKS_BASE_URL` | Fireworks endpoint override |
|
||||
|
||||
+121
-1
@@ -462,6 +462,13 @@ impl ConfigToml {
|
||||
"providers.fireworks.http_headers" => {
|
||||
serialize_http_headers(&self.providers.fireworks.http_headers)
|
||||
}
|
||||
"providers.moonshot.api_key" => self.providers.moonshot.api_key.clone(),
|
||||
"providers.moonshot.base_url" => self.providers.moonshot.base_url.clone(),
|
||||
"providers.moonshot.model" => self.providers.moonshot.model.clone(),
|
||||
"providers.moonshot.auth_mode" => self.providers.moonshot.auth_mode.clone(),
|
||||
"providers.moonshot.http_headers" => {
|
||||
serialize_http_headers(&self.providers.moonshot.http_headers)
|
||||
}
|
||||
"providers.sglang.api_key" => self.providers.sglang.api_key.clone(),
|
||||
"providers.sglang.base_url" => self.providers.sglang.base_url.clone(),
|
||||
"providers.sglang.model" => self.providers.sglang.model.clone(),
|
||||
@@ -612,6 +619,21 @@ impl ConfigToml {
|
||||
"providers.fireworks.http_headers" => {
|
||||
self.providers.fireworks.http_headers = parse_http_headers(value)?;
|
||||
}
|
||||
"providers.moonshot.api_key" => {
|
||||
self.providers.moonshot.api_key = Some(value.to_string());
|
||||
}
|
||||
"providers.moonshot.base_url" => {
|
||||
self.providers.moonshot.base_url = Some(value.to_string());
|
||||
}
|
||||
"providers.moonshot.model" => {
|
||||
self.providers.moonshot.model = Some(value.to_string());
|
||||
}
|
||||
"providers.moonshot.auth_mode" => {
|
||||
self.providers.moonshot.auth_mode = Some(value.to_string());
|
||||
}
|
||||
"providers.moonshot.http_headers" => {
|
||||
self.providers.moonshot.http_headers = parse_http_headers(value)?;
|
||||
}
|
||||
"providers.sglang.api_key" => {
|
||||
self.providers.sglang.api_key = Some(value.to_string());
|
||||
}
|
||||
@@ -716,6 +738,11 @@ impl ConfigToml {
|
||||
"providers.fireworks.base_url" => self.providers.fireworks.base_url = None,
|
||||
"providers.fireworks.model" => self.providers.fireworks.model = None,
|
||||
"providers.fireworks.http_headers" => self.providers.fireworks.http_headers.clear(),
|
||||
"providers.moonshot.api_key" => self.providers.moonshot.api_key = None,
|
||||
"providers.moonshot.base_url" => self.providers.moonshot.base_url = None,
|
||||
"providers.moonshot.model" => self.providers.moonshot.model = None,
|
||||
"providers.moonshot.auth_mode" => self.providers.moonshot.auth_mode = None,
|
||||
"providers.moonshot.http_headers" => self.providers.moonshot.http_headers.clear(),
|
||||
"providers.sglang.api_key" => self.providers.sglang.api_key = None,
|
||||
"providers.sglang.base_url" => self.providers.sglang.base_url = None,
|
||||
"providers.sglang.model" => self.providers.sglang.model = None,
|
||||
@@ -869,6 +896,21 @@ impl ConfigToml {
|
||||
if let Some(v) = serialize_http_headers(&self.providers.fireworks.http_headers) {
|
||||
out.insert("providers.fireworks.http_headers".to_string(), v);
|
||||
}
|
||||
if let Some(v) = self.providers.moonshot.api_key.as_ref() {
|
||||
out.insert("providers.moonshot.api_key".to_string(), redact_secret(v));
|
||||
}
|
||||
if let Some(v) = self.providers.moonshot.base_url.as_ref() {
|
||||
out.insert("providers.moonshot.base_url".to_string(), v.clone());
|
||||
}
|
||||
if let Some(v) = self.providers.moonshot.model.as_ref() {
|
||||
out.insert("providers.moonshot.model".to_string(), v.clone());
|
||||
}
|
||||
if let Some(v) = self.providers.moonshot.auth_mode.as_ref() {
|
||||
out.insert("providers.moonshot.auth_mode".to_string(), v.clone());
|
||||
}
|
||||
if let Some(v) = serialize_http_headers(&self.providers.moonshot.http_headers) {
|
||||
out.insert("providers.moonshot.http_headers".to_string(), v);
|
||||
}
|
||||
if let Some(v) = self.providers.sglang.api_key.as_ref() {
|
||||
out.insert("providers.sglang.api_key".to_string(), redact_secret(v));
|
||||
}
|
||||
@@ -1028,7 +1070,8 @@ impl ConfigToml {
|
||||
.or_else(|| self.model.clone())
|
||||
.unwrap_or_else(|| {
|
||||
if provider == ProviderKind::Moonshot
|
||||
&& auth_mode.as_deref().is_some_and(auth_mode_uses_kimi_oauth)
|
||||
&& (auth_mode.as_deref().is_some_and(auth_mode_uses_kimi_oauth)
|
||||
|| moonshot_base_url_uses_kimi_code(&base_url))
|
||||
{
|
||||
DEFAULT_KIMI_CODE_MODEL.to_string()
|
||||
} else {
|
||||
@@ -1257,6 +1300,13 @@ fn default_base_url_for_provider(provider: ProviderKind) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
fn moonshot_base_url_uses_kimi_code(base_url: &str) -> bool {
|
||||
let normalized = base_url.trim_end_matches('/').to_ascii_lowercase();
|
||||
normalized == DEFAULT_KIMI_CODE_BASE_URL
|
||||
|| normalized == "https://api.kimi.com/coding"
|
||||
|| normalized.starts_with("https://api.kimi.com/coding/")
|
||||
}
|
||||
|
||||
fn base_url_is_custom_for_provider(provider: ProviderKind, base_url: &str) -> bool {
|
||||
let actual = base_url.trim_end_matches('/');
|
||||
let default = default_base_url_for_provider(provider).trim_end_matches('/');
|
||||
@@ -2358,6 +2408,52 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moonshot_provider_config_values_round_trip() -> Result<()> {
|
||||
let mut config = ConfigToml::default();
|
||||
|
||||
config.set_value("providers.moonshot.api_key", "moonshot-secret-value")?;
|
||||
config.set_value("providers.moonshot.base_url", DEFAULT_KIMI_CODE_BASE_URL)?;
|
||||
config.set_value("providers.moonshot.model", DEFAULT_KIMI_CODE_MODEL)?;
|
||||
config.set_value("providers.moonshot.auth_mode", "api_key")?;
|
||||
config.set_value("providers.moonshot.http_headers", "X-Test=ok")?;
|
||||
|
||||
assert_eq!(
|
||||
config
|
||||
.get_display_value("providers.moonshot.api_key")
|
||||
.as_deref(),
|
||||
Some("moon***alue")
|
||||
);
|
||||
assert_eq!(
|
||||
config.get_value("providers.moonshot.base_url").as_deref(),
|
||||
Some(DEFAULT_KIMI_CODE_BASE_URL)
|
||||
);
|
||||
assert_eq!(
|
||||
config.get_value("providers.moonshot.model").as_deref(),
|
||||
Some(DEFAULT_KIMI_CODE_MODEL)
|
||||
);
|
||||
assert_eq!(
|
||||
config.get_value("providers.moonshot.auth_mode").as_deref(),
|
||||
Some("api_key")
|
||||
);
|
||||
assert_eq!(
|
||||
config
|
||||
.list_values()
|
||||
.get("providers.moonshot.api_key")
|
||||
.map(String::as_str),
|
||||
Some("moon***alue")
|
||||
);
|
||||
|
||||
config.unset_value("providers.moonshot.auth_mode")?;
|
||||
config.unset_value("providers.moonshot.base_url")?;
|
||||
config.unset_value("providers.moonshot.model")?;
|
||||
|
||||
assert_eq!(config.get_value("providers.moonshot.auth_mode"), None);
|
||||
assert_eq!(config.get_value("providers.moonshot.base_url"), None);
|
||||
assert_eq!(config.get_value("providers.moonshot.model"), None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_merge_denies_credentials_endpoints_and_provider_selection() {
|
||||
let mut base = ConfigToml {
|
||||
@@ -2637,6 +2733,30 @@ mod tests {
|
||||
assert_eq!(resolved.api_key_source, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moonshot_kimi_code_api_key_endpoint_defaults_to_kimi_for_coding() {
|
||||
let _lock = env_lock();
|
||||
let _env = EnvGuard::without_deepseek_runtime_overrides();
|
||||
let mut config = ConfigToml {
|
||||
provider: ProviderKind::Moonshot,
|
||||
..ConfigToml::default()
|
||||
};
|
||||
config.providers.moonshot.api_key = Some("kimi-code-key".to_string());
|
||||
config.providers.moonshot.base_url = Some(DEFAULT_KIMI_CODE_BASE_URL.to_string());
|
||||
|
||||
let resolved = config.resolve_runtime_options(&CliRuntimeOverrides::default());
|
||||
|
||||
assert_eq!(resolved.provider, ProviderKind::Moonshot);
|
||||
assert_eq!(resolved.auth_mode, None);
|
||||
assert_eq!(resolved.base_url, DEFAULT_KIMI_CODE_BASE_URL);
|
||||
assert_eq!(resolved.model, DEFAULT_KIMI_CODE_MODEL);
|
||||
assert_eq!(resolved.api_key.as_deref(), Some("kimi-code-key"));
|
||||
assert_eq!(
|
||||
resolved.api_key_source,
|
||||
Some(RuntimeApiKeySource::ConfigFile)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wanjie_ark_provider_defaults_to_openai_compatible_endpoint_and_model() {
|
||||
let _lock = env_lock();
|
||||
|
||||
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Kimi Code API-key setup.** `codewhale config set providers.moonshot.*`
|
||||
now writes the Moonshot/Kimi provider table, and Kimi Code API-key
|
||||
endpoints default to `kimi-for-coding` without using the Kimi CLI OAuth path.
|
||||
|
||||
## [0.8.45] - 2026-05-25
|
||||
|
||||
### Added
|
||||
@@ -17,9 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- **Command palette voice input.** The command palette can launch a configured
|
||||
speech-to-text helper and show footer status while transcription runs
|
||||
(#2047).
|
||||
- **Moonshot/Kimi OAuth provider.** Moonshot/Kimi is now a first-class
|
||||
provider, including Kimi CLI OAuth reuse, secure refresh writes, model
|
||||
completion, CLI auth, and secret-store integration.
|
||||
- **Moonshot/Kimi provider.** Moonshot/Kimi is now a first-class provider,
|
||||
including API-key auth, model completion, CLI auth, secret-store
|
||||
integration, and optional Kimi CLI credential reuse.
|
||||
- **Deterministic whale-species sub-agent names.** Sub-agents now get stable,
|
||||
human-readable whale-species nicknames (e.g. "Beluga", "Orca") while
|
||||
preserving the raw agent ID in the popup (#2035, #2016).
|
||||
|
||||
@@ -1570,11 +1570,17 @@ impl Config {
|
||||
{
|
||||
return model_for_provider(provider, normalized);
|
||||
}
|
||||
if provider == ApiProvider::Moonshot
|
||||
&& self
|
||||
.provider_config()
|
||||
.is_some_and(provider_config_uses_kimi_oauth)
|
||||
{
|
||||
let moonshot_config = (provider == ApiProvider::Moonshot)
|
||||
.then(|| self.provider_config())
|
||||
.flatten();
|
||||
let moonshot_uses_kimi_code = moonshot_config.is_some_and(|config| {
|
||||
provider_config_uses_kimi_oauth(config)
|
||||
|| config
|
||||
.base_url
|
||||
.as_deref()
|
||||
.is_some_and(moonshot_base_url_uses_kimi_code)
|
||||
});
|
||||
if moonshot_uses_kimi_code {
|
||||
return DEFAULT_KIMI_CODE_MODEL.to_string();
|
||||
}
|
||||
|
||||
@@ -1771,8 +1777,9 @@ impl Config {
|
||||
),
|
||||
ApiProvider::Moonshot => anyhow::bail!(
|
||||
"Moonshot/Kimi API key not found. Run 'codewhale auth set --provider moonshot', \
|
||||
set MOONSHOT_API_KEY/KIMI_API_KEY, add [providers.moonshot] api_key, \
|
||||
or run `kimi login` and set [providers.moonshot] auth_mode = \"kimi_oauth\"."
|
||||
set MOONSHOT_API_KEY/KIMI_API_KEY, or add [providers.moonshot] api_key. \
|
||||
For a Kimi Code plan key, set [providers.moonshot] base_url = \
|
||||
\"https://api.kimi.com/coding/v1\" and model = \"kimi-for-coding\"."
|
||||
),
|
||||
// Self-hosted deployments commonly run without auth on localhost.
|
||||
// Return an empty key and let the client omit the Authorization header.
|
||||
@@ -2880,6 +2887,13 @@ fn provider_preserves_custom_base_url_model(provider: ApiProvider, base_url: &st
|
||||
base_url_is_custom_for_provider(provider, base_url)
|
||||
}
|
||||
|
||||
fn moonshot_base_url_uses_kimi_code(base_url: &str) -> bool {
|
||||
let normalized = normalize_base_url(base_url).to_ascii_lowercase();
|
||||
normalized == DEFAULT_KIMI_CODE_BASE_URL
|
||||
|| normalized == "https://api.kimi.com/coding"
|
||||
|| normalized.starts_with("https://api.kimi.com/coding/")
|
||||
}
|
||||
|
||||
fn provider_config_uses_kimi_oauth(config: &ProviderConfig) -> bool {
|
||||
config
|
||||
.auth_mode
|
||||
@@ -6434,6 +6448,42 @@ api_key = "stale-api-key"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moonshot_kimi_code_api_key_uses_coding_model() -> Result<()> {
|
||||
let _lock = lock_test_env();
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"codewhale-tui-kimi-code-key-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
fs::create_dir_all(&temp_root)?;
|
||||
let _guard = EnvGuard::new(&temp_root);
|
||||
|
||||
let config_path = temp_root.join(".deepseek").join("config.toml");
|
||||
ensure_parent_dir(&config_path)?;
|
||||
fs::write(
|
||||
&config_path,
|
||||
r#"provider = "moonshot"
|
||||
|
||||
[providers.moonshot]
|
||||
api_key = "kimi-code-key"
|
||||
base_url = "https://api.kimi.com/coding/v1"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = Config::load(None, None)?;
|
||||
assert_eq!(config.api_provider(), ApiProvider::Moonshot);
|
||||
assert_eq!(config.deepseek_base_url(), DEFAULT_KIMI_CODE_BASE_URL);
|
||||
assert_eq!(config.default_model(), DEFAULT_KIMI_CODE_MODEL);
|
||||
assert_eq!(config.deepseek_api_key()?, "kimi-code-key");
|
||||
assert!(has_api_key_for(&config, ApiProvider::Moonshot));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_api_key_for_detects_env_and_config_per_provider() -> Result<()> {
|
||||
let _lock = lock_test_env();
|
||||
|
||||
@@ -267,6 +267,11 @@ command = "~/.deepseek/hooks/pre.sh" # / message_submit / mode_change /
|
||||
<p className="mt-5 text-ink-soft leading-[1.9] tracking-wide">
|
||||
开放模型平台方向:CodeWhale 保持 DeepSeek 优先,同时内置 Moonshot/Kimi、OpenRouter、NVIDIA NIM、
|
||||
AtlasCloud、Wanjie Ark、Novita、Fireworks 和自托管 SGLang/vLLM/Ollama 路径。
|
||||
Kimi Code 会员 API Key 使用 <code className="inline">providers.moonshot.base_url</code>
|
||||
指向 <code className="inline">https://api.kimi.com/coding/v1</code>,模型为
|
||||
<code className="inline">kimi-for-coding</code>;Kimi/Moonshot 平台 API Key 继续使用
|
||||
<code className="inline">https://api.moonshot.ai/v1</code> 和
|
||||
<code className="inline">kimi-k2.6</code>。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -515,6 +520,11 @@ command = "~/.deepseek/hooks/pre.sh" # / message_submit / mode_change /
|
||||
Open-model platform direction: CodeWhale stays DeepSeek-first while shipping Moonshot/Kimi,
|
||||
OpenRouter, NVIDIA NIM, AtlasCloud, Wanjie Ark, Novita, Fireworks, and self-hosted
|
||||
SGLang/vLLM/Ollama paths.
|
||||
Kimi Code membership API keys use <code className="inline">providers.moonshot.base_url</code>
|
||||
set to <code className="inline">https://api.kimi.com/coding/v1</code> with
|
||||
<code className="inline">kimi-for-coding</code>; Kimi/Moonshot Platform API keys use
|
||||
<code className="inline">https://api.moonshot.ai/v1</code> with
|
||||
<code className="inline">kimi-k2.6</code>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ codewhale doctor # full connectivity check`}
|
||||
<p className="mb-2">CodeWhale ships with these built-in providers:</p>
|
||||
<ul className="list-disc pl-5 space-y-1 text-sm text-ink-soft mb-3">
|
||||
<li><strong>DeepSeek</strong> — first-class, native API. Reasoning streaming, cache metrics, thinking effort control.</li>
|
||||
<li><strong>Moonshot/Kimi</strong> — Kimi API key mode or local Kimi CLI OAuth reuse.</li>
|
||||
<li><strong>Moonshot/Kimi</strong> — Kimi Code and Kimi/Moonshot Platform API-key modes.</li>
|
||||
<li><strong>OpenRouter</strong> — unified API for DeepSeek models and more.</li>
|
||||
<li><strong>OpenAI-compatible</strong>, <strong>NVIDIA NIM</strong>, <strong>AtlasCloud</strong>, <strong>Wanjie Ark</strong>, <strong>Novita</strong>, <strong>Fireworks</strong>, <strong>SGLang</strong>, <strong>vLLM</strong>, <strong>Ollama</strong></li>
|
||||
</ul>
|
||||
@@ -425,7 +425,7 @@ codewhale doctor # 完整连接检查`}
|
||||
<p className="mb-2">CodeWhale 内建以下提供商:</p>
|
||||
<ul className="list-disc pl-5 space-y-1 text-sm text-ink-soft mb-3">
|
||||
<li><strong>DeepSeek</strong> — 一级支持,原生 API。推理流、缓存指标、思考力度控制。</li>
|
||||
<li><strong>Moonshot/Kimi</strong> — Kimi API key 模式或复用本地 Kimi CLI OAuth。</li>
|
||||
<li><strong>Moonshot/Kimi</strong> — Kimi Code 与 Kimi/Moonshot 平台 API key 模式。</li>
|
||||
<li><strong>OpenRouter</strong> — 统一 API,可访问 DeepSeek 等模型。</li>
|
||||
<li><strong>OpenAI 兼容</strong>、<strong>NVIDIA NIM</strong>、<strong>AtlasCloud</strong>、<strong>Wanjie Ark</strong>、<strong>Novita</strong>、<strong>Fireworks</strong>、<strong>SGLang</strong>、<strong>vLLM</strong>、<strong>Ollama</strong></li>
|
||||
</ul>
|
||||
|
||||
@@ -31,7 +31,7 @@ const tracksEn = [
|
||||
{ title: "Bidirectional MCP", note: "Consume tools from external servers; expose as server via `codewhale mcp`; ~/.deepseek/mcp.json" },
|
||||
{ title: "Skills + unified slash palette", note: "~/.deepseek/skills/ auto-loading; /help, /mode, /status, /config, /trust, /feedback" },
|
||||
{ title: "v0.8.45 provider surface", note: "DeepSeek, NVIDIA NIM, OpenAI-compatible, AtlasCloud, Wanjie Ark, OpenRouter, Novita, Fireworks, Moonshot/Kimi, SGLang, vLLM, and Ollama" },
|
||||
{ title: "Moonshot/Kimi OAuth", note: "Kimi CLI OAuth reuse plus API-key mode for Moonshot/Kimi sessions" },
|
||||
{ title: "Moonshot/Kimi API-key setup", note: "Kimi Code plan and Kimi/Moonshot Platform API-key paths for Moonshot/Kimi sessions" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -97,7 +97,7 @@ const tracksZh = [
|
||||
{ title: "双向 MCP 协议", note: "消费外部服务器工具;通过 `codewhale mcp` 暴露为服务器;~/.deepseek/mcp.json" },
|
||||
{ title: "技能 + 统一命令面板", note: "~/.deepseek/skills/ 自动加载;/help、/mode、/status、/config、/trust、/feedback" },
|
||||
{ title: "v0.8.45 提供商表面", note: "DeepSeek、NVIDIA NIM、OpenAI 兼容、AtlasCloud、Wanjie Ark、OpenRouter、Novita、Fireworks、Moonshot/Kimi、SGLang、vLLM、Ollama" },
|
||||
{ title: "Moonshot/Kimi OAuth", note: "复用 Kimi CLI OAuth,也支持 Moonshot/Kimi API key 模式" },
|
||||
{ title: "Moonshot/Kimi API-key 设置", note: "Kimi Code 会员与 Kimi/Moonshot 平台 API key 路径" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,6 +11,10 @@ interface KVNamespace {
|
||||
}
|
||||
|
||||
async function getKv(): Promise<KVNamespace | undefined> {
|
||||
if (process.env.NEXT_PHASE === "phase-production-build") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const mod = await import("@opennextjs/cloudflare");
|
||||
const ctx = await mod.getCloudflareContext({ async: true });
|
||||
|
||||
+14
-6
@@ -23,12 +23,7 @@ interface CloudflareEnv {
|
||||
GITHUB_REPO?: string;
|
||||
}
|
||||
|
||||
export async function getEnv(): Promise<CloudflareEnv> {
|
||||
try {
|
||||
const mod = await import("@opennextjs/cloudflare");
|
||||
const ctx = await mod.getCloudflareContext({ async: true });
|
||||
return ctx.env as CloudflareEnv;
|
||||
} catch {
|
||||
function envFromProcess(): CloudflareEnv {
|
||||
return {
|
||||
DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY,
|
||||
DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL,
|
||||
@@ -38,6 +33,19 @@ export async function getEnv(): Promise<CloudflareEnv> {
|
||||
GITHUB_REPO: process.env.GITHUB_REPO,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getEnv(): Promise<CloudflareEnv> {
|
||||
if (process.env.NEXT_PHASE === "phase-production-build") {
|
||||
return envFromProcess();
|
||||
}
|
||||
|
||||
try {
|
||||
const mod = await import("@opennextjs/cloudflare");
|
||||
const ctx = await mod.getCloudflareContext({ async: true });
|
||||
return ctx.env as CloudflareEnv;
|
||||
} catch {
|
||||
return envFromProcess();
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDispatch(): Promise<CuratedDispatch | null> {
|
||||
|
||||
@@ -58,7 +58,7 @@ interface GhIssue { number: number; title: string; html_url: string; body: strin
|
||||
const FALLBACK_SHIPPED: RoadmapItem[] = [
|
||||
{
|
||||
title: "v0.8.45",
|
||||
note: "Moonshot/Kimi OAuth, provider-surface sync, and current Windows install/runtime guidance",
|
||||
note: "Moonshot/Kimi provider support, API-key setup guidance, provider-surface sync, and current Windows install/runtime guidance",
|
||||
href: "https://github.com/Hmbown/CodeWhale/releases/tag/v0.8.45",
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user