docs: sync release credit surfaces

This commit is contained in:
Hunter B
2026-05-31 17:43:46 -07:00
parent 7eb740bd86
commit 6f295e9961
15 changed files with 255 additions and 98 deletions
+8
View File
@@ -112,6 +112,14 @@ Thanks to contributors whose PRs landed or were harvested in this release:
**@hoclaptrinh33** (#2358),
and **@BryonGo** (#2437).
Thanks also to reporters and verification helpers whose issues, patches,
screenshots, logs, or retest requests shaped this release: **@buko** (#2359,
#2360, #2369, #2469), **@yyyCode**, **@gaslebinh-glitch**, **@Dr3259**,
**@lpeng1711694086-lang**, **@VerrPower**, **@yan-zay**, **@jretz**,
**@Neo-millunnium**, **@caeserchen**, **@T-Phuong-Nguyen**, **@zhyuzhyu**,
**@0gl20shk0sbt36**, **@hatakes**, **@goodvecn-dev**, **@bevis-wong**,
**@PurplePulse**, and **@nbiish**.
## [0.8.47] - 2026-05-26
### Added
+4
View File
@@ -422,6 +422,10 @@ description: DeepSeek にカスタムワークフローを実行させたいと
このプロジェクトは、増え続けるコントリビューターのコミュニティから助けを得て出荷されています:
v0.8.48 でマージまたは取り込まれた貢献者: **[@cy2311](https://github.com/cy2311)**、**[@LING71671](https://github.com/LING71671)**、**[@axobase001](https://github.com/axobase001)**、**[@dzyuan](https://github.com/dzyuan)**、**[@mvanhorn](https://github.com/mvanhorn)**、**[@malsony](https://github.com/malsony)**、**[@gaord](https://github.com/gaord)**、**[@yuanchenglu](https://github.com/yuanchenglu)**、**[@idling11](https://github.com/idling11)**、**[@h3c-hexin](https://github.com/h3c-hexin)**、**[@AdityaVG13](https://github.com/AdityaVG13)**、**[@Sskift](https://github.com/Sskift)**、**[@cyq1017](https://github.com/cyq1017)**、**[@HUQIANTAO](https://github.com/HUQIANTAO)**、**[@New2Niu](https://github.com/New2Niu)**、**[@AiurArtanis](https://github.com/AiurArtanis)**、**[@Lee-take](https://github.com/Lee-take)**、**[@nightt5879](https://github.com/nightt5879)**、**[@AresNing](https://github.com/AresNing)**、**[@AccMoment](https://github.com/AccMoment)**、**[@reidliu41](https://github.com/reidliu41)**、**[@aboimpinto](https://github.com/aboimpinto)**、**[@zhuangbiaowei](https://github.com/zhuangbiaowei)**、**[@donglovejava](https://github.com/donglovejava)**、**[@hongqitai](https://github.com/hongqitai)**、**[@zlh124](https://github.com/zlh124)**、**[@encyc](https://github.com/encyc)**、**[@Implementist](https://github.com/Implementist)**、**[@lihuan215](https://github.com/lihuan215)**、**[@LeoAlex0](https://github.com/LeoAlex0)**、**[@jimmyzhuu](https://github.com/jimmyzhuu)**、**[@rockyzhang](https://github.com/rockyzhang)**、**[@mo-vic](https://github.com/mo-vic)**、**[@hufanexplore](https://github.com/hufanexplore)**、**[@hoclaptrinh33](https://github.com/hoclaptrinh33)**、**[@BryonGo](https://github.com/BryonGo)**。
報告、再現手順、検証で v0.8.48 を支えてくれた **[@buko](https://github.com/buko)**、**[@yyyCode](https://github.com/yyyCode)**、**[@gaslebinh-glitch](https://github.com/gaslebinh-glitch)**、**[@Dr3259](https://github.com/Dr3259)**、**[@lpeng1711694086-lang](https://github.com/lpeng1711694086-lang)**、**[@VerrPower](https://github.com/VerrPower)**、**[@yan-zay](https://github.com/yan-zay)**、**[@jretz](https://github.com/jretz)**、**[@Neo-millunnium](https://github.com/Neo-millunnium)**、**[@caeserchen](https://github.com/caeserchen)**、**[@T-Phuong-Nguyen](https://github.com/T-Phuong-Nguyen)**、**[@zhyuzhyu](https://github.com/zhyuzhyu)**、**[@0gl20shk0sbt36](https://github.com/0gl20shk0sbt36)**、**[@hatakes](https://github.com/hatakes)**、**[@goodvecn-dev](https://github.com/goodvecn-dev)**、**[@bevis-wong](https://github.com/bevis-wong)**、**[@PurplePulse](https://github.com/PurplePulse)**、**[@nbiish](https://github.com/nbiish)** にも感謝します。
- **[merchloubna70-dot](https://github.com/merchloubna70-dot)** — 機能、修正、VS Code 拡張のスキャフォールドにまたがる 28 件の PR (#645#681)
- **[WyxBUPT-22](https://github.com/WyxBUPT-22)** — 表、太字/斜体、水平線の Markdown レンダリング (#579)
- **[loongmiaow-pixel](https://github.com/loongmiaow-pixel)** — Windows と中国向けインストールドキュメント (#578)
+11
View File
@@ -705,6 +705,17 @@ This project ships with help from a growing community of contributors:
- **[hongqitai](https://github.com/hongqitai)** — state schema parent-entry support and clippy/fmt cleanup (#2308, #2432)
- **[BryonGo](https://github.com/BryonGo)** — effective-model compaction budgeting fix (#2437)
Reports, repros, and verification that shaped v0.8.48 also deserve visible
credit: **[@buko](https://github.com/buko)**, **[@yyyCode](https://github.com/yyyCode)**,
**[@gaslebinh-glitch](https://github.com/gaslebinh-glitch)**, **[@Dr3259](https://github.com/Dr3259)**,
**[@lpeng1711694086-lang](https://github.com/lpeng1711694086-lang)**, **[@VerrPower](https://github.com/VerrPower)**,
**[@yan-zay](https://github.com/yan-zay)**, **[@jretz](https://github.com/jretz)**,
**[@Neo-millunnium](https://github.com/Neo-millunnium)**, **[@caeserchen](https://github.com/caeserchen)**,
**[@T-Phuong-Nguyen](https://github.com/T-Phuong-Nguyen)**, **[@zhyuzhyu](https://github.com/zhyuzhyu)**,
**[@0gl20shk0sbt36](https://github.com/0gl20shk0sbt36)**, **[@hatakes](https://github.com/hatakes)**,
**[@goodvecn-dev](https://github.com/goodvecn-dev)**, **[@bevis-wong](https://github.com/bevis-wong)**,
**[@PurplePulse](https://github.com/PurplePulse)**, and **[@nbiish](https://github.com/nbiish)**.
---
## Contributing
+4
View File
@@ -509,6 +509,10 @@ Lịch sử cập nhật chi tiết: [CHANGELOG.md](CHANGELOG.md).
Dự án này được phát triển và vận hành trơn tru với sự đóng góp của cộng đồng các nhà phát triển ngày càng lớn mạnh:
Các đóng góp đã được merge hoặc được harvest trong v0.8.48: **[@cy2311](https://github.com/cy2311)**, **[@LING71671](https://github.com/LING71671)**, **[@axobase001](https://github.com/axobase001)**, **[@dzyuan](https://github.com/dzyuan)**, **[@mvanhorn](https://github.com/mvanhorn)**, **[@malsony](https://github.com/malsony)**, **[@gaord](https://github.com/gaord)**, **[@yuanchenglu](https://github.com/yuanchenglu)**, **[@idling11](https://github.com/idling11)**, **[@h3c-hexin](https://github.com/h3c-hexin)**, **[@AdityaVG13](https://github.com/AdityaVG13)**, **[@Sskift](https://github.com/Sskift)**, **[@cyq1017](https://github.com/cyq1017)**, **[@HUQIANTAO](https://github.com/HUQIANTAO)**, **[@New2Niu](https://github.com/New2Niu)**, **[@AiurArtanis](https://github.com/AiurArtanis)**, **[@Lee-take](https://github.com/Lee-take)**, **[@nightt5879](https://github.com/nightt5879)**, **[@AresNing](https://github.com/AresNing)**, **[@AccMoment](https://github.com/AccMoment)**, **[@reidliu41](https://github.com/reidliu41)**, **[@aboimpinto](https://github.com/aboimpinto)**, **[@zhuangbiaowei](https://github.com/zhuangbiaowei)**, **[@donglovejava](https://github.com/donglovejava)**, **[@hongqitai](https://github.com/hongqitai)**, **[@zlh124](https://github.com/zlh124)**, **[@encyc](https://github.com/encyc)**, **[@Implementist](https://github.com/Implementist)**, **[@lihuan215](https://github.com/lihuan215)**, **[@LeoAlex0](https://github.com/LeoAlex0)**, **[@jimmyzhuu](https://github.com/jimmyzhuu)**, **[@rockyzhang](https://github.com/rockyzhang)**, **[@mo-vic](https://github.com/mo-vic)**, **[@hufanexplore](https://github.com/hufanexplore)**, **[@hoclaptrinh33](https://github.com/hoclaptrinh33)** và **[@BryonGo](https://github.com/BryonGo)**.
Xin cảm ơn các báo cáo, bước tái hiện lỗi và xác minh từ **[@buko](https://github.com/buko)**, **[@yyyCode](https://github.com/yyyCode)**, **[@gaslebinh-glitch](https://github.com/gaslebinh-glitch)**, **[@Dr3259](https://github.com/Dr3259)**, **[@lpeng1711694086-lang](https://github.com/lpeng1711694086-lang)**, **[@VerrPower](https://github.com/VerrPower)**, **[@yan-zay](https://github.com/yan-zay)**, **[@jretz](https://github.com/jretz)**, **[@Neo-millunnium](https://github.com/Neo-millunnium)**, **[@caeserchen](https://github.com/caeserchen)**, **[@T-Phuong-Nguyen](https://github.com/T-Phuong-Nguyen)**, **[@zhyuzhyu](https://github.com/zhyuzhyu)**, **[@0gl20shk0sbt36](https://github.com/0gl20shk0sbt36)**, **[@hatakes](https://github.com/hatakes)**, **[@goodvecn-dev](https://github.com/goodvecn-dev)**, **[@bevis-wong](https://github.com/bevis-wong)**, **[@PurplePulse](https://github.com/PurplePulse)** và **[@nbiish](https://github.com/nbiish)** đã giúp định hình v0.8.48.
- **[merchloubna70-dot](https://github.com/merchloubna70-dot)** — Đóng góp 28 PR bao gồm tính năng mới, sửa lỗi và dựng sẵn extension cho VS Code (#645#681)
- **[WyxBUPT-22](https://github.com/WyxBUPT-22)** — Xây dựng trình kết xuất Markdown hỗ trợ bảng biểu, chữ đậm/nghiêng và đường kẻ ngang (#579)
- **[loongmiaow-pixel](https://github.com/loongmiaow-pixel)** — Tài liệu cài đặt cho Windows và Trung Quốc (#578)
+4
View File
@@ -537,6 +537,10 @@ description: 当 DeepSeek 需要遵循我的自定义工作流时使用这个技
本项目由不断壮大的贡献者社区共同打造:
v0.8.48 合并或吸收的贡献者包括:**[@cy2311](https://github.com/cy2311)**、**[@LING71671](https://github.com/LING71671)**、**[@axobase001](https://github.com/axobase001)**、**[@dzyuan](https://github.com/dzyuan)**、**[@mvanhorn](https://github.com/mvanhorn)**、**[@malsony](https://github.com/malsony)**、**[@gaord](https://github.com/gaord)**、**[@yuanchenglu](https://github.com/yuanchenglu)**、**[@idling11](https://github.com/idling11)**、**[@h3c-hexin](https://github.com/h3c-hexin)**、**[@AdityaVG13](https://github.com/AdityaVG13)**、**[@Sskift](https://github.com/Sskift)**、**[@cyq1017](https://github.com/cyq1017)**、**[@HUQIANTAO](https://github.com/HUQIANTAO)**、**[@New2Niu](https://github.com/New2Niu)**、**[@AiurArtanis](https://github.com/AiurArtanis)**、**[@Lee-take](https://github.com/Lee-take)**、**[@nightt5879](https://github.com/nightt5879)**、**[@AresNing](https://github.com/AresNing)**、**[@AccMoment](https://github.com/AccMoment)**、**[@reidliu41](https://github.com/reidliu41)**、**[@aboimpinto](https://github.com/aboimpinto)**、**[@zhuangbiaowei](https://github.com/zhuangbiaowei)**、**[@donglovejava](https://github.com/donglovejava)**、**[@hongqitai](https://github.com/hongqitai)**、**[@zlh124](https://github.com/zlh124)**、**[@encyc](https://github.com/encyc)**、**[@Implementist](https://github.com/Implementist)**、**[@lihuan215](https://github.com/lihuan215)**、**[@LeoAlex0](https://github.com/LeoAlex0)**、**[@jimmyzhuu](https://github.com/jimmyzhuu)**、**[@rockyzhang](https://github.com/rockyzhang)**、**[@mo-vic](https://github.com/mo-vic)**、**[@hufanexplore](https://github.com/hufanexplore)**、**[@hoclaptrinh33](https://github.com/hoclaptrinh33)** 和 **[@BryonGo](https://github.com/BryonGo)**。
同样感谢提供报告、复现和验证的 **[@buko](https://github.com/buko)**、**[@yyyCode](https://github.com/yyyCode)**、**[@gaslebinh-glitch](https://github.com/gaslebinh-glitch)**、**[@Dr3259](https://github.com/Dr3259)**、**[@lpeng1711694086-lang](https://github.com/lpeng1711694086-lang)**、**[@VerrPower](https://github.com/VerrPower)**、**[@yan-zay](https://github.com/yan-zay)**、**[@jretz](https://github.com/jretz)**、**[@Neo-millunnium](https://github.com/Neo-millunnium)**、**[@caeserchen](https://github.com/caeserchen)**、**[@T-Phuong-Nguyen](https://github.com/T-Phuong-Nguyen)**、**[@zhyuzhyu](https://github.com/zhyuzhyu)**、**[@0gl20shk0sbt36](https://github.com/0gl20shk0sbt36)**、**[@hatakes](https://github.com/hatakes)**、**[@goodvecn-dev](https://github.com/goodvecn-dev)**、**[@bevis-wong](https://github.com/bevis-wong)**、**[@PurplePulse](https://github.com/PurplePulse)** 和 **[@nbiish](https://github.com/nbiish)**。
- **[merchloubna70-dot](https://github.com/merchloubna70-dot)** — 28 个 PR,涵盖功能、修复和 VS Code 扩展基础架构 (#645#681)
- **[WyxBUPT-22](https://github.com/WyxBUPT-22)** — Markdown 表格、粗体/斜体和水平线渲染 (#579)
- **[loongmiaow-pixel](https://github.com/loongmiaow-pixel)** — Windows + 中国安装文档 (#578)
+8
View File
@@ -112,6 +112,14 @@ Thanks to contributors whose PRs landed or were harvested in this release:
**@hoclaptrinh33** (#2358),
and **@BryonGo** (#2437).
Thanks also to reporters and verification helpers whose issues, patches,
screenshots, logs, or retest requests shaped this release: **@buko** (#2359,
#2360, #2369, #2469), **@yyyCode**, **@gaslebinh-glitch**, **@Dr3259**,
**@lpeng1711694086-lang**, **@VerrPower**, **@yan-zay**, **@jretz**,
**@Neo-millunnium**, **@caeserchen**, **@T-Phuong-Nguyen**, **@zhyuzhyu**,
**@0gl20shk0sbt36**, **@hatakes**, **@goodvecn-dev**, **@bevis-wong**,
**@PurplePulse**, and **@nbiish**.
## [0.8.47] - 2026-05-26
### Added
+14 -3
View File
@@ -10,14 +10,17 @@ publish-crates), see [`RELEASE_RUNBOOK.md`](RELEASE_RUNBOOK.md).
## 1. CHANGELOG entry exists for the version
- [ ] `CHANGELOG.md` has a `## [X.Y.Z] - YYYY-MM-DD` heading at the top
- [ ] The entry credits every external contributor whose commit lands in this
version. Get the list with:
- [ ] The entry credits every external contributor, harvested PR author,
linked issue reporter, reproduction/log provider, reviewer, and
verification helper whose work materially shaped this version. Get the
commit list with:
```
git log vPREV..HEAD --no-merges --format="%h %an <%ae> %s" \
| grep -v '<your-email@…>'
```
For each contributor, link both their display name and (when known)
`@github-handle`.
`@github-handle`. Then inspect linked issues and harvested PRs so
reporters/helpers are not lost just because they did not author commits.
- [ ] The entry uses the Keep a Changelog headers — `Added`, `Changed`,
`Fixed`, `Security`, `Removed`, `Deprecated`. Add `Known issues` only
if there is something material the user must work around.
@@ -84,6 +87,12 @@ Run, in order, from the repo root:
- [ ] `git push origin vX.Y.Z`
- [ ] The `release.yml` workflow has built and uploaded artifacts to the
GitHub release for this tag.
- [ ] The live GitHub Release body has its own `## Contributors` or
`## Credits` section; do not rely on "see CHANGELOG" alone. Verify with:
```
gh release view vX.Y.Z --repo Hmbown/CodeWhale --json body \
--jq '.body | test("## (Contributors|Credits)")'
```
- [ ] `npm view codewhale@X.Y.Z version codewhaleBinaryVersion --json`
reports the new version on the npm registry.
- [ ] `crates.io` has the new version (or the `publish-crates.sh` job has
@@ -94,6 +103,8 @@ Run, in order, from the repo root:
- [ ] Edit the GitHub release notes to expand any CVE-style or attack
details that were intentionally omitted from the PR title/body.
- [ ] Re-run the GitHub Release body check after any release-workflow rerun;
workflows can overwrite notes and accidentally remove contributor credit.
- [ ] Note any deferred items in the next release's tracking issue.
- [ ] Close any issues that this release fixed.
+22 -20
View File
@@ -39,6 +39,7 @@ npm install -g codewhale
# Cargo (needs Rust 1.88+)
cargo install codewhale-cli --locked
cargo install codewhale-tui --locked
# Homebrew (macOS)
brew tap Hmbown/deepseek-tui && brew install deepseek-tui
@@ -47,7 +48,7 @@ brew tap Hmbown/deepseek-tui && brew install deepseek-tui
# https://github.com/Hmbown/CodeWhale/releases`}
</pre>
<p>
Run <code className="inline">codewhale</code> to start. First run creates <code className="inline">~/.deepseek/</code> automatically.
Run <code className="inline">codewhale</code> to start. First run creates <code className="inline">~/.codewhale/</code> automatically. Legacy <code className="inline">~/.deepseek/</code> is still read as a compatibility fallback.
See the <Link href="/install" className="body-link">full install guide</Link> for China mirrors, Docker, and troubleshooting.
</p>
</>
@@ -72,7 +73,7 @@ brew tap Hmbown/deepseek-tui && brew install deepseek-tui
<>
Yes. CodeWhale is the new name for what was previously called DeepSeek TUI.
The canonical command is now <code className="inline">codewhale</code>. Legacy <code className="inline">deepseek</code> and <code className="inline">deepseek-tui</code> commands remain as compatibility shims they still work.
Config lives at <code className="inline">~/.deepseek/</code>. <code className="inline">DEEPSEEK_*</code> env vars continue to work.
Config lives at <code className="inline">~/.codewhale/</code>. Legacy <code className="inline">~/.deepseek/</code> config is still read as a compatibility fallback, and <code className="inline">DEEPSEEK_*</code> env vars continue to work.
DeepSeek is not deprecated. The rename reflects CodeWhale's broader mission as the agentic terminal for open models across providers, not a narrowing away from DeepSeek.
</>
),
@@ -90,7 +91,7 @@ export DEEPSEEK_API_KEY=sk-...
codewhale auth set --provider deepseek --api-key sk-...
# Method 3: config.toml
# Add to ~/.deepseek/config.toml:
# Add to ~/.codewhale/config.toml:
api_key = "sk-..."
# Check what's active:
@@ -113,11 +114,11 @@ codewhale doctor # full connectivity check`}
<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>OpenRouter</strong> unified API for DeepSeek models and more.</li>
<li><strong>OpenAI</strong>, <strong>NVIDIA NIM</strong>, <strong>Novita</strong>, <strong>Fireworks</strong>, <strong>sglang</strong>, <strong>vLLM</strong>, <strong>Ollama</strong></li>
<li><strong>OpenAI</strong>, <strong>NVIDIA NIM</strong>, <strong>Volcengine Ark</strong>, <strong>Xiaomi MiMo</strong>, <strong>SiliconFlow</strong>, <strong>Novita</strong>, <strong>Fireworks</strong>, <strong>sglang</strong>, <strong>vLLM</strong>, <strong>Ollama</strong></li>
</ul>
<p>
Set the corresponding env var (e.g. <code className="inline">OPENROUTER_API_KEY</code>) and your provider in <code className="inline">~/.deepseek/config.toml</code>.
Hugging Face, ZenMux, and self-hosted OpenAI-compatible endpoints are on the roadmap.
Set the corresponding env var (e.g. <code className="inline">OPENROUTER_API_KEY</code>) and your provider in <code className="inline">~/.codewhale/config.toml</code>.
Self-hosted OpenAI-compatible endpoints are supported through the provider config.
</p>
</>
),
@@ -131,7 +132,7 @@ codewhale doctor # full connectivity check`}
{`# 1. Set your OpenRouter key
export OPENROUTER_API_KEY=sk-or-v1-...
# 2. In ~/.deepseek/config.toml:
# 2. In ~/.codewhale/config.toml:
[providers.openrouter]
api_key = "sk-or-v1-..."
@@ -213,7 +214,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`}
CodeWhale runs entirely on your machine. No telemetry, no cloud processing of your code.
Sandbox backends: <strong>seatbelt</strong> (macOS), <strong>landlock</strong> (Linux), restricted tokens (Windows).
Workspace boundaries default to <code className="inline">--workspace</code>. <code className="inline">/trust</code> lifts them.
Approval mode is configurable per session. All credential/approval/elevation events are written to <code className="inline">~/.deepseek/audit.log</code>.
Approval mode is configurable per session. All credential/approval/elevation events are written to <code className="inline">~/.codewhale/audit.log</code>.
</>
),
sources: ["SECURITY.md", "docs/ARCHITECTURE.md"],
@@ -222,7 +223,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`}
q: "How do MCP servers work?",
a: (
<>
CodeWhale is a bidirectional MCP client and server. Define servers in <code className="inline">~/.deepseek/mcp.json</code>.
CodeWhale is a bidirectional MCP client and server. Define servers in <code className="inline">~/.codewhale/mcp.json</code>.
Tools appear as <code className="inline">mcp_&lt;server&gt;_&lt;tool&gt;</code>. You can also expose CodeWhale as an MCP server with <code className="inline">codewhale mcp</code>.
See the <Link href="/docs#mcp" className="body-link">docs page</Link> for configuration examples.
</>
@@ -269,7 +270,7 @@ registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"`}
q: "My API key was rejected or I get auth errors on first run.",
a: (
<>
<p className="mb-2">Run <code className="inline">codewhale doctor</code> it checks API key, network, sandbox, and MCP servers. Full report is written to <code className="inline">~/.deepseek/doctor.log</code>.</p>
<p className="mb-2">Run <code className="inline">codewhale doctor</code> it checks API key, network, sandbox, and MCP servers. Full report is written to <code className="inline">~/.codewhale/doctor.log</code>.</p>
<p className="mb-2">Common causes:</p>
<ul className="list-disc pl-5 space-y-1 text-sm text-ink-soft">
<li>Stale <code className="inline">DEEPSEEK_API_KEY</code> in shell startup file open a fresh shell or use <code className="inline">codewhale auth set</code></li>
@@ -352,6 +353,7 @@ npm install -g codewhale
# Cargo Rust 1.88+
cargo install codewhale-cli --locked
cargo install codewhale-tui --locked
# HomebrewmacOS
brew tap Hmbown/deepseek-tui && brew install deepseek-tui
@@ -360,7 +362,7 @@ brew tap Hmbown/deepseek-tui && brew install deepseek-tui
# https://github.com/Hmbown/CodeWhale/releases`}
</pre>
<p>
<code className="inline">codewhale</code> <code className="inline">~/.deepseek/</code>
<code className="inline">codewhale</code> <code className="inline">~/.codewhale/</code> <code className="inline">~/.deepseek/</code> 退
<Link href="/zh/install" className="body-link"></Link> Docker
</p>
</>
@@ -384,7 +386,7 @@ brew tap Hmbown/deepseek-tui && brew install deepseek-tui
a: (
<>
CodeWhale DeepSeek TUI <code className="inline">codewhale</code> <code className="inline">deepseek</code> <code className="inline">deepseek-tui</code>
<code className="inline">~/.deepseek/</code><code className="inline">DEEPSEEK_*</code>
<code className="inline">~/.codewhale/</code> <code className="inline">~/.deepseek/</code> 退<code className="inline">DEEPSEEK_*</code>
DeepSeek CodeWhale 广使 DeepSeek
</>
),
@@ -402,7 +404,7 @@ export DEEPSEEK_API_KEY=sk-...
codewhale auth set --provider deepseek --api-key sk-...
# 3config.toml
# ~/.deepseek/config.toml
# ~/.codewhale/config.toml
api_key = "sk-..."
#
@@ -425,11 +427,11 @@ codewhale doctor # 完整连接检查`}
<ul className="list-disc pl-5 space-y-1 text-sm text-ink-soft mb-3">
<li><strong>DeepSeek</strong> API</li>
<li><strong>OpenRouter</strong> API访 DeepSeek </li>
<li><strong>OpenAI</strong><strong>NVIDIA NIM</strong><strong>Novita</strong><strong>Fireworks</strong><strong>sglang</strong><strong>vLLM</strong><strong>Ollama</strong></li>
<li><strong>OpenAI</strong><strong>NVIDIA NIM</strong><strong>Volcengine Ark</strong><strong>Xiaomi MiMo</strong><strong>SiliconFlow</strong><strong>Novita</strong><strong>Fireworks</strong><strong>sglang</strong><strong>vLLM</strong><strong>Ollama</strong></li>
</ul>
<p>
<code className="inline">OPENROUTER_API_KEY</code> <code className="inline">~/.deepseek/config.toml</code>
Hugging FaceZenMux OpenAI 线
<code className="inline">OPENROUTER_API_KEY</code> <code className="inline">~/.codewhale/config.toml</code>
OpenAI provider
</p>
</>
),
@@ -443,7 +445,7 @@ codewhale doctor # 完整连接检查`}
{`# 1. 设置 OpenRouter 密钥
export OPENROUTER_API_KEY=sk-or-v1-...
# 2. ~/.deepseek/config.toml
# 2. ~/.codewhale/config.toml
[providers.openrouter]
api_key = "sk-or-v1-..."
@@ -525,7 +527,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`}
CodeWhale
<strong>seatbelt</strong>macOS<strong>landlock</strong>LinuxWindows
<code className="inline">--workspace</code><code className="inline">/trust</code>
// <code className="inline">~/.deepseek/audit.log</code>
// <code className="inline">~/.codewhale/audit.log</code>
</>
),
sources: ["SECURITY.md", "docs/ARCHITECTURE.md"],
@@ -534,7 +536,7 @@ default_text_model = "openrouter/deepseek/deepseek-v4-pro"`}
q: "MCP 服务器如何工作?",
a: (
<>
CodeWhale MCP <code className="inline">~/.deepseek/mcp.json</code>
CodeWhale MCP <code className="inline">~/.codewhale/mcp.json</code>
<code className="inline">mcp_&lt;server&gt;_&lt;tool&gt;</code> <code className="inline">codewhale mcp</code> CodeWhale MCP
<Link href="/zh/docs#mcp" className="body-link"></Link>
</>
@@ -581,7 +583,7 @@ registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"`}
q: "首次运行时提示 API 密钥被拒绝或认证错误?",
a: (
<>
<p className="mb-2"> <code className="inline">codewhale doctor</code> API MCP <code className="inline">~/.deepseek/doctor.log</code></p>
<p className="mb-2"> <code className="inline">codewhale doctor</code> API MCP <code className="inline">~/.codewhale/doctor.log</code></p>
<p className="mb-2"></p>
<ul className="list-disc pl-5 space-y-1 text-sm text-ink-soft">
<li>Shell <code className="inline">DEEPSEEK_API_KEY</code> Shell 使 <code className="inline">codewhale auth set</code></li>
+32 -32
View File
@@ -9,12 +9,13 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s
return {
title: isZh ? "安装 · CodeWhale" : "Install · CodeWhale",
description: isZh
? "通过 Cargo 安装 codewhale-cli。其他方式:npm、Homebrew、预编译二进制、Docker、国内镜像。"
: "Install codewhale-cli via Cargo. Other ways: npm, Homebrew, prebuilt binary, Docker, source.",
? "安装 CodeWhale 的 codewhale / codewhale-tui 二进制对。其他方式:npm、Homebrew、预编译二进制、Docker、国内镜像。"
: "Install the matched codewhale / codewhale-tui binary pair. Other ways: npm, Homebrew, prebuilt binary, Docker, source.",
};
}
const CARGO_INSTALL = `cargo install codewhale-cli --locked`;
const CARGO_INSTALL = `cargo install codewhale-cli --locked
cargo install codewhale-tui --locked`;
const FIRST_RUN = `codewhale`;
const VERIFY = `codewhale --version
codewhale doctor`;
@@ -32,22 +33,20 @@ replace-with = "tuna"
[source.tuna]
registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"`;
const TUNA_INSTALL = `cargo install codewhale-cli --locked`;
const TUNA_INSTALL = `cargo install codewhale-cli --locked
cargo install codewhale-tui --locked`;
const NPMMIRROR = `npm config set registry https://registry.npmmirror.com
npm install -g codewhale`;
const BREW = `brew tap Hmbown/deepseek-tui
brew install deepseek-tui`;
const DOCKER = `git clone https://github.com/Hmbown/CodeWhale
cd codewhale
docker build -t codewhale .
const DOCKER = `docker volume create codewhale-home
docker run --rm -it \\
-e DEEPSEEK_API_KEY=$DEEPSEEK_API_KEY \\
-v ~/.deepseek:/home/codewhale/.deepseek \\
-v "$PWD:/work" -w /work \\
codewhale`;
-v codewhale-home:/home/codewhale/.codewhale \\
-v "$PWD:/workspace" -w /workspace \\
ghcr.io/hmbown/codewhale:latest`;
const FROM_SOURCE = `git clone https://github.com/Hmbown/CodeWhale
cd codewhale
@@ -57,7 +56,7 @@ cargo build --release --locked
cargo install --path crates/cli --locked # codewhale
cargo install --path crates/tui --locked # codewhale-tui`;
const CONFIG_TREE = `~/.deepseek/
const CONFIG_TREE = `~/.codewhale/
config.toml api keys, model, hooks, profiles
mcp.json MCP server definitions
skills/ user skills (each with SKILL.md)
@@ -65,9 +64,9 @@ const CONFIG_TREE = `~/.deepseek/
tasks/ background task store
audit.log credential / approval / elevation audit trail
./.deepseek/ project-scoped config (optional, per-repo)`;
./.codewhale/ project-scoped config (optional, per-repo)`;
const CONFIG_TREE_ZH = `~/.deepseek/
const CONFIG_TREE_ZH = `~/.codewhale/
config.toml API
mcp.json MCP
skills/ SKILL.md
@@ -75,7 +74,7 @@ const CONFIG_TREE_ZH = `~/.deepseek/
tasks/
audit.log / /
./.deepseek/ `;
./.codewhale/ `;
export default async function InstallPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
@@ -108,14 +107,15 @@ export default async function InstallPage({ params }: { params: Promise<{ locale
<p className="mt-4 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
<code className="inline">codewhale</code> <code className="inline">~/.cargo/bin</code>
<code className="inline">codewhale</code> <code className="inline">codewhale-tui</code> <code className="inline">~/.cargo/bin</code>
Rust 1.88+访{" "}
<a href="https://rustup.rs" className="body-link">rustup.rs</a>
Rust Homebrew
</>
) : (
<>
Compiles and installs <code className="inline">codewhale</code> to{" "}
Compiles and installs <code className="inline">codewhale</code> and{" "}
<code className="inline">codewhale-tui</code> to{" "}
<code className="inline">~/.cargo/bin</code>. Requires Rust 1.88+ install via{" "}
<a href="https://rustup.rs" className="body-link">rustup.rs</a> if you don&apos;t have it.
See <a href="#other-ways" className="body-link">Other ways to install</a> below for
@@ -139,13 +139,13 @@ export default async function InstallPage({ params }: { params: Promise<{ locale
<>
<code className="inline">codewhale doctor</code> API
MCP {" "}
<code className="inline">~/.deepseek/doctor.log</code>
<code className="inline">~/.codewhale/doctor.log</code>
</>
) : (
<>
<code className="inline">codewhale doctor</code> checks your API key, network,
sandbox availability, and MCP servers. Full report is written to{" "}
<code className="inline">~/.deepseek/doctor.log</code>.
<code className="inline">~/.codewhale/doctor.log</code>.
</>
)}
</p>
@@ -167,16 +167,15 @@ export default async function InstallPage({ params }: { params: Promise<{ locale
Homebrew npm 使
<code className="inline">brew upgrade deepseek-tui</code> {" "}
<code className="inline">npm update -g codewhale</code>
Cargo {" "}
<code className="inline">cargo install codewhale-cli --locked --force</code>
Cargo <code className="inline">cargo install</code> <code className="inline">--force</code>
</>
) : (
<>
Checks GitHub Releases for a newer version and replaces the binary in place. If you
installed via Homebrew or npm, prefer the package manager instead:{" "}
<code className="inline">brew upgrade deepseek-tui</code> or{" "}
<code className="inline">npm update -g codewhale</code>. Cargo users can re-run{" "}
<code className="inline">cargo install codewhale-cli --locked --force</code>.
<code className="inline">npm update -g codewhale</code>. Cargo users can re-run both{" "}
<code className="inline">cargo install</code> commands with <code className="inline">--force</code>.
</>
)}
</p>
@@ -222,7 +221,7 @@ export default async function InstallPage({ params }: { params: Promise<{ locale
<div className="space-y-2">
<InstallCodeBlock cmd={SET_KEY_BASH} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="text-xs text-ink-mute">
{isZh ? "或保存到 ~/.deepseek/config.toml" : "Or persist it to ~/.deepseek/config.toml:"}
{isZh ? "或保存到 ~/.codewhale/config.toml" : "Or persist it to ~/.codewhale/config.toml:"}
</p>
<InstallCodeBlock cmd={SET_KEY_AUTH} copyLabel={copyLabel} copiedLabel={copiedLabel} />
</div>
@@ -265,8 +264,8 @@ export default async function InstallPage({ params }: { params: Promise<{ locale
</h2>
<p className="text-sm text-ink-soft max-w-2xl mb-10">
{isZh
? "如果上面的 Cargo 路径不适合你,从下面找到匹配你情况的一条。每条都安装同一 codewhale 二进制。"
: "If the Cargo path above doesn't fit your setup, pick the row that matches your situation. Every path installs the same codewhale binary."}
? "如果上面的 Cargo 路径不适合你,从下面找到匹配你情况的一条。每条都安装同一 codewhale / codewhale-tui 二进制。"
: "If the Cargo path above doesn't fit your setup, pick the row that matches your situation. Every path installs the same codewhale / codewhale-tui binary pair."}
</p>
<div className="space-y-10">
@@ -372,8 +371,8 @@ export default async function InstallPage({ params }: { params: Promise<{ locale
<InstallCodeBlock cmd={DOCKER} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-3 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh
? "支持 multi-arch buildx。目前没有发布镜像仓库,需要本地构建。"
: "Multi-arch buildx is supported. No image is published to a registry yet, so you build locally."}
? "发布镜像位于 GHCR。需要固定版本时,把 latest 替换成具体的发布标签。"
: "The release image is published to GHCR. Replace latest with a release tag when you need a pinned version."}
</p>
</div>
@@ -403,16 +402,17 @@ export default async function InstallPage({ params }: { params: Promise<{ locale
<p className="mt-4 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
<code className="inline">./.deepseek/</code> MCP
<code className="inline">./.codewhale/</code> MCP
<code className="inline">~/.deepseek</code> <code className="inline">./.deepseek</code> 退
</>
) : (
<>
The project-scoped <code className="inline">./.deepseek/</code> directory is optional
The project-scoped <code className="inline">./.codewhale/</code> directory is optional
each repo can carry its own MCP servers, hooks, skills, and config overrides (e.g.
provider keys). On first run the app asks whether to interactively create a config
file if one is missing.
file if one is missing. Legacy <code className="inline">~/.deepseek</code> and{" "}
<code className="inline">./.deepseek</code> paths are still read as compatibility fallbacks.
</>
)}
</p>
+116 -1
View File
@@ -15,10 +15,70 @@ const FALLBACK_STATS: RepoStats = {
forks: 0,
openIssues: 0,
openPulls: 0,
contributors: 98,
contributors: 141,
fetchedAt: new Date().toISOString(),
};
const RELEASE_CONTRIBUTORS = [
"@cy2311",
"@LING71671",
"@axobase001",
"@dzyuan",
"@mvanhorn",
"@malsony",
"@gaord",
"@yuanchenglu",
"@idling11",
"@h3c-hexin",
"@AdityaVG13",
"@Sskift",
"@cyq1017",
"@HUQIANTAO",
"@New2Niu",
"@AiurArtanis",
"@Lee-take",
"@nightt5879",
"@AresNing",
"@AccMoment",
"@reidliu41",
"@aboimpinto",
"@zhuangbiaowei",
"@donglovejava",
"@hongqitai",
"@zlh124",
"@encyc",
"@Implementist",
"@lihuan215",
"@LeoAlex0",
"@jimmyzhuu",
"@rockyzhang",
"@mo-vic",
"@hufanexplore",
"@hoclaptrinh33",
"@BryonGo",
];
const RELEASE_HELPERS = [
"@buko",
"@yyyCode",
"@gaslebinh-glitch",
"@Dr3259",
"@lpeng1711694086-lang",
"@VerrPower",
"@yan-zay",
"@jretz",
"@Neo-millunnium",
"@caeserchen",
"@T-Phuong-Nguyen",
"@zhyuzhyu",
"@0gl20shk0sbt36",
"@hatakes",
"@goodvecn-dev",
"@bevis-wong",
"@PurplePulse",
"@nbiish",
];
const FALLBACK_DISPATCH_EN: CuratedDispatch = {
generatedAt: new Date().toISOString(),
headline: "Quiet release week — install paths and contributor guides up to date.",
@@ -417,6 +477,61 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
</div>
</section>
{/* RELEASE CREDITS */}
<section className="mx-auto max-w-[1400px] px-6 py-14">
<div className="flex items-baseline gap-4 mb-5 hairline-b pb-4">
<Seal char="谢" />
<div>
<div className="eyebrow mb-2">{isZh ? "v0.8.48 致谢" : "v0.8.48 credits"}</div>
<h2 className="font-display text-3xl">
{isZh ? "每个补丁和报告都算数" : "Every patch and report counts"}
</h2>
</div>
</div>
<div className="grid lg:grid-cols-12 gap-10">
<div className="lg:col-span-5">
<p className={`text-ink-soft ${isZh ? "leading-[1.9] tracking-wide" : "leading-relaxed"}`}>
{isZh
? "这一版合并和吸收了来自社区的大量工作。完整条目在 CHANGELOG 中;这里保留最新发布的公开致谢入口。"
: "This release merged and harvested a large community tranche. The full notes live in the changelog; this keeps the latest public credit surface easy to find."}
</p>
<Link href="https://github.com/Hmbown/CodeWhale/blob/main/CHANGELOG.md#0848---2026-05-31" className="inline-block mt-4 font-mono text-xs uppercase tracking-wider text-indigo hover:underline">
{isZh ? "查看完整 changelog →" : "Full changelog →"}
</Link>
</div>
<div className="lg:col-span-7 grid gap-6">
<div>
<div className="eyebrow mb-3">{isZh ? "已合并 / 已吸收贡献" : "Merged and harvested contributions"}</div>
<div className="flex flex-wrap gap-2">
{RELEASE_CONTRIBUTORS.map((handle) => (
<Link
key={handle}
href={`https://github.com/${handle.slice(1)}`}
className="font-mono text-xs px-2 py-1 hairline-t hairline-b hairline-l hairline-r text-ink-soft hover:text-indigo hover:bg-paper-deep"
>
{handle}
</Link>
))}
</div>
</div>
<div>
<div className="eyebrow mb-3">{isZh ? "报告、复现和验证" : "Reports, repros, and verification"}</div>
<div className="flex flex-wrap gap-2">
{RELEASE_HELPERS.map((handle) => (
<Link
key={handle}
href={`https://github.com/${handle.slice(1)}`}
className="font-mono text-xs px-2 py-1 hairline-t hairline-b hairline-l hairline-r text-ink-soft hover:text-indigo hover:bg-paper-deep"
>
{handle}
</Link>
))}
</div>
</div>
</div>
</div>
</section>
{/* JOIN IN */}
<section className="bg-ink text-paper">
<div className="mx-auto max-w-[1400px] px-6 py-16 grid lg:grid-cols-12 gap-10 items-center">
+6 -6
View File
@@ -27,9 +27,9 @@ const tracksEn = [
{ title: "RLM batched processing", note: "Persistent sandboxed Python REPL with 116 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: "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: "Skills + unified slash palette", note: "~/.deepseek/skills/ auto-loading; /help, /mode, /status, /config, /trust, /feedback" },
{ title: "Durable sessions + tasks", note: "Save, resume, rollback; background task queue with replayable timelines under ~/.codewhale/tasks/" },
{ title: "Bidirectional MCP", note: "Consume tools from external servers; expose as server via `codewhale mcp`; ~/.codewhale/mcp.json" },
{ title: "Skills + unified slash palette", note: "~/.codewhale/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" },
],
@@ -93,9 +93,9 @@ const tracksZh = [
{ title: "RLM 批量处理", note: "持久沙箱 Python REPL,支持 1–16 路廉价并行子调用,处理长文本分析" },
{ title: "三种运行模式", note: "Plan(只读)、Agent(默认)、YOLO(自动批准);审批模式正交(建议/自动/拒绝)" },
{ title: "跨平台沙箱", note: "seatbeltmacOS)、landlockLinux);Windows 通过受限令牌实现基础隔离(功能有限)" },
{ title: "持久化会话 + 后台任务", note: "保存、恢复、回滚;后台任务队列,可回放时间线,位于 ~/.deepseek/tasks/" },
{ title: "双向 MCP 协议", note: "消费外部服务器工具;通过 `deepseek mcp` 暴露为服务器;~/.deepseek/mcp.json" },
{ title: "技能 + 统一命令面板", note: "~/.deepseek/skills/ 自动加载;/help、/mode、/status、/config、/trust、/feedback" },
{ title: "持久化会话 + 后台任务", note: "保存、恢复、回滚;后台任务队列,可回放时间线,位于 ~/.codewhale/tasks/" },
{ title: "双向 MCP 协议", note: "消费外部服务器工具;通过 `codewhale mcp` 暴露为服务器;~/.codewhale/mcp.json" },
{ title: "技能 + 统一命令面板", note: "~/.codewhale/skills/ 自动加载;/help、/mode、/status、/config、/trust、/feedback" },
{ title: "OpenRouter 提供商", note: "原生集成 OpenRouter,支持 300+ 模型,覆盖数十个提供商" },
{ title: "多提供商支持", note: "按会话动态切换提供商(DeepSeek、OpenAI、Anthropic、OpenRouter" },
],
+19 -3
View File
@@ -18,8 +18,8 @@ export interface RepoFacts {
}
export const FACTS: RepoFacts = {
"generatedAt": "2026-05-26T17:03:21.939Z",
"version": "0.8.46",
"generatedAt": "2026-06-01T00:40:33.053Z",
"version": "0.8.48",
"crates": [
"agent",
"app-server",
@@ -30,6 +30,7 @@ export const FACTS: RepoFacts = {
"hooks",
"mcp",
"protocol",
"release",
"secrets",
"state",
"tools",
@@ -69,11 +70,21 @@ export const FACTS: RepoFacts = {
"label": "Wanjie Ark",
"env": "WANJIE_ARK_API_KEY / WANJIE_API_KEY / WANJIE_MAAS_API_KEY"
},
{
"id": "volcengine",
"label": "Volcengine Ark",
"env": "VOLCENGINE_API_KEY / VOLCENGINE_ARK_API_KEY / ARK_API_KEY"
},
{
"id": "openrouter",
"label": "OpenRouter",
"env": "OPENROUTER_API_KEY"
},
{
"id": "xiaomi-mimo",
"label": "Xiaomi MiMo",
"env": "XIAOMI_MIMO_API_KEY / XIAOMI_API_KEY / MIMO_API_KEY"
},
{
"id": "novita",
"label": "Novita AI",
@@ -84,6 +95,11 @@ export const FACTS: RepoFacts = {
"label": "Fireworks AI",
"env": "FIREWORKS_API_KEY"
},
{
"id": "siliconflow",
"label": "SiliconFlow",
"env": "SILICONFLOW_API_KEY"
},
{
"id": "moonshot",
"label": "Moonshot/Kimi",
@@ -107,7 +123,7 @@ export const FACTS: RepoFacts = {
],
"defaultModel": "deepseek-v4-pro",
"nodeEngines": ">=18",
"toolCount": 70,
"toolCount": 75,
"license": "MIT",
"latestRelease": null
};
+3 -32
View File
@@ -1,24 +1,11 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { lastPageFromLink, relativeTime } from "./github";
// We test the pure helper functions directly.
// The async fetch functions require mocking the global fetch.
// ── relativeTime ──────────────────────────────────────────────────────
function relativeTime(iso: string): string {
const diff = Date.now() - +new Date(iso);
const mins = Math.round(diff / 60000);
if (mins < 1) return "just now";
if (mins < 60) return `${mins}m`;
const hrs = Math.round(mins / 60);
if (hrs < 24) return `${hrs}h`;
const days = Math.round(hrs / 24);
if (days < 30) return `${days}d`;
const months = Math.round(days / 30);
if (months < 12) return `${months}mo`;
return `${Math.round(months / 12)}y`;
}
describe("relativeTime", () => {
beforeEach(() => {
vi.useFakeTimers();
@@ -45,7 +32,7 @@ describe("relativeTime", () => {
it("returns days for < 30 days", () => {
expect(relativeTime("2026-05-25T12:00:00Z")).toBe("7d");
expect(relativeTime("2026-05-02T12:00:00Z")).toBe("30d");
expect(relativeTime("2026-05-03T12:00:00Z")).toBe("29d");
});
it("returns months for < 12 months", () => {
@@ -55,28 +42,12 @@ describe("relativeTime", () => {
it("returns years for >= 12 months", () => {
expect(relativeTime("2024-06-01T12:00:00Z")).toBe("2y");
expect(relativeTime("2025-01-01T00:00:00Z")).toBe("2y");
expect(relativeTime("2025-01-01T00:00:00Z")).toBe("1y");
});
});
// ── lastPageFromLink (via re-export test) ──────────────────────────────
function lastPageFromLink(link: string | null): number | undefined {
if (!link) return undefined;
for (const part of link.split(",")) {
const [rawUrl, rawRel] = part
.split(";")
.map((segment: string) => segment.trim());
if (rawRel !== 'rel="last"') continue;
const match = rawUrl.match(/^<(.+)>$/);
if (!match) continue;
const page = new URL(match[1]).searchParams.get("page");
const parsed = page ? Number.parseInt(page, 10) : NaN;
if (Number.isFinite(parsed) && parsed > 0) return parsed;
}
return undefined;
}
describe("lastPageFromLink", () => {
it("returns undefined for null input", () => {
expect(lastPageFromLink(null)).toBeUndefined();
+1 -1
View File
@@ -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 = 99;
const MIN_KNOWN_CONTRIBUTORS = 141;
function headers(token?: string): HeadersInit {
const h: Record<string, string> = {
+3
View File
@@ -70,9 +70,12 @@ function deriveProviders() {
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" },
Volcengine: { id: "volcengine", label: "Volcengine Ark", env: "VOLCENGINE_API_KEY / VOLCENGINE_ARK_API_KEY / ARK_API_KEY" },
Openrouter: { id: "openrouter", label: "OpenRouter", env: "OPENROUTER_API_KEY" },
XiaomiMimo: { id: "xiaomi-mimo", label: "Xiaomi MiMo", env: "XIAOMI_MIMO_API_KEY / XIAOMI_API_KEY / MIMO_API_KEY" },
Novita: { id: "novita", label: "Novita AI", env: "NOVITA_API_KEY" },
Fireworks: { id: "fireworks", label: "Fireworks AI", env: "FIREWORKS_API_KEY" },
Siliconflow: { id: "siliconflow", label: "SiliconFlow", env: "SILICONFLOW_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" },