Files
codewhale/web/app/[locale]/install/page.tsx
T
Hunter B 9f76bcdddd chore(release): polish v0.8.60 web notes and esbuild
Add the v0.8.60 website feature spotlight, refresh install copy, sync the TUI changelog, and pin esbuild 0.28.1 across root/web lockfiles for the Dependabot advisory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 15:12:42 -07:00

476 lines
22 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from "next/link";
import { Seal } from "@/components/seal";
import { InstallCodeBlock } from "@/components/install-code-block";
import { InstallBinary } from "@/components/install-binary";
import { getFacts } from "@/lib/facts";
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const isZh = locale === "zh";
const facts = await getFacts();
const version = facts.version ?? "0.8.60";
return {
title: isZh ? "安装 · CodeWhale" : "Install · CodeWhale",
description: isZh
? `安装 CodeWhale 的 codewhale / codewhale-tui 二进制对。v${version} 支持 npm、Cargo、GitHub Releases、CNB、Homebrew、预编译二进制、Docker、国内镜像。`
: `Install the matched codewhale / codewhale-tui binary pair. For v${version} use npm, Cargo, GitHub Releases, CNB, Homebrew, prebuilt binary, Docker, or source.`,
};
}
const CARGO_INSTALL = `cargo install codewhale-cli --locked
cargo install codewhale-tui --locked`;
const FIRST_RUN = `codewhale`;
const VERIFY = `codewhale --version
codewhale doctor`;
const UPDATE = `codewhale update`;
const SET_KEY_BASH = `export DEEPSEEK_API_KEY=sk-...`;
const SET_KEY_AUTH = `codewhale auth set --provider deepseek --api-key sk-...`;
const TUNA_CONFIG = `# ~/.cargo/config.toml
[source.crates-io]
replace-with = "tuna"
[source.tuna]
registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"`;
const TUNA_INSTALL = `cargo install codewhale-cli --locked
cargo install codewhale-tui --locked`;
const BREW = `brew tap Hmbown/deepseek-tui
brew install deepseek-tui`;
const DOCKER = `docker volume create codewhale-home
docker run --rm -it \\
-e DEEPSEEK_API_KEY=$DEEPSEEK_API_KEY \\
-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
cargo build --release --locked
# Install both binaries from the local checkout
cargo install --path crates/cli --locked # codewhale
cargo install --path crates/tui --locked # codewhale-tui`;
const CONFIG_TREE = `~/.codewhale/
├── config.toml api keys, model, hooks, profiles
├── mcp.json MCP server definitions
├── skills/ user skills (each with SKILL.md)
├── sessions/ checkpoints + offline queue
├── tasks/ background task store
└── audit.log credential / approval / elevation audit trail
./.codewhale/ project-scoped config (optional, per-repo)`;
const CONFIG_TREE_ZH = `~/.codewhale/
├── config.toml API 密钥、模型、钩子、配置集
├── mcp.json MCP 服务器定义
├── skills/ 用户技能(每个含 SKILL.md)
├── sessions/ 检查点 + 离线队列
├── tasks/ 后台任务存储
└── audit.log 凭证 / 审批 / 提权审计日志
./.codewhale/ 项目级配置(可选,每个仓库)`;
export default async function InstallPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const isZh = locale === "zh";
const facts = await getFacts();
const version = facts.version ?? "0.8.60";
const releaseTag = `v${version}`;
const copyLabel = isZh ? "复制" : "Copy";
const copiedLabel = isZh ? "已复制 ✓" : "Copied ✓";
const releaseDownload = `# Download your platform archive:
https://github.com/Hmbown/CodeWhale/releases/tag/${releaseTag}`;
const cnbInstall = `cargo install --git https://cnb.cool/codewhale.net/codewhale --tag ${releaseTag} codewhale-cli --locked --force
cargo install --git https://cnb.cool/codewhale.net/codewhale --tag ${releaseTag} codewhale-tui --locked --force`;
return (
<>
{/* ① INSTALL */}
<section className="mx-auto max-w-[1100px] px-6 pt-12 pb-10">
<div className="flex items-baseline gap-4 mb-3">
<Seal char="装" />
<div className="eyebrow">{isZh ? "01 · 安装" : "01 · Install"}</div>
</div>
<h1 className="font-display tracking-crisp mb-6">
{isZh ? (
<> <span className="font-cjk text-indigo text-5xl ml-2">Install</span></>
) : (
<>Install <span className="font-cjk text-indigo text-5xl ml-2"></span></>
)}
</h1>
<div className="space-y-3">
<InstallCodeBlock cmd={CARGO_INSTALL} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<InstallCodeBlock cmd={FIRST_RUN} copyLabel={copyLabel} copiedLabel={copiedLabel} />
</div>
<p className="mt-4 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
v{version} CargonpmGitHub ReleasesCNBHomebrew
release assets使 GitHub Releases Cargo
</>
) : (
<>
For v{version}, use Cargo, npm, GitHub Releases, CNB, Homebrew, prebuilt binaries,
or mainland China mirrors. If one package manager lags the release assets, use{" "}
<a href="#other-ways" className="body-link">Other ways to install</a> below while
the mirror catches up.
</>
)}
</p>
</section>
{/* ② VERIFY */}
<section className="mx-auto max-w-[1100px] px-6 py-10 hairline-t">
<div className="flex items-baseline gap-4 mb-5">
<Seal char="验" />
<div className="eyebrow">{isZh ? "02 · 验证" : "02 · Verify"}</div>
</div>
<InstallCodeBlock cmd={VERIFY} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-4 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
<code className="inline">codewhale doctor</code> API
MCP {" "}
<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">~/.codewhale/doctor.log</code>.
</>
)}
</p>
</section>
{/* ③ UPDATE */}
<section className="mx-auto max-w-[1100px] px-6 py-10 hairline-t">
<div className="flex items-baseline gap-4 mb-5">
<Seal char="新" />
<div className="eyebrow">{isZh ? "03 · 更新" : "03 · Update"}</div>
</div>
<InstallCodeBlock cmd={UPDATE} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-4 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
GitHub Releases
Homebrew npm 使
<code className="inline">brew upgrade deepseek-tui</code> {" "}
<code className="inline">npm update -g codewhale</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, prefer the package manager instead:{" "}
<code className="inline">brew upgrade deepseek-tui</code>. Cargo users can re-run both{" "}
<code className="inline">cargo install</code> commands with <code className="inline">--force</code>.
</>
)}
</p>
</section>
{/* ④ FIRST RUN */}
<section className="mx-auto max-w-[1100px] px-6 py-10 hairline-t">
<div className="flex items-baseline gap-4 mb-5">
<Seal char="始" />
<div className="eyebrow">{isZh ? "04 · 首次运行" : "04 · First run"}</div>
</div>
<ol className="space-y-6 max-w-2xl">
<li>
<div className="font-display text-lg mb-2">
{isZh ? "① 获取 API 密钥" : "① Get an API key"}
</div>
<p className="text-sm text-ink-soft leading-relaxed">
{isZh ? (
<>
{" "}
<a href="https://platform.deepseek.com" className="body-link">
platform.deepseek.com
</a>{" "}
<code className="inline">sk-...</code>
</>
) : (
<>
Sign up at{" "}
<a href="https://platform.deepseek.com" className="body-link">
platform.deepseek.com
</a>{" "}
and create a key (format: <code className="inline">sk-...</code>).
</>
)}
</p>
</li>
<li>
<div className="font-display text-lg mb-2">
{isZh ? "② 设置密钥" : "② Set the key"}
</div>
<div className="space-y-2">
<InstallCodeBlock cmd={SET_KEY_BASH} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="text-xs text-ink-mute">
{isZh ? "或保存到 ~/.codewhale/config.toml" : "Or persist it to ~/.codewhale/config.toml:"}
</p>
<InstallCodeBlock cmd={SET_KEY_AUTH} copyLabel={copyLabel} copiedLabel={copiedLabel} />
</div>
</li>
<li>
<div className="font-display text-lg mb-2">
{isZh ? "③ 在项目目录中运行" : "③ Run it in a project"}
</div>
<InstallCodeBlock cmd={`cd path/to/project\ncodewhale`} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-3 text-sm text-ink-soft leading-relaxed">
{isZh ? (
<>
Plan {" "}
<kbd className="font-mono text-xs px-1 hairline-t hairline-b hairline-l hairline-r">Tab</kbd>{" "}
Agent YOLO
</>
) : (
<>
Plan mode (read-only) is the default. Press{" "}
<kbd className="font-mono text-xs px-1 hairline-t hairline-b hairline-l hairline-r">Tab</kbd>{" "}
to switch to Agent mode (tool execution, per-action approval). Press again for
YOLO (auto-approve).
</>
)}
</p>
</li>
</ol>
</section>
{/* ⑤ OTHER WAYS TO INSTALL */}
<section id="other-ways" className="bg-paper-deep hairline-t hairline-b">
<div className="mx-auto max-w-[1100px] px-6 py-12">
<div className="flex items-baseline gap-4 mb-5">
<Seal char="备" />
<div className="eyebrow">{isZh ? "05 · 其他安装方式" : "05 · Other ways to install"}</div>
</div>
<h2 className="font-display text-3xl mb-2">
{isZh ? "其他安装方式" : "Other ways to install"}
</h2>
<p className="text-sm text-ink-soft max-w-2xl mb-10">
{isZh
? "如果上面的 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">
{/* Cargo */}
<div>
<div className="eyebrow mb-2 text-indigo">
{isZh ? "Rust 工具链" : "Rust toolchain"}
</div>
<InstallCodeBlock cmd={CARGO_INSTALL} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-3 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
<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>
</>
) : (
<>
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.
</>
)}
</p>
</div>
{/* GitHub Release */}
<div className="rounded-lg border border-ink/12 bg-white/70 p-5">
<div className="font-display text-lg mb-3">{isZh ? "GitHub Releases" : "GitHub Releases"}</div>
<InstallCodeBlock cmd={releaseDownload} copyLabel={copyLabel} copiedLabel={copiedLabel} />
</div>
{/* CNB */}
<div className="rounded-lg border border-ink/12 bg-white/70 p-5">
<div className="font-display text-lg mb-3">{isZh ? "CNB 镜像" : "CNB mirror"}</div>
<InstallCodeBlock cmd={cnbInstall} copyLabel={copyLabel} copiedLabel={copiedLabel} />
</div>
{/* Mainland China network */}
<div>
<div className="eyebrow mb-2 text-indigo">
{isZh ? "中国大陆网络" : "Mainland China network"}
</div>
<p className="text-sm text-ink-soft leading-relaxed max-w-2xl mb-3">
{isZh ? (
<>
Cargo Tuna <code className="inline">~/.cargo/config.toml</code>
</>
) : (
<>
Cargo via Tsinghua Tuna mirror add to{" "}
<code className="inline">~/.cargo/config.toml</code>:
</>
)}
</p>
<InstallCodeBlock cmd={TUNA_CONFIG} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<div className="mt-3">
<InstallCodeBlock cmd={TUNA_INSTALL} copyLabel={copyLabel} copiedLabel={copiedLabel} />
</div>
<p className="mt-4 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
v{version} Cargo + Tuna CNB GitHub
DeepSeek API<code className="inline">api.deepseek.com</code>
</>
) : (
<>
For v{version}, Cargo + Tuna or the CNB path routes around GitHub download
bottlenecks. The DeepSeek API at{" "}
<code className="inline">api.deepseek.com</code> is reachable from mainland China
without a proxy.
</>
)}
</p>
</div>
{/* Homebrew */}
<div>
<div className="eyebrow mb-2 text-indigo">
Homebrew{" "}
<span className="text-ink-mute font-mono normal-case tracking-normal">
· macOS / Linux
</span>
</div>
<InstallCodeBlock cmd={BREW} copyLabel={copyLabel} copiedLabel={copiedLabel} />
</div>
{/* Prebuilt binary */}
<div>
<div className="eyebrow mb-2 text-indigo">
{isZh ? "预编译二进制" : "Prebuilt binary"}{" "}
<span className="text-ink-mute font-mono normal-case tracking-normal">
{isZh ? "· 已自动检测" : "· auto-detected"}
</span>
</div>
<InstallBinary
copyLabel={copyLabel}
copiedLabel={copiedLabel}
verifyHeading={isZh ? "校验 SHA256" : "Verify checksum"}
/>
</div>
{/* Docker */}
<div>
<div className="eyebrow mb-2 text-indigo">Docker</div>
<InstallCodeBlock cmd={DOCKER} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-3 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh
? "发布镜像位于 GHCR。需要固定版本时,把 latest 替换成具体的发布标签。"
: "The release image is published to GHCR. Replace latest with a release tag when you need a pinned version."}
</p>
</div>
{/* From source */}
<div>
<div className="eyebrow mb-2 text-indigo">
{isZh ? "从源码编译" : "From source"}
</div>
<InstallCodeBlock cmd={FROM_SOURCE} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-3 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh
? "适合本地修改 workspace 或贡献补丁。"
: "Useful for hacking on the workspace itself or contributing patches."}
</p>
</div>
</div>
</div>
</section>
{/* ⑥ WHERE CONFIG LIVES */}
<section className="mx-auto max-w-[1100px] px-6 py-12">
<div className="flex items-baseline gap-4 mb-5">
<Seal char="件" />
<div className="eyebrow">{isZh ? "06 · 配置文件在哪" : "06 · Where config lives"}</div>
</div>
<InstallCodeBlock cmd={isZh ? CONFIG_TREE_ZH : CONFIG_TREE} copyLabel={copyLabel} copiedLabel={copiedLabel} />
<p className="mt-4 text-sm text-ink-soft leading-relaxed max-w-2xl">
{isZh ? (
<>
<code className="inline">./.codewhale/</code> MCP
<code className="inline">~/.deepseek</code> <code className="inline">./.deepseek</code> 退
</>
) : (
<>
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. Legacy <code className="inline">~/.deepseek</code> and{" "}
<code className="inline">./.deepseek</code> paths are still read as compatibility fallbacks.
</>
)}
</p>
</section>
{/* ⑦ NEXT STEPS */}
<section className="bg-paper-deep hairline-t hairline-b">
<div className="mx-auto max-w-[1100px] px-6 py-12">
<div className="flex items-baseline gap-4 mb-5">
<Seal char="续" />
<div className="eyebrow">{isZh ? "07 · 下一步" : "07 · Next steps"}</div>
</div>
<div className="grid md:grid-cols-3 gap-0 col-rule hairline-t hairline-b">
<Link
href={isZh ? "/zh/docs" : "/docs"}
className="p-6 hover:bg-paper-deep transition-colors"
>
<div className="font-display text-xl mb-2">Docs</div>
<div className="text-sm text-ink-soft mb-3">
{isZh ? "模式、工具、配置、提供商、MCP" : "Modes, tools, config, providers, MCP"}
</div>
<span className="font-mono text-[0.7rem] uppercase tracking-widest text-indigo">
{isZh ? "阅读文档 →" : "Read docs →"}
</span>
</Link>
<Link
href={isZh ? "/zh/faq" : "/faq"}
className="p-6 hover:bg-paper-deep transition-colors"
>
<div className="font-display text-xl mb-2">FAQ</div>
<div className="text-sm text-ink-soft mb-3">
{isZh ? "安装、配置、模型、提供商等常见问题" : "Common questions on install, config, models, providers"}
</div>
<span className="font-mono text-[0.7rem] uppercase tracking-widest text-indigo">
{isZh ? "查看 FAQ →" : "See FAQ →"}
</span>
</Link>
<Link
href={isZh ? "/zh/roadmap" : "/roadmap"}
className="p-6 hover:bg-paper-deep transition-colors"
>
<div className="font-display text-xl mb-2">Roadmap</div>
<div className="text-sm text-ink-soft mb-3">
{isZh ? "已发布、进行中、考虑中、暂不考虑" : "Shipped, underway, considered, ruled out"}
</div>
<span className="font-mono text-[0.7rem] uppercase tracking-widest text-indigo">
{isZh ? "查看路线图 →" : "View roadmap →"}
</span>
</Link>
</div>
</div>
</section>
</>
);
}