From ab38635f782c48104635d018a50a163a38065efb Mon Sep 17 00:00:00 2001
From: Hunter Bown
Date: Mon, 25 May 2026 23:39:34 -0500
Subject: [PATCH] fix(kimi): support API-key setup for Kimi Code
---
CHANGELOG.md | 12 ++-
README.md | 26 ++++---
crates/config/src/lib.rs | 122 +++++++++++++++++++++++++++++-
crates/tui/CHANGELOG.md | 12 ++-
crates/tui/src/config.rs | 64 ++++++++++++++--
web/app/[locale]/docs/page.tsx | 10 +++
web/app/[locale]/faq/page.tsx | 4 +-
web/app/[locale]/roadmap/page.tsx | 4 +-
web/lib/facts.ts | 4 +
web/lib/kv.ts | 24 ++++--
web/lib/roadmap-feed.ts | 2 +-
11 files changed, 248 insertions(+), 36 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f21e957..2d0f05ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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).
diff --git a/README.md b/README.md
index d1dd66a1..450ca64d 100644
--- a/README.md
+++ b/README.md
@@ -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 |
diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs
index 576de517..d9d72864 100644
--- a/crates/config/src/lib.rs
+++ b/crates/config/src/lib.rs
@@ -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();
diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md
index 7f21e957..2d0f05ae 100644
--- a/crates/tui/CHANGELOG.md
+++ b/crates/tui/CHANGELOG.md
@@ -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).
diff --git a/crates/tui/src/config.rs b/crates/tui/src/config.rs
index ce15b29e..78975ee3 100644
--- a/crates/tui/src/config.rs
+++ b/crates/tui/src/config.rs
@@ -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();
diff --git a/web/app/[locale]/docs/page.tsx b/web/app/[locale]/docs/page.tsx
index 78be0f71..5a639add 100644
--- a/web/app/[locale]/docs/page.tsx
+++ b/web/app/[locale]/docs/page.tsx
@@ -267,6 +267,11 @@ command = "~/.deepseek/hooks/pre.sh" # / message_submit / mode_change /
开放模型平台方向:CodeWhale 保持 DeepSeek 优先,同时内置 Moonshot/Kimi、OpenRouter、NVIDIA NIM、
AtlasCloud、Wanjie Ark、Novita、Fireworks 和自托管 SGLang/vLLM/Ollama 路径。
+ Kimi Code 会员 API Key 使用 providers.moonshot.base_url
+ 指向 https://api.kimi.com/coding/v1,模型为
+ kimi-for-coding;Kimi/Moonshot 平台 API Key 继续使用
+ https://api.moonshot.ai/v1 和
+ kimi-k2.6。
@@ -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 providers.moonshot.base_url
+ set to https://api.kimi.com/coding/v1 with
+ kimi-for-coding; Kimi/Moonshot Platform API keys use
+ https://api.moonshot.ai/v1 with
+ kimi-k2.6.
diff --git a/web/app/[locale]/faq/page.tsx b/web/app/[locale]/faq/page.tsx
index ec5f7dcd..2d9db748 100644
--- a/web/app/[locale]/faq/page.tsx
+++ b/web/app/[locale]/faq/page.tsx
@@ -112,7 +112,7 @@ codewhale doctor # full connectivity check`}
CodeWhale ships with these built-in providers:
- DeepSeek — first-class, native API. Reasoning streaming, cache metrics, thinking effort control.
- - Moonshot/Kimi — Kimi API key mode or local Kimi CLI OAuth reuse.
+ - Moonshot/Kimi — Kimi Code and Kimi/Moonshot Platform API-key modes.
- OpenRouter — unified API for DeepSeek models and more.
- OpenAI-compatible, NVIDIA NIM, AtlasCloud, Wanjie Ark, Novita, Fireworks, SGLang, vLLM, Ollama
@@ -425,7 +425,7 @@ codewhale doctor # 完整连接检查`}
CodeWhale 内建以下提供商:
- DeepSeek — 一级支持,原生 API。推理流、缓存指标、思考力度控制。
- - Moonshot/Kimi — Kimi API key 模式或复用本地 Kimi CLI OAuth。
+ - Moonshot/Kimi — Kimi Code 与 Kimi/Moonshot 平台 API key 模式。
- OpenRouter — 统一 API,可访问 DeepSeek 等模型。
- OpenAI 兼容、NVIDIA NIM、AtlasCloud、Wanjie Ark、Novita、Fireworks、SGLang、vLLM、Ollama
diff --git a/web/app/[locale]/roadmap/page.tsx b/web/app/[locale]/roadmap/page.tsx
index 787ff0c6..3bcc5a46 100644
--- a/web/app/[locale]/roadmap/page.tsx
+++ b/web/app/[locale]/roadmap/page.tsx
@@ -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 路径" },
],
},
{
diff --git a/web/lib/facts.ts b/web/lib/facts.ts
index 25ec174b..6e7514ce 100644
--- a/web/lib/facts.ts
+++ b/web/lib/facts.ts
@@ -11,6 +11,10 @@ interface KVNamespace {
}
async function getKv(): Promise {
+ if (process.env.NEXT_PHASE === "phase-production-build") {
+ return undefined;
+ }
+
try {
const mod = await import("@opennextjs/cloudflare");
const ctx = await mod.getCloudflareContext({ async: true });
diff --git a/web/lib/kv.ts b/web/lib/kv.ts
index c6048b3a..f3a4bab6 100644
--- a/web/lib/kv.ts
+++ b/web/lib/kv.ts
@@ -23,20 +23,28 @@ interface CloudflareEnv {
GITHUB_REPO?: string;
}
+function envFromProcess(): CloudflareEnv {
+ return {
+ DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY,
+ DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL,
+ DEEPSEEK_MODEL: process.env.DEEPSEEK_MODEL,
+ GITHUB_TOKEN: process.env.GITHUB_TOKEN,
+ CRON_SECRET: process.env.CRON_SECRET,
+ GITHUB_REPO: process.env.GITHUB_REPO,
+ };
+}
+
export async function getEnv(): Promise {
+ 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 {
- DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY,
- DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL,
- DEEPSEEK_MODEL: process.env.DEEPSEEK_MODEL,
- GITHUB_TOKEN: process.env.GITHUB_TOKEN,
- CRON_SECRET: process.env.CRON_SECRET,
- GITHUB_REPO: process.env.GITHUB_REPO,
- };
+ return envFromProcess();
}
}
diff --git a/web/lib/roadmap-feed.ts b/web/lib/roadmap-feed.ts
index 48edda44..4c1e4606 100644
--- a/web/lib/roadmap-feed.ts
+++ b/web/lib/roadmap-feed.ts
@@ -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",
},
];