From af1f99e85d67585ca83cea558420afca8623b5e1 Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Mon, 25 May 2026 22:06:39 -0500 Subject: [PATCH 1/6] docs(changelog): add missing v0.8.45 entries --- CHANGELOG.md | 10 ++++++++++ crates/tui/CHANGELOG.md | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47c20bd9..7f21e957 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **RLM session objects.** `rlm_open` can now load `session://` refs, exposing the active prompt, history, and session data as symbolic objects inside RLM REPLs (#2047). +- **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. - **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). @@ -52,6 +58,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Thanks @reidliu41 (#2143). - **Model picker selection survives Esc.** Dismissing the model picker with Esc no longer loses the highlighted selection. Thanks @reidliu41 (#2056). +- **Moonshot/Kimi sessions launch from the dispatcher.** The `codewhale` + wrapper now includes Moonshot/Kimi in the TUI provider allowlist, so + `codewhale --provider moonshot --model kimi-k2.6` reaches the TUI instead of + stopping after config resolution. - **Slash recovery no longer restores command tails in the composer.** Resuming a session or recovering from a crash no longer leaves stale slash-command text (e.g. `/sessions`) in the composer input (#2047, #2032). diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 47c20bd9..7f21e957 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -14,6 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **RLM session objects.** `rlm_open` can now load `session://` refs, exposing the active prompt, history, and session data as symbolic objects inside RLM REPLs (#2047). +- **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. - **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). @@ -52,6 +58,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Thanks @reidliu41 (#2143). - **Model picker selection survives Esc.** Dismissing the model picker with Esc no longer loses the highlighted selection. Thanks @reidliu41 (#2056). +- **Moonshot/Kimi sessions launch from the dispatcher.** The `codewhale` + wrapper now includes Moonshot/Kimi in the TUI provider allowlist, so + `codewhale --provider moonshot --model kimi-k2.6` reaches the TUI instead of + stopping after config resolution. - **Slash recovery no longer restores command tails in the composer.** Resuming a session or recovering from a crash no longer leaves stale slash-command text (e.g. `/sessions`) in the composer input (#2047, #2032). From 05e4b08335c87a27e20dc02b0ece4c799a80cbb9 Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Mon, 25 May 2026 22:06:44 -0500 Subject: [PATCH 2/6] fix(cli): allow Moonshot Kimi TUI delegation --- crates/cli/src/lib.rs | 82 ++++++++++++++++++++++++++++++++++----- crates/secrets/src/lib.rs | 19 +++++++-- 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 2bd75181..18cc93d8 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -1453,12 +1453,13 @@ fn build_tui_command( | ProviderKind::Openrouter | ProviderKind::Novita | ProviderKind::Fireworks + | ProviderKind::Moonshot | ProviderKind::Sglang | ProviderKind::Vllm | ProviderKind::Ollama ) { bail!( - "The interactive TUI supports DeepSeek, NVIDIA NIM, OpenAI-compatible, AtlasCloud, Wanjie Ark, OpenRouter, Novita, Fireworks, SGLang, vLLM, and Ollama providers. Remove --provider {} or use `codewhale model ...` for provider registry inspection.", + "The interactive TUI supports DeepSeek, NVIDIA NIM, OpenAI-compatible, AtlasCloud, Wanjie Ark, OpenRouter, Novita, Fireworks, Moonshot/Kimi, SGLang, vLLM, and Ollama providers. Remove --provider {} or use `codewhale model ...` for provider registry inspection.", resolved_runtime.provider.as_str() ); } @@ -1480,14 +1481,10 @@ fn build_tui_command( } if let Some(api_key) = resolved_runtime.api_key.as_ref() { cmd.env("DEEPSEEK_API_KEY", api_key); - if resolved_runtime.provider == ProviderKind::Openai { - cmd.env("OPENAI_API_KEY", api_key); - } - if resolved_runtime.provider == ProviderKind::Atlascloud { - cmd.env("ATLASCLOUD_API_KEY", api_key); - } - if resolved_runtime.provider == ProviderKind::WanjieArk { - cmd.env("WANJIE_ARK_API_KEY", api_key); + for var in provider_env_vars(resolved_runtime.provider) { + if *var != "DEEPSEEK_API_KEY" { + cmd.env(var, api_key); + } } let source = resolved_runtime .api_key_source @@ -2668,6 +2665,73 @@ mod tests { ); } + #[test] + fn build_tui_command_allows_moonshot_and_forwards_kimi_key() { + let _lock = env_lock(); + let dir = tempfile::TempDir::new().expect("tempdir"); + let custom = dir + .path() + .join(format!("custom-tui{}", std::env::consts::EXE_SUFFIX)); + std::fs::write(&custom, b"").unwrap(); + let custom_str = custom.to_string_lossy().into_owned(); + let _bin = ScopedEnvVar::set("DEEPSEEK_TUI_BIN", &custom_str); + + let cli = parse_ok(&[ + "codewhale", + "--provider", + "moonshot", + "--model", + "kimi-k2.6", + "--workspace", + "/tmp/codewhale-workspace", + ]); + let resolved = ResolvedRuntimeOptions { + provider: ProviderKind::Moonshot, + model: "kimi-k2.6".to_string(), + api_key: Some("resolved-kimi-key".to_string()), + api_key_source: Some(RuntimeApiKeySource::Env), + base_url: "https://api.moonshot.ai/v1".to_string(), + auth_mode: Some("api_key".to_string()), + output_mode: None, + log_level: None, + telemetry: false, + approval_policy: None, + sandbox_mode: None, + yolo: None, + http_headers: std::collections::BTreeMap::new(), + }; + + let cmd = build_tui_command(&cli, &resolved, Vec::new()).expect("command"); + assert_eq!( + command_env(&cmd, "DEEPSEEK_PROVIDER").as_deref(), + Some("moonshot") + ); + assert_eq!( + command_env(&cmd, "DEEPSEEK_MODEL").as_deref(), + Some("kimi-k2.6") + ); + assert_eq!( + command_env(&cmd, "DEEPSEEK_BASE_URL").as_deref(), + Some("https://api.moonshot.ai/v1") + ); + assert_eq!( + command_env(&cmd, "DEEPSEEK_API_KEY").as_deref(), + Some("resolved-kimi-key") + ); + assert_eq!( + command_env(&cmd, "MOONSHOT_API_KEY").as_deref(), + Some("resolved-kimi-key") + ); + assert_eq!( + command_env(&cmd, "KIMI_API_KEY").as_deref(), + Some("resolved-kimi-key") + ); + assert_eq!( + command_env(&cmd, "DEEPSEEK_API_KEY_SOURCE").as_deref(), + Some("env") + ); + } + #[test] fn parses_top_level_prompt_flag_for_canonical_one_shot() { let cli = parse_ok(&["deepseek", "-p", "Reply with exactly OK."]); diff --git a/crates/secrets/src/lib.rs b/crates/secrets/src/lib.rs index 0254aa61..69c3fc9a 100644 --- a/crates/secrets/src/lib.rs +++ b/crates/secrets/src/lib.rs @@ -484,9 +484,7 @@ impl Secrets { /// Resolve a secret with `secret store → env → none` precedence. /// - /// `name` is the canonical provider name (`"deepseek"`, - /// `"openrouter"`, `"novita"`, `"nvidia"`/`"nvidia-nim"`, `"openai"`, - /// or `"atlascloud"`). + /// `name` is the canonical provider name or a supported provider alias. /// Empty strings on either layer are treated as "not set". #[must_use] pub fn resolve(&self, name: &str) -> Option { @@ -779,6 +777,21 @@ mod tests { unsafe { std::env::remove_var("FIREWORKS_API_KEY") }; } + #[test] + fn moonshot_kimi_env_aliases_resolve() { + let _lock = env_lock(); + clear_known_envs(); + // Safety: env mutation guarded by env_lock(). + unsafe { std::env::set_var("KIMI_API_KEY", "kimi-key") }; + + assert_eq!(env_for("moonshot").as_deref(), Some("kimi-key")); + assert_eq!(env_for("moonshot-ai").as_deref(), Some("kimi-key")); + assert_eq!(env_for("kimi").as_deref(), Some("kimi-key")); + assert_eq!(env_for("kimi-k2").as_deref(), Some("kimi-key")); + // Safety: env mutation guarded by env_lock(). + unsafe { std::env::remove_var("KIMI_API_KEY") }; + } + #[test] fn sglang_env_aliases_resolve() { let _lock = env_lock(); From 85e9a46ddfb8e507219906d6a64e1c981106114b Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Mon, 25 May 2026 22:06:51 -0500 Subject: [PATCH 3/6] docs(readme): refresh provider list and Windows surface --- README.md | 72 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 411cf0c9..d1dd66a1 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ It is built around DeepSeek V4 (`deepseek-v4-pro` / `deepseek-v4-flash`), includ - **Reasoning-effort tiers** — cycle through `off → high → max` with `Shift + Tab` - **Session save/resume/fork** — checkpoint long-running sessions and fork saved conversations into sibling paths with parent lineage shown in the picker - **Workspace rollback** — side-git pre/post-turn snapshots with `/restore` and `revert_turn`, without touching your repo's `.git` -- **OS-level sandbox** — Seatbelt on macOS, Landlock on Linux, Job Objects on Windows; shell commands run with workspace-scoped filesystem access only +- **Approval + platform sandbox controls** — Seatbelt on macOS and Landlock on Linux where available; Windows uses the same approval flow and terminal/runtime protections while OS-level filesystem isolation remains a tracked helper contract - **Durable task queue** — background tasks can survive restarts - **HTTP/SSE runtime API** — `codewhale serve --http` for headless agent workflows - **MCP protocol** — connect to Model Context Protocol servers for extended tooling; please see [docs/MCP.md](docs/MCP.md) @@ -210,12 +210,37 @@ codewhale --version Prebuilt binaries can also be downloaded from [GitHub Releases](https://github.com/Hmbown/CodeWhale/releases). Use `DEEPSEEK_TUI_RELEASE_BASE_URL` for mirrored release assets. -### Windows (Scoop) +### Windows -[Scoop](https://scoop.sh) is a Windows package manager. The `codewhale` package is listed -in Scoop's main bucket, but that manifest updates independently and can lag the -GitHub/npm/Cargo release. Run `scoop update` first, then verify the installed -version with `codewhale --version`: +Windows x64 is a first-class release target. Use npm or direct GitHub release +downloads when you need the newest v0.8.45 binary; Cargo also works when Rust +1.88+ and the MSVC toolchain are installed. + +```powershell +npm install -g codewhale +codewhale --version + +cargo install codewhale-cli --locked --force +cargo install codewhale-tui --locked --force +``` + +Current Windows terminal behavior: + +- interactive sessions always use the TUI-owned alternate screen; the old + `--no-alt-screen` flag is accepted for script compatibility but no longer + disables the interactive alternate screen +- runtime logs stay out of the alternate-screen buffer when `RUST_LOG` or + `DEEPSEEK_LOG_LEVEL` is enabled +- mouse capture defaults on in Windows Terminal, ConEmu, and Cmder, but stays + off in legacy console hosts and JetBrains terminals; use `--mouse-capture` or + `--no-mouse-capture` to override +- mouse-wheel-as-arrow terminals keep composer history usable by routing empty + composer Up/Down to transcript scrolling where appropriate + +[Scoop](https://scoop.sh) is also supported. The `deepseek-tui` package is +listed in Scoop's main bucket, but that manifest updates independently and can +lag the GitHub/npm/Cargo release. Run `scoop update` first, then verify the +installed version with `codewhale --version`: ```bash scoop update @@ -248,17 +273,27 @@ Both binaries are required. Cross-compilation and platform-specific notes: [docs -### Other API Providers +### Providers -Official DeepSeek remains the default and first-class path. Other providers are -additive, with OpenRouter starting from DeepSeek Pro/Flash before broader -open-model catalogs are enabled. +Official DeepSeek remains the default and first-class path. v0.8.45 supports +all 12 provider IDs in this order: `deepseek`, `nvidia-nim`, `openai`, +`atlascloud`, `wanjie-ark`, `openrouter`, `novita`, `fireworks`, `moonshot`, +`sglang`, `vllm`, and `ollama`. Other providers are additive, with OpenRouter +starting from DeepSeek Pro/Flash before broader open-model catalogs are enabled. ```bash +# DeepSeek (default) +codewhale auth set --provider deepseek --api-key "YOUR_DEEPSEEK_API_KEY" +codewhale --provider deepseek --model deepseek-v4-pro + # NVIDIA NIM codewhale auth set --provider nvidia-nim --api-key "YOUR_NVIDIA_API_KEY" codewhale --provider nvidia-nim +# Generic OpenAI-compatible endpoint +codewhale auth set --provider openai --api-key "YOUR_OPENAI_COMPATIBLE_API_KEY" +OPENAI_BASE_URL="https://openai-compatible.example/v4" codewhale --provider openai --model deepseek-v4-pro + # AtlasCloud codewhale auth set --provider atlascloud --api-key "YOUR_ATLASCLOUD_API_KEY" codewhale --provider atlascloud @@ -283,9 +318,11 @@ codewhale --provider fireworks --model deepseek-v4-pro codewhale auth set --provider moonshot --api-key "YOUR_MOONSHOT_OR_KIMI_API_KEY" codewhale --provider moonshot --model kimi-k2.6 -# Generic OpenAI-compatible endpoint -codewhale auth set --provider openai --api-key "YOUR_OPENAI_COMPATIBLE_API_KEY" -OPENAI_BASE_URL="https://openai-compatible.example/v4" codewhale --provider openai --model deepseek-v4-pro +# 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 # Self-hosted SGLang SGLANG_BASE_URL="http://localhost:30000/v1" codewhale --provider sglang --model deepseek-v4-flash @@ -466,15 +503,16 @@ Key environment variables: | `DEEPSEEK_HTTP_HEADERS` | Optional custom model request headers, e.g. `X-Model-Provider-Id=your-model-provider` | | `DEEPSEEK_MODEL` | Default model | | `DEEPSEEK_STREAM_IDLE_TIMEOUT_SECS` | Stream idle timeout in seconds, default `300`, clamped to `1..=3600` | -| `DEEPSEEK_PROVIDER` | `codewhale` (default), `nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `openrouter`, `novita`, `fireworks`, `moonshot`, `sglang`, `vllm`, `ollama` | +| `DEEPSEEK_PROVIDER` | `deepseek` (default), `nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `openrouter`, `novita`, `fireworks`, `moonshot`, `sglang`, `vllm`, `ollama` | | `DEEPSEEK_PROFILE` | Config profile name | | `DEEPSEEK_MEMORY` | Set to `on` to enable user memory | | `DEEPSEEK_ALLOW_INSECURE_HTTP=1` | Allow non-local `http://` API base URLs on trusted networks | -| `NVIDIA_API_KEY` / `OPENAI_API_KEY` / `ATLASCLOUD_API_KEY` / `WANJIE_ARK_API_KEY` / `OPENROUTER_API_KEY` / `NOVITA_API_KEY` / `FIREWORKS_API_KEY` / `MOONSHOT_API_KEY` / `KIMI_API_KEY` / `SGLANG_API_KEY` / `VLLM_API_KEY` / `OLLAMA_API_KEY` | Provider auth | +| `NVIDIA_API_KEY` / `NVIDIA_NIM_API_KEY` / `OPENAI_API_KEY` / `ATLASCLOUD_API_KEY` / `WANJIE_ARK_API_KEY` / `WANJIE_API_KEY` / `WANJIE_MAAS_API_KEY` / `OPENROUTER_API_KEY` / `NOVITA_API_KEY` / `FIREWORKS_API_KEY` / `MOONSHOT_API_KEY` / `KIMI_API_KEY` / `SGLANG_API_KEY` / `VLLM_API_KEY` / `OLLAMA_API_KEY` | Provider auth | +| `NVIDIA_NIM_BASE_URL` / `NIM_BASE_URL` / `NVIDIA_BASE_URL` | NVIDIA NIM endpoint override | | `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_ARK_MODEL` | Wanjie Ark endpoint and model override | -| `MOONSHOT_BASE_URL` / `KIMI_BASE_URL` / `MOONSHOT_MODEL` / `KIMI_MODEL` | Moonshot/Kimi 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 | | `OPENROUTER_BASE_URL` | OpenRouter endpoint override | | `NOVITA_BASE_URL` | Novita endpoint override | | `FIREWORKS_BASE_URL` | Fireworks endpoint override | From 64c870f35c7a6f56cbe273935eb55848e48801bb Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Mon, 25 May 2026 22:07:10 -0500 Subject: [PATCH 4/6] docs(web): sync v0.8.45 facts and provider surface --- web/app/[locale]/docs/page.tsx | 46 +++++++++++++++---------------- web/app/[locale]/faq/page.tsx | 22 ++++++++------- web/app/[locale]/page.tsx | 22 +++++++-------- web/app/[locale]/roadmap/page.tsx | 26 ++++++++--------- web/lib/facts-drift.ts | 14 ++++++---- web/lib/facts.generated.ts | 34 ++++++++++++++++------- web/lib/github.ts | 2 +- web/lib/roadmap-feed.ts | 27 +++++++++++++++--- web/scripts/derive-facts.mjs | 17 +++++++----- 9 files changed, 125 insertions(+), 85 deletions(-) diff --git a/web/app/[locale]/docs/page.tsx b/web/app/[locale]/docs/page.tsx index cfe384b5..78be0f71 100644 --- a/web/app/[locale]/docs/page.tsx +++ b/web/app/[locale]/docs/page.tsx @@ -121,7 +121,7 @@ export default async function DocsPage({ params }: { params: Promise<{ locale: s { group: "Git / 诊断 / 测试", tools: "git_status · git_diff · diagnostics · run_tests" }, { group: "子 Agent", tools: "agent_open · agent_eval · agent_close —— 持久会话,并行执行,通过 var_handle 读取大结果" }, { group: "递归 LM (RLM)", tools: "rlm_open · rlm_eval · rlm_configure · rlm_close —— 沙箱 Python REPL,内置 peek/search/chunk/sub_query_batch 等辅助函数" }, - { group: "MCP", tools: "mcp__——从 ~/.codewhale/mcp.json 自动注册" }, + { group: "MCP", tools: "mcp__——从 ~/.deepseek/mcp.json 自动注册" }, ].map((row) => (
{row.group}
@@ -152,8 +152,8 @@ export default async function DocsPage({ params }: { params: Promise<{ locale: s ))}

- 沙箱:{facts.sandboxBackends.join("、")}。工作区边界默认为 --workspace。 - /trust 可解除边界限制。 + 沙箱:{facts.sandboxBackends.join("、")}。Windows 当前不宣称 OS 级文件系统沙箱,但保留同样的审批、工作区边界和终端运行时保护。 + /trust 可解除工作区边界限制。

@@ -163,7 +163,7 @@ export default async function DocsPage({ params }: { params: Promise<{ locale: s 配置 Configuration
-{`# ~/.codewhale/config.toml
+{`# ~/.deepseek/config.toml
 api_key = "sk-..."
 base_url = "https://api.deepseek.com"
 default_text_model = "${facts.defaultModel ?? "deepseek-v4-pro"}"  # 默认模型;deepseek-v4-flash 用于快速 / 子智能体
@@ -179,7 +179,7 @@ default_timeout_secs = 30
 
 [[hooks.hooks]]
 event = "session_start"                     # 也支持: tool_call_before / tool_call_after
-command = "~/.codewhale/hooks/pre.sh"        # / message_submit / mode_change / on_error / shell_env`}
+command = "~/.deepseek/hooks/pre.sh"         # / message_submit / mode_change / on_error / shell_env`}
                 

完整参考:config.example.toml。 @@ -193,7 +193,7 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change /

codewhale 双向支持模型上下文协议(Model Context Protocol):作为客户端从 - ~/.codewhale/mcp.json 加载服务器,同时也可作为服务器暴露工具 + ~/.deepseek/mcp.json 加载服务器,同时也可作为服务器暴露工具 (codewhale mcp)。工具以 mcp_<server>_<tool> 形式呈现。

@@ -218,7 +218,7 @@ command = "~/.codewhale/hooks/pre.sh"        # / message_submit / mode_change /
                   技能 Skills
                 
                 

- 技能是 ~/.codewhale/skills/<name>/ 下的一个文件夹, + 技能是 ~/.deepseek/skills/<name>/ 下的一个文件夹, 根目录包含 SKILL.md。Agent 启动时加载技能名称和描述, 在需要时通过 Skill 工具拉取完整内容。

@@ -253,7 +253,7 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change /

使用 codewhale auth set --provider <id> 切换。下表为 crates/tui/src/config.rsApiProvider 枚举的实时投影 - ,目前共 {facts.providers.length} 个。 + ,v0.8.45 当前共 {facts.providers.length} 个。

{facts.providers.map((p) => ( @@ -265,9 +265,8 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change / ))}

- 开放模型平台方向:CodeWhale 正在扩展对 - OpenRouter Hugging Face 自托管 模型的支持, - 为您提供完全自主的模型选择——从云端 API 到本地部署均可覆盖。 + 开放模型平台方向:CodeWhale 保持 DeepSeek 优先,同时内置 Moonshot/Kimi、OpenRouter、NVIDIA NIM、 + AtlasCloud、Wanjie Ark、Novita、Fireworks 和自托管 SGLang/vLLM/Ollama 路径。

@@ -373,7 +372,7 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change / { group: "Git / diag / test", tools: "git_status · git_diff · diagnostics · run_tests" }, { group: "Sub-agents", tools: "agent_open · agent_eval · agent_close — persistent sessions, parallel execution, bounded result retrieval via var_handle" }, { group: "Recursive LM (RLM)", tools: "rlm_open · rlm_eval · rlm_configure · rlm_close — sandboxed Python REPL with peek/search/chunk/sub_query_batch helpers" }, - { group: "MCP", tools: "mcp__ — auto-registered from ~/.codewhale/mcp.json" }, + { group: "MCP", tools: "mcp__ — auto-registered from ~/.deepseek/mcp.json" }, ].map((row) => (
{row.group}
@@ -403,8 +402,9 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change / ))}

- Sandbox: {facts.sandboxBackends.join(", ")}. Workspace boundary defaults to{" "} - --workspace. /trust lifts the boundary. + Sandbox: {facts.sandboxBackends.join(", ")}. On Windows, CodeWhale does not advertise + OS-level filesystem isolation yet, but keeps the same approvals, workspace boundary, + and terminal runtime protections. /trust lifts the workspace boundary.

@@ -413,7 +413,7 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change / Configuration 配置
-{`# ~/.codewhale/config.toml
+{`# ~/.deepseek/config.toml
 api_key = "sk-..."
 base_url = "https://api.deepseek.com"
 default_text_model = "${facts.defaultModel ?? "deepseek-v4-pro"}"  # default; deepseek-v4-flash is the fast / sub-agent option
@@ -429,7 +429,7 @@ default_timeout_secs = 30
 
 [[hooks.hooks]]
 event = "session_start"                     # or: tool_call_before / tool_call_after
-command = "~/.codewhale/hooks/pre.sh"        # / message_submit / mode_change / on_error / shell_env`}
+command = "~/.deepseek/hooks/pre.sh"         # / message_submit / mode_change / on_error / shell_env`}
                 

Full reference: config.example.toml. @@ -442,7 +442,7 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change /

codewhale speaks the Model Context Protocol both ways: as a client (loads - servers from ~/.codewhale/mcp.json) and as a server + servers from ~/.deepseek/mcp.json) and as a server (codewhale mcp). Tools surface as mcp_<server>_<tool>.

@@ -466,7 +466,7 @@ command = "~/.codewhale/hooks/pre.sh"        # / message_submit / mode_change /
                   Skills 技能
                 
                 

- A skill is a folder under ~/.codewhale/skills/<name>/ + A skill is a folder under ~/.deepseek/skills/<name>/ with a SKILL.md at the root. The agent loads skill names + descriptions on startup and can pull in the full body via the Skill tool when relevant.

@@ -500,7 +500,7 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change /

Switch with codewhale auth set --provider <id>. The table below is a live projection of the ApiProvider enum - in crates/tui/src/config.rs — currently {facts.providers.length} providers. + in crates/tui/src/config.rs — v0.8.45 currently has {facts.providers.length} providers.

{facts.providers.map((p) => ( @@ -512,9 +512,9 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change / ))}

- Open-model platform direction: CodeWhale is expanding support for - OpenRouter, Hugging Face, and self-hosted models, - giving you full sovereignty over model choice — from cloud APIs to local deployments. + 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.

@@ -547,4 +547,4 @@ command = "~/.codewhale/hooks/pre.sh" # / message_submit / mode_change / )} ); -} \ No newline at end of file +} diff --git a/web/app/[locale]/faq/page.tsx b/web/app/[locale]/faq/page.tsx index f216552a..ec5f7dcd 100644 --- a/web/app/[locale]/faq/page.tsx +++ b/web/app/[locale]/faq/page.tsx @@ -23,7 +23,7 @@ const faqEn: FaqItem[] = [ q: "What is CodeWhale?", a: ( <> - CodeWhale is a terminal-native coding agent for open-source and open-weight models. It runs from the codewhale command, streams reasoning blocks, edits local workspaces with approval gates, and can auto-route each turn to the right model and thinking level. DeepSeek V4 is the first-class model path; OpenRouter is ready. Hugging Face, self-hosted, and other open-model surfaces are on the roadmap. + CodeWhale is a terminal-native coding agent for open-source and open-weight models. It runs from the codewhale command, streams reasoning blocks, edits local workspaces with approval gates, and can auto-route each turn to the right model and thinking level. DeepSeek V4 is the first-class model path; v0.8.45 also ships Moonshot/Kimi, OpenRouter, NVIDIA NIM, OpenAI-compatible, AtlasCloud, Wanjie Ark, Novita, Fireworks, SGLang, vLLM, and Ollama paths. ), sources: ["README.md", "docs/ARCHITECTURE.md"], @@ -112,12 +112,13 @@ 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.
  • OpenRouter — unified API for DeepSeek models and more.
  • -
  • OpenAI, NVIDIA NIM, Novita, Fireworks, sglang, vLLM, Ollama
  • +
  • OpenAI-compatible, NVIDIA NIM, AtlasCloud, Wanjie Ark, Novita, Fireworks, SGLang, vLLM, Ollama

Set the corresponding env var (e.g. OPENROUTER_API_KEY) and your provider in ~/.deepseek/config.toml. - Hugging Face, ZenMux, and self-hosted OpenAI-compatible endpoints are on the roadmap. + SGLang, vLLM, and Ollama can also run against self-hosted OpenAI-compatible endpoints.

), @@ -156,7 +157,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`} Yes. Use the vllm, sglang, or ollama providers with your local endpoint. For OpenAI-compatible endpoints (llama.cpp server, text-generation-webui, Aphrodite, etc.), you can use the openai provider with a custom base_url. CodeWhale also respects DEEPSEEK_ALLOW_INSECURE_HTTP=true for local HTTP endpoints. - Full Hugging Face TGI/vLLM integration is on the roadmap. + Direct Hugging Face TGI discovery remains roadmap work. ), sources: ["#574", "#1303", "docs/CONFIGURATION.md"], @@ -211,7 +212,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`} a: ( <> CodeWhale runs entirely on your machine. No telemetry, no cloud processing of your code. - Sandbox backends: seatbelt (macOS), landlock (Linux), restricted tokens (Windows). + Sandbox backends: seatbelt (macOS), landlock (Linux). Windows keeps the same approval and terminal runtime protections, but does not advertise OS-level filesystem isolation yet. Workspace boundaries default to --workspace. /trust lifts them. Approval mode is configurable per session. All credential/approval/elevation events are written to ~/.deepseek/audit.log. @@ -336,7 +337,7 @@ const faqZh: FaqItem[] = [ q: "CodeWhale 是什么?", a: ( <> - CodeWhale 是一个面向开源模型的终端原生编程智能体。通过 codewhale 命令启动,流式输出推理块,在有审批门槛的情况下编辑本地工作区,并可为每个回合自动选择最合适的模型和推理深度。DeepSeek V4 是一级模型路径;OpenRouter 已就绪。Hugging Face、自托管等开放模型接口已在路线图中。 + CodeWhale 是一个面向开源模型的终端原生编程智能体。通过 codewhale 命令启动,流式输出推理块,在有审批门槛的情况下编辑本地工作区,并可为每个回合自动选择最合适的模型和推理深度。DeepSeek V4 是一级模型路径;v0.8.45 也内置 Moonshot/Kimi、OpenRouter、NVIDIA NIM、OpenAI 兼容、AtlasCloud、Wanjie Ark、Novita、Fireworks、SGLang、vLLM 和 Ollama 路径。 ), sources: ["README.md", "docs/ARCHITECTURE.md"], @@ -424,12 +425,13 @@ codewhale doctor # 完整连接检查`}

CodeWhale 内建以下提供商:

  • DeepSeek — 一级支持,原生 API。推理流、缓存指标、思考力度控制。
  • +
  • Moonshot/Kimi — Kimi API key 模式或复用本地 Kimi CLI OAuth。
  • OpenRouter — 统一 API,可访问 DeepSeek 等模型。
  • -
  • OpenAINVIDIA NIMNovitaFireworkssglangvLLMOllama
  • +
  • OpenAI 兼容NVIDIA NIMAtlasCloudWanjie ArkNovitaFireworksSGLangvLLMOllama

设置对应的环境变量(如 OPENROUTER_API_KEY)并在 ~/.deepseek/config.toml 中配置你的提供商。 - Hugging Face、ZenMux 和自托管 OpenAI 兼容端点正在路线图中。 + SGLang、vLLM 和 Ollama 也可以连接自托管 OpenAI 兼容端点。

), @@ -468,7 +470,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`} 可以。使用 vllmsglangollama 提供商连接本地端点。 对于 OpenAI 兼容端点(llama.cpp server、text-generation-webui 等),可以使用 openai 提供商并设置自定义 base_url。 CodeWhale 也支持 DEEPSEEK_ALLOW_INSECURE_HTTP=true 用于本地 HTTP 端点。 - 完整的 Hugging Face TGI/vLLM 集成正在路线图中。 + Hugging Face TGI 的直接发现仍在路线图中。 ), sources: ["#574", "#1303", "docs/CONFIGURATION.md"], @@ -523,7 +525,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`} a: ( <> CodeWhale 完全在你的机器上运行。无遥测,不会将你的代码上传到云端处理。 - 沙箱后端:seatbelt(macOS)、landlock(Linux)、受限令牌(Windows)。 + 沙箱后端:seatbelt(macOS)、landlock(Linux)。Windows 保留同样的审批与终端运行时保护,但当前不宣称 OS 级文件系统沙箱。 工作区边界默认为 --workspace/trust 可解除边界。 审批模式可按会话配置。所有凭证/审批/提权事件写入 ~/.deepseek/audit.log。 diff --git a/web/app/[locale]/page.tsx b/web/app/[locale]/page.tsx index 05762720..2c999f39 100644 --- a/web/app/[locale]/page.tsx +++ b/web/app/[locale]/page.tsx @@ -15,7 +15,7 @@ const FALLBACK_STATS: RepoStats = { forks: 0, openIssues: 0, openPulls: 0, - contributors: 98, + contributors: 99, fetchedAt: new Date().toISOString(), }; @@ -94,8 +94,8 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s

CodeWhale {isZh - ? " 是面向 DeepSeek V4 及其他开放权重模型的终端原生编程智能体。它读改文件、跑测试、调用 MCP 服务器,全程在你的文件系统沙箱内运行。" - : " is a terminal-native coding agent for DeepSeek V4 and other open-weight models. It reads and edits files, runs tests, calls MCP servers — all inside your filesystem sandbox."} + ? " 是面向 DeepSeek V4 及其他开放权重模型的终端原生编程智能体。它读改文件、跑测试、调用 MCP 服务器,并通过审批、工作区边界和平台沙箱控制风险。" + : " is a terminal-native coding agent for DeepSeek V4 and other open-weight models. It reads and edits files, runs tests, calls MCP servers, and controls risk through approvals, workspace boundaries, and platform sandboxes."}

@@ -155,7 +155,7 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s )}
- {isZh ? "需要 Node 或 Rust 1.88+" : "needs Node or Rust 1.88+"} + {isZh ? "Linux / macOS / Windows x64" : "Linux / macOS / Windows x64"} {isZh ? "其他方式 →" : "other ways →"}
@@ -188,14 +188,14 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
02 · 开源模型优先

DeepSeek V4 深度集成

- 原生 DeepSeek API:推理流、缓存指标、思考力度控制。OpenRouter、NVIDIA NIM、vLLM、sglang 同时可选。 + 原生 DeepSeek API:推理流、缓存指标、思考力度控制。Moonshot/Kimi、OpenRouter、NVIDIA NIM、vLLM、SGLang 等同时可选。

03 · 沙箱边界

Plan、Agent、YOLO

- Plan 只读;Agent 风险操作前确认;YOLO 全自动。沙箱:seatbelt(macOS)、landlock(Linux)、受限令牌(Windows)。 + Plan 只读;Agent 风险操作前确认;YOLO 全自动。macOS 使用 seatbelt,Linux 使用 landlock;Windows 保留同样的审批与终端保护。

@@ -212,14 +212,14 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
02 · open models first

DeepSeek V4, deeply integrated

- Native DeepSeek API: reasoning streaming, cache metrics, thinking-effort control. OpenRouter, NVIDIA NIM, vLLM, and sglang also supported. + Native DeepSeek API: reasoning streaming, cache metrics, thinking-effort control. Moonshot/Kimi, OpenRouter, NVIDIA NIM, vLLM, and SGLang are also supported.

-
03 · sandboxed
+
03 · controlled

Plan, Agent, YOLO

- Plan reads only. Agent asks before risky ops. YOLO auto-approves. Sandboxed via seatbelt (macOS), landlock (Linux), restricted tokens (Windows). + Plan reads only. Agent asks before risky ops. YOLO auto-approves. macOS uses seatbelt, Linux uses landlock; Windows keeps the same approval and terminal protections.

@@ -325,7 +325,7 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s B -->|tool call| T["read_file · edit_file · grep
apply_patch · exec_shell
mcp_<server>_<tool>"] T -->|approval Y/N| P["审批对话框
approval dialog"] P --> B - T -->|exec| S["沙箱
seatbelt · landlock · win32"] + T -->|exec| S["平台控制
seatbelt · landlock · approvals"] classDef accent fill:#e9eefe,stroke:#0e0e10,stroke-width:1px; classDef api fill:#0e0e10,stroke:#0e0e10,color:#ffffff; class C api; @@ -337,7 +337,7 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s B -->|tool call| T["read_file · edit_file · grep
apply_patch · exec_shell
mcp_<server>_<tool>"] T -->|approval Y/N| P["Approval
dialog"] P --> B - T -->|exec| S["Sandbox
seatbelt · landlock · win32"] + T -->|exec| S["Platform controls
seatbelt · landlock · approvals"] classDef accent fill:#e9eefe,stroke:#0e0e10,stroke-width:1px; classDef api fill:#0e0e10,stroke:#0e0e10,color:#ffffff; class C api; diff --git a/web/app/[locale]/roadmap/page.tsx b/web/app/[locale]/roadmap/page.tsx index 17e382bb..787ff0c6 100644 --- a/web/app/[locale]/roadmap/page.tsx +++ b/web/app/[locale]/roadmap/page.tsx @@ -26,12 +26,12 @@ const tracksEn = [ { title: "Sub-agent parallel execution", note: "agent_open / agent_eval / agent_close; up to 10 concurrent sessions with bounded result handles" }, { title: "RLM batched processing", note: "Persistent sandboxed Python REPL with 1–16 cheap parallel children for long-input analysis" }, { title: "Three operating modes", note: "Plan (read-only), Agent (default), YOLO (auto-approved); orthogonal suggest / auto / never approval" }, - { title: "Per-platform sandbox", note: "seatbelt (macOS), landlock (Linux); Windows containment via restricted tokens (limited)" }, + { title: "Per-platform controls", note: "seatbelt (macOS), landlock (Linux); Windows keeps approvals and terminal/runtime protections while OS sandbox work remains tracked" }, { title: "Durable sessions + tasks", note: "Save, resume, rollback; background task queue with replayable timelines under ~/.deepseek/tasks/" }, - { title: "Bidirectional MCP", note: "Consume tools from external servers; expose as server via `deepseek mcp`; ~/.deepseek/mcp.json" }, + { 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: "OpenRouter provider", note: "First-class OpenRouter integration with 300+ models across dozens of providers" }, - { title: "Multi-provider support", note: "Hot-swap between providers (DeepSeek, OpenAI, Anthropic, OpenRouter) per session" }, + { 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" }, ], }, { @@ -43,8 +43,8 @@ const tracksEn = [ { title: "Memory typed store", note: "SQLite + FTS5 backend with graph-structured agent memory and multi-signal recall (#534–#536)" }, { title: "Feishu / Lark bot", note: "Chat-platform frontend over the existing runtime API (#757)" }, { title: "Chinese-market & i18n", note: "Locale-aware UI, platform refinements, region-specific search backends (#755)" }, - { title: "Hugging Face model discovery + Model Lab", note: "Browse, download, and manage models from Hugging Face Hub directly in the TUI" }, - { title: "ZenMux / OpenAI-compatible providers", note: "Bring any OpenAI-compatible endpoint (vLLM, LiteLLM, Ollama, local) as a first-class provider" }, + { title: "Model Lab", note: "Curated model discovery and benchmarking for open-weight and self-hosted workflows" }, + { title: "Provider billing and catalogs", note: "/balance capability layer plus richer live model catalogs for providers that expose listing endpoints" }, ], }, { @@ -92,12 +92,12 @@ const tracksZh = [ { title: "子 Agent 并行执行", note: "agent_open / agent_eval / agent_close;最多 10 个并发会话,通过 var_handle 有界读取结果" }, { title: "RLM 批量处理", note: "持久沙箱 Python REPL,支持 1–16 路廉价并行子调用,处理长文本分析" }, { title: "三种运行模式", note: "Plan(只读)、Agent(默认)、YOLO(自动批准);审批模式正交(建议/自动/拒绝)" }, - { title: "跨平台沙箱", note: "seatbelt(macOS)、landlock(Linux);Windows 通过受限令牌实现基础隔离(功能有限)" }, + { title: "跨平台控制", note: "seatbelt(macOS)、landlock(Linux);Windows 保留审批与终端运行时保护,OS 沙箱仍在跟踪中" }, { title: "持久化会话 + 后台任务", note: "保存、恢复、回滚;后台任务队列,可回放时间线,位于 ~/.deepseek/tasks/" }, - { title: "双向 MCP 协议", note: "消费外部服务器工具;通过 `deepseek mcp` 暴露为服务器;~/.deepseek/mcp.json" }, + { title: "双向 MCP 协议", note: "消费外部服务器工具;通过 `codewhale mcp` 暴露为服务器;~/.deepseek/mcp.json" }, { title: "技能 + 统一命令面板", note: "~/.deepseek/skills/ 自动加载;/help、/mode、/status、/config、/trust、/feedback" }, - { title: "OpenRouter 提供商", note: "原生集成 OpenRouter,支持 300+ 模型,覆盖数十个提供商" }, - { title: "多提供商支持", note: "按会话动态切换提供商(DeepSeek、OpenAI、Anthropic、OpenRouter)" }, + { 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 模式" }, ], }, { @@ -109,8 +109,8 @@ const tracksZh = [ { title: "记忆类型化存储", note: "SQLite + FTS5 后端,图结构 Agent 记忆,多信号召回(#534–#536)" }, { title: "飞书 / Lark 机器人", note: "基于现有 runtime API 的聊天平台前端(#757)" }, { title: "中国市场与国际化改进", note: "本地化 UI、平台优化、区域搜索引擎(#755)" }, - { title: "Hugging Face 模型发现 + 模型实验室", note: "在 TUI 中直接浏览、下载和管理 Hugging Face Hub 上的模型" }, - { title: "ZenMux / OpenAI 兼容提供商", note: "将任意 OpenAI 兼容端点(vLLM、LiteLLM、Ollama、本地模型)作为一级提供商接入" }, + { title: "模型实验室", note: "面向开放权重和自托管工作流的模型发现与基准测试" }, + { title: "提供商账单与目录", note: "/balance 能力层,以及对支持列表接口的提供商提供更完整的实时模型目录" }, ], }, { @@ -339,4 +339,4 @@ export default async function RoadmapPage({ params }: { params: Promise<{ locale )} ); -} \ No newline at end of file +} diff --git a/web/lib/facts-drift.ts b/web/lib/facts-drift.ts index 5e99f35c..531ebcff 100644 --- a/web/lib/facts-drift.ts +++ b/web/lib/facts-drift.ts @@ -77,12 +77,15 @@ function deriveProvidersFromConfig(cfg: string): ProviderFact[] { // so the binary rejects it — keep it out of the docs. Issue #1104. const labelMap: Record = { Deepseek: { id: "deepseek", label: "DeepSeek", env: "DEEPSEEK_API_KEY" }, - NvidiaNim: { id: "nvidia-nim", label: "NVIDIA NIM", env: "NVIDIA_API_KEY" }, - Openai: { id: "openai", label: "OpenAI", env: "OPENAI_API_KEY" }, + NvidiaNim: { id: "nvidia-nim", label: "NVIDIA NIM", env: "NVIDIA_API_KEY / NVIDIA_NIM_API_KEY" }, + Openai: { id: "openai", label: "OpenAI-compatible", env: "OPENAI_API_KEY" }, + Atlascloud: { id: "atlascloud", label: "AtlasCloud", env: "ATLASCLOUD_API_KEY" }, + WanjieArk: { id: "wanjie-ark", label: "Wanjie Ark", env: "WANJIE_ARK_API_KEY / WANJIE_API_KEY / WANJIE_MAAS_API_KEY" }, Openrouter: { id: "openrouter", label: "OpenRouter", env: "OPENROUTER_API_KEY" }, - Novita: { id: "novita", label: "Novita", env: "NOVITA_API_KEY" }, - Fireworks: { id: "fireworks", label: "Fireworks", env: "FIREWORKS_API_KEY" }, - Sglang: { id: "sglang", label: "sglang", env: "SGLANG_API_KEY" }, + Novita: { id: "novita", label: "Novita AI", env: "NOVITA_API_KEY" }, + Fireworks: { id: "fireworks", label: "Fireworks AI", env: "FIREWORKS_API_KEY" }, + Moonshot: { id: "moonshot", label: "Moonshot/Kimi", env: "MOONSHOT_API_KEY / KIMI_API_KEY" }, + Sglang: { id: "sglang", label: "SGLang", env: "SGLANG_API_KEY" }, Vllm: { id: "vllm", label: "vLLM", env: "VLLM_API_KEY" }, Ollama: { id: "ollama", label: "Ollama", env: "OLLAMA_API_KEY" }, }; @@ -98,7 +101,6 @@ function deriveSandboxBackends(files: string[]): string[] { const map: Record = { seatbelt: "seatbelt (macOS)", landlock: "landlock (Linux)", - windows: "AppContainer / restricted tokens (Windows)", }; return files .map((f) => f.replace(/\.rs$/, "")) diff --git a/web/lib/facts.generated.ts b/web/lib/facts.generated.ts index b4468cf9..0db1a07a 100644 --- a/web/lib/facts.generated.ts +++ b/web/lib/facts.generated.ts @@ -18,8 +18,8 @@ export interface RepoFacts { } export const FACTS: RepoFacts = { - "generatedAt": "2026-05-24T16:01:45.189Z", - "version": "0.8.43", + "generatedAt": "2026-05-26T03:03:01.383Z", + "version": "0.8.45", "crates": [ "agent", "app-server", @@ -38,8 +38,7 @@ export const FACTS: RepoFacts = { ], "sandboxBackends": [ "landlock (Linux)", - "seatbelt (macOS)", - "AppContainer / restricted tokens (Windows)" + "seatbelt (macOS)" ], "providers": [ { @@ -50,13 +49,23 @@ export const FACTS: RepoFacts = { { "id": "nvidia-nim", "label": "NVIDIA NIM", - "env": "NVIDIA_API_KEY" + "env": "NVIDIA_API_KEY / NVIDIA_NIM_API_KEY" }, { "id": "openai", - "label": "OpenAI", + "label": "OpenAI-compatible", "env": "OPENAI_API_KEY" }, + { + "id": "atlascloud", + "label": "AtlasCloud", + "env": "ATLASCLOUD_API_KEY" + }, + { + "id": "wanjie-ark", + "label": "Wanjie Ark", + "env": "WANJIE_ARK_API_KEY / WANJIE_API_KEY / WANJIE_MAAS_API_KEY" + }, { "id": "openrouter", "label": "OpenRouter", @@ -64,17 +73,22 @@ export const FACTS: RepoFacts = { }, { "id": "novita", - "label": "Novita", + "label": "Novita AI", "env": "NOVITA_API_KEY" }, { "id": "fireworks", - "label": "Fireworks", + "label": "Fireworks AI", "env": "FIREWORKS_API_KEY" }, + { + "id": "moonshot", + "label": "Moonshot/Kimi", + "env": "MOONSHOT_API_KEY / KIMI_API_KEY" + }, { "id": "sglang", - "label": "sglang", + "label": "SGLang", "env": "SGLANG_API_KEY" }, { @@ -90,7 +104,7 @@ export const FACTS: RepoFacts = { ], "defaultModel": "deepseek-v4-pro", "nodeEngines": ">=18", - "toolCount": 69, + "toolCount": 70, "license": "MIT", "latestRelease": null }; diff --git a/web/lib/github.ts b/web/lib/github.ts index aeafe692..380c0004 100644 --- a/web/lib/github.ts +++ b/web/lib/github.ts @@ -2,7 +2,7 @@ import type { FeedItem, RepoStats } from "./types"; const REPO = process.env.GITHUB_REPO ?? "Hmbown/CodeWhale"; const GH = "https://api.github.com"; -const MIN_KNOWN_CONTRIBUTORS = 98; +const MIN_KNOWN_CONTRIBUTORS = 99; function headers(token?: string): HeadersInit { const h: Record = { diff --git a/web/lib/roadmap-feed.ts b/web/lib/roadmap-feed.ts index 102f1037..48edda44 100644 --- a/web/lib/roadmap-feed.ts +++ b/web/lib/roadmap-feed.ts @@ -55,6 +55,20 @@ async function gh(url: string, ghToken?: string): Promise { interface GhRelease { tag_name: string; name: string | null; body: string | null; html_url: string; prerelease: boolean; draft: boolean } interface GhIssue { number: number; title: string; html_url: string; body: string | null; state: string; pull_request?: unknown } +const FALLBACK_SHIPPED: RoadmapItem[] = [ + { + title: "v0.8.45", + note: "Moonshot/Kimi OAuth, provider-surface sync, and current Windows install/runtime guidance", + href: "https://github.com/Hmbown/CodeWhale/releases/tag/v0.8.45", + }, +]; + +function withPinnedShipped(items: RoadmapItem[]): RoadmapItem[] { + const seen = new Set(items.map((item) => item.title)); + const pinned = FALLBACK_SHIPPED.filter((item) => !seen.has(item.title)); + return [...pinned, ...items]; +} + function summarizeReleaseBody(body: string | null): string { if (!body) return ""; // First non-empty line, stripped of markdown headers / bullets / links @@ -100,17 +114,19 @@ export async function fetchRoadmap(ghToken?: string): Promise { fetchByLabel("roadmap:ruled-out", ghToken, "all"), ]); - const shipped: RoadmapItem[] = (releases ?? []) + const shipped: RoadmapItem[] = releases + ? releases .filter((r) => !r.draft) .map((r) => ({ title: r.name?.trim() || r.tag_name, note: summarizeReleaseBody(r.body) || r.tag_name, href: r.html_url, - })); + })) + : FALLBACK_SHIPPED; return { generatedAt: new Date().toISOString(), - shipped, + shipped: withPinnedShipped(shipped), underway, considered, ruledOut, @@ -121,7 +137,10 @@ export async function getCachedRoadmap(kv: KVNamespace | undefined, ghToken: str try { if (kv) { const cached = await kv.get(KV_KEY); - if (cached) return JSON.parse(cached) as RoadmapFeed; + if (cached) { + const parsed = JSON.parse(cached) as RoadmapFeed; + return { ...parsed, shipped: withPinnedShipped(parsed.shipped ?? []) }; + } } const fresh = await fetchRoadmap(ghToken); if (kv) { diff --git a/web/scripts/derive-facts.mjs b/web/scripts/derive-facts.mjs index 76b31f2d..b1e7830d 100644 --- a/web/scripts/derive-facts.mjs +++ b/web/scripts/derive-facts.mjs @@ -46,10 +46,10 @@ function deriveSandboxBackends() { const files = readdirSync(dir) .filter((f) => f.endsWith(".rs")) .map((f) => f.replace(/\.rs$/, "")) - .filter((f) => !["mod", "policy", "backend", "opensandbox"].includes(f)) + .filter((f) => !["mod", "policy", "backend", "opensandbox", "windows"].includes(f)) .sort(); // canonicalize platform names - const map = { seatbelt: "seatbelt (macOS)", landlock: "landlock (Linux)", windows: "AppContainer / restricted tokens (Windows)" }; + const map = { seatbelt: "seatbelt (macOS)", landlock: "landlock (Linux)" }; return files.map((f) => map[f] ?? f); } @@ -66,12 +66,15 @@ function deriveProviders() { // shared ProviderKind, so we exclude it until that lands. Issue #1104. const labelMap = { Deepseek: { id: "deepseek", label: "DeepSeek", env: "DEEPSEEK_API_KEY" }, - NvidiaNim: { id: "nvidia-nim", label: "NVIDIA NIM", env: "NVIDIA_API_KEY" }, - Openai: { id: "openai", label: "OpenAI", env: "OPENAI_API_KEY" }, + NvidiaNim: { id: "nvidia-nim", label: "NVIDIA NIM", env: "NVIDIA_API_KEY / NVIDIA_NIM_API_KEY" }, + Openai: { id: "openai", label: "OpenAI-compatible", env: "OPENAI_API_KEY" }, + Atlascloud: { id: "atlascloud", label: "AtlasCloud", env: "ATLASCLOUD_API_KEY" }, + WanjieArk: { id: "wanjie-ark", label: "Wanjie Ark", env: "WANJIE_ARK_API_KEY / WANJIE_API_KEY / WANJIE_MAAS_API_KEY" }, Openrouter: { id: "openrouter", label: "OpenRouter", env: "OPENROUTER_API_KEY" }, - Novita: { id: "novita", label: "Novita", env: "NOVITA_API_KEY" }, - Fireworks: { id: "fireworks", label: "Fireworks", env: "FIREWORKS_API_KEY" }, - Sglang: { id: "sglang", label: "sglang", env: "SGLANG_API_KEY" }, + Novita: { id: "novita", label: "Novita AI", env: "NOVITA_API_KEY" }, + Fireworks: { id: "fireworks", label: "Fireworks AI", env: "FIREWORKS_API_KEY" }, + Moonshot: { id: "moonshot", label: "Moonshot/Kimi", env: "MOONSHOT_API_KEY / KIMI_API_KEY" }, + Sglang: { id: "sglang", label: "SGLang", env: "SGLANG_API_KEY" }, Vllm: { id: "vllm", label: "vLLM", env: "VLLM_API_KEY" }, Ollama: { id: "ollama", label: "Ollama", env: "OLLAMA_API_KEY" }, }; From 0628adab38f1a0515ebb6306ab9b5a1c9171136d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 03:47:42 +0000 Subject: [PATCH 5/6] test: add table-driven env-forwarding regression test for all providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers Openrouter, Novita, NvidiaNim, Fireworks, Sglang, Vllm, Ollama, Atlascloud, and WanjieArk — the providers that were silently expanded by the generic provider_env_vars loop but had no test coverage beyond the existing Moonshot and OpenAI cases. Co-Authored-By: bot_apk --- crates/cli/src/lib.rs | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 18cc93d8..18f2c076 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -2732,6 +2732,84 @@ mod tests { ); } + #[test] + fn build_tui_command_forwards_provider_env_vars_for_all_providers() { + let _lock = env_lock(); + let dir = tempfile::TempDir::new().expect("tempdir"); + let custom = dir + .path() + .join(format!("custom-tui{}", std::env::consts::EXE_SUFFIX)); + std::fs::write(&custom, b"").unwrap(); + let custom_str = custom.to_string_lossy().into_owned(); + let _bin = ScopedEnvVar::set("DEEPSEEK_TUI_BIN", &custom_str); + + // (provider, cli flag, extra env vars that must be forwarded besides DEEPSEEK_API_KEY) + let cases: &[(ProviderKind, &str, &[&str])] = &[ + (ProviderKind::Openrouter, "openrouter", &["OPENROUTER_API_KEY"]), + (ProviderKind::Novita, "novita", &["NOVITA_API_KEY"]), + ( + ProviderKind::NvidiaNim, + "nvidia-nim", + &["NVIDIA_API_KEY", "NVIDIA_NIM_API_KEY"], + ), + (ProviderKind::Fireworks, "fireworks", &["FIREWORKS_API_KEY"]), + (ProviderKind::Sglang, "sglang", &["SGLANG_API_KEY"]), + (ProviderKind::Vllm, "vllm", &["VLLM_API_KEY"]), + (ProviderKind::Ollama, "ollama", &["OLLAMA_API_KEY"]), + ( + ProviderKind::Atlascloud, + "atlascloud", + &["ATLASCLOUD_API_KEY"], + ), + ( + ProviderKind::WanjieArk, + "wanjie-ark", + &["WANJIE_ARK_API_KEY", "WANJIE_API_KEY", "WANJIE_MAAS_API_KEY"], + ), + ]; + + for &(provider, flag, expected_vars) in cases { + let cli = parse_ok(&[ + "codewhale", + "--provider", + flag, + "--workspace", + "/tmp/codewhale-workspace", + ]); + let resolved = ResolvedRuntimeOptions { + provider, + model: "test-model".to_string(), + api_key: Some("test-key".to_string()), + api_key_source: Some(RuntimeApiKeySource::Env), + base_url: "http://localhost:8000/v1".to_string(), + auth_mode: Some("api_key".to_string()), + output_mode: None, + log_level: None, + telemetry: false, + approval_policy: None, + sandbox_mode: None, + yolo: None, + http_headers: std::collections::BTreeMap::new(), + }; + + let cmd = build_tui_command(&cli, &resolved, Vec::new()) + .unwrap_or_else(|e| panic!("{flag}: {e}")); + + assert_eq!( + command_env(&cmd, "DEEPSEEK_API_KEY").as_deref(), + Some("test-key"), + "{flag}: DEEPSEEK_API_KEY not forwarded" + ); + for var in expected_vars { + assert_eq!( + command_env(&cmd, var).as_deref(), + Some("test-key"), + "{flag}: {var} not forwarded" + ); + } + } + } + #[test] fn parses_top_level_prompt_flag_for_canonical_one_shot() { let cli = parse_ok(&["deepseek", "-p", "Reply with exactly OK."]); From c4e91446affcfe6f3df47132bb5fa57de1923aa8 Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Mon, 25 May 2026 23:07:12 -0500 Subject: [PATCH 6/6] test(cli): format provider env regression cases --- crates/cli/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 18f2c076..3eaeb3c0 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -2745,7 +2745,11 @@ mod tests { // (provider, cli flag, extra env vars that must be forwarded besides DEEPSEEK_API_KEY) let cases: &[(ProviderKind, &str, &[&str])] = &[ - (ProviderKind::Openrouter, "openrouter", &["OPENROUTER_API_KEY"]), + ( + ProviderKind::Openrouter, + "openrouter", + &["OPENROUTER_API_KEY"], + ), (ProviderKind::Novita, "novita", &["NOVITA_API_KEY"]), ( ProviderKind::NvidiaNim, @@ -2764,7 +2768,11 @@ mod tests { ( ProviderKind::WanjieArk, "wanjie-ark", - &["WANJIE_ARK_API_KEY", "WANJIE_API_KEY", "WANJIE_MAAS_API_KEY"], + &[ + "WANJIE_ARK_API_KEY", + "WANJIE_API_KEY", + "WANJIE_MAAS_API_KEY", + ], ), ];