返回 CodeWhale
page.tsx
根目录 / web / app / [locale] / roadmap / page.tsx
1 import Link from "next/link";
2 import { Icon } from "@/components/icon";
3 import { PageHeader, Section } from "@/components/page-header";
4 import { Status, type StatusTone } from "@/components/status-badge";
5 import { getCachedRoadmap, type RoadmapItem } from "@/lib/roadmap-feed";
6 import { getEnv } from "@/lib/kv";
7 import { fill, getRoadmap, pickTextLocale } from "@/lib/i18n/dictionaries";
8 import { buildPageMetadata } from "@/lib/page-meta";
9
10 export const revalidate = 1800;
11
12 export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
13 const { locale } = await params;
14 const t = getRoadmap(locale);
15 return buildPageMetadata({
16 path: "/roadmap",
17 locale,
18 title: t.metaTitle,
19 description: t.metaDescription,
20 });
21 }
22
23 const tracksEn = [
24 {
25 title: "Shipped",
26 items: [
27 { title: "Small searchable toolbox", note: "Every turn starts with read, write, edit, bash, agent, and tool_search. Specialized native, Web, MCP, plugin, memory, task, and verification tools are policy-filtered and searchable; activated schemas stay in a bounded per-conversation toolbox." },
28 { title: "Sub-agent parallel execution", note: "agent; 64 concurrent sessions by default, configurable to 128, with bounded result handles" },
29 { title: "RLM batched processing", note: "Persistent sandboxed Python REPL with 1–16 cheap parallel children for long-input analysis" },
30 { title: "Three operating modes", note: "Plan (read-only authority), Work (execution), and Operate (Fleet/Workflow orchestration) keep one primitive vocabulary; Ask / Auto-Review / Full Access remains an orthogonal approval posture." },
31 { title: "OS command sandbox", note: "Seatbelt on macOS when available; opt-in bubblewrap on Linux when installed. Windows currently reports no OS sandbox." },
32 { title: "Durable sessions + tasks", note: "Save, resume, rollback; background task queue with replayable timelines" },
33 { title: "Bidirectional MCP", note: "Consume tools from external servers; expose as server via `codewhale mcp`" },
34 { title: "Skills + unified slash palette", note: "Skills load automatically from your Codewhale folder; /help, /mode, /status, /config, /trust, /feedback" },
35 { title: "OpenRouter provider", note: "OpenRouter integration with 300+ models across dozens of providers" },
36 { title: "OpenAI-compatible & local runtimes", note: "Generic `openai` route for any OpenAI-compatible gateway, plus vLLM, SGLang, and Ollama against your own localhost endpoints — no key required" },
37 { title: "Multi-provider support", note: "Hot-swap between providers (DeepSeek, OpenAI, Anthropic, OpenRouter) per session" },
38 { title: "Local web client", note: "In published releases: `codewhale web` is a loopback-only browser client over the Runtime API behind a one-time bootstrap session boundary; approvals and user input recover across page reloads" },
39 { title: "Anonymous usage counting", note: "On by default and disclosed at first launch. Turning it off is a saved choice that later versions keep, and showing the notice never records any acceptance on your behalf. While on, sessions post aggregate session, feature, and error counts to a first-party endpoint whose full source is in the repository; it stores no IP, country, or geo data and keeps records for three months. Never conversations, code, prompts, files, file/repo/branch names, model content, credentials, or a per-turn/per-tool timeline. Turn it off with `codewhale config set telemetry false` or CODEWHALE_TELEMETRY=0." },
40 ],
41 },
42 {
43 title: "Underway",
44 items: [
45 { title: "VS Code extension", note: "The repository ships an early local-runtime companion: terminal launch, health checks, read-only thread summaries, and restore-point browsing. Full chat and editor actions are not part of this slice." },
46 { title: "Memory typed store", note: "The native store has shipped: Markdown is the durable source of truth, with a rebuildable SQLite FTS5 index over it. Graph-structured agent memory and multi-signal recall are still ahead" },
47 { title: "Feishu / Lark bot", note: "First long-connection bridge over the runtime API shipped; richer chat features underway" },
48 { title: "Chinese-market & i18n", note: "Locale-aware UI, platform refinements, region-specific search backends" },
49 { title: "Hugging Face model discovery + Model Lab", note: "Browse, download, and manage models from Hugging Face Hub directly in the TUI" },
50 ],
51 },
52 {
53 title: "Considered",
54 items: [
55 { title: "Workrooms", note: "Durable, addressable agent-work threads over the Runtime API and user surfaces" },
56 { title: "Exa web-search backend", note: "Bundled alternative to the existing DDG + Bing path" },
57 { title: "Homebrew core formula", note: "Tap exists; pursuing homebrew-core inclusion" },
58 { title: "Native Windows installer", note: "MSI / WinGet; Scoop manifest already ships" },
59 { title: "Unsloth / NeMo / Arcee fine-tune integration", note: "One-click fine-tuning workflows backed by Unsloth, NVIDIA NeMo, and Arcee toolkits" },
60 ],
61 },
62 {
63 title: "Ruled out",
64 items: [
65 { title: "Silent or content-level telemetry", note: "Codewhale counts anonymous usage by default, discloses it at first launch, and keeps every opt-out. What stays ruled out: conversations, code, prompts, files, model content, credentials, per-turn or per-tool timelines, collection hidden from the user, and any third-party ad or analytics SDK in the runtime binary. A selected hosted provider still receives the context required for its model turn; loopback routes can keep inference local." },
66 { title: "Mandatory hosted relay for local sessions", note: "The local runtime and bring-your-own-provider routes continue to work without sending sessions through a Codewhale service" },
67 { title: "Required account for the local runtime", note: "Installing and running Codewhale locally requires no account" },
68 { title: "Sponsored model promotion", note: "Model picker stays neutral — no paid placement" },
69 { title: "Public share links for local sessions", note: "The retired share-link direction is not coming back; any future sharing design starts fresh." },
70 ],
71 },
72 {
73 title: "Open model platform · ideas, not started",
74 items: [
75 { title: "Community model registry", note: "Discover, share, and rate community fine-tuned models with reproducible recipes" },
76 { title: "One-click deploy", note: "Deploy any model to RunPod, Replicate, or your own infra with a single command" },
77 { title: "Model evaluation dashboard", note: "Transparent, reproducible comparisons across providers, quantization levels, and hardware" },
78 ],
79 },
80 ];
81
82 const tracksZh = [
83 {
84 title: "已完成",
85 items: [
86 { title: "小型可搜索工具箱", note: "每轮以 read、write、edit、bash、agent 与 tool_search 开始。原生专用工具、Web、MCP、插件、记忆、任务和验证工具按策略过滤并可搜索;激活的 schema 保存在有界的会话工具箱中。" },
87 { title: "子 Agent 并行执行", note: "agent;默认 64 个并发会话,可配置到 128 个,通过 var_handle 有界读取结果" },
88 { title: "RLM 批量处理", note: "持久沙箱 Python REPL,支持 1–16 路廉价并行子调用,处理长文本分析" },
89 { title: "三种运行模式", note: "Plan(只读权限)、Work(执行)与 Operate(Fleet / Workflow 编排)使用同一套基础工具名称;Ask、Auto-Review 与 Full Access 审批姿态独立设置。" },
90 { title: "OS 命令沙箱", note: "macOS 在可用时使用 Seatbelt;Linux 在安装后可显式启用 bubblewrap。Windows 当前报告无 OS 沙箱。" },
91 { title: "持久化会话 + 后台任务", note: "保存、恢复、回滚;后台任务队列,可回放时间线" },
92 { title: "双向 MCP 协议", note: "消费外部服务器工具;通过 `codewhale mcp` 暴露为服务器" },
93 { title: "技能 + 统一命令面板", note: "技能从 Codewhale 目录自动加载;/help、/mode、/status、/config、/trust、/feedback" },
94 { title: "OpenRouter 提供商", note: "原生集成 OpenRouter,支持 300+ 模型,覆盖数十个提供商" },
95 { title: "OpenAI 兼容与本地运行时", note: "通用 `openai` 路由可接入任意 OpenAI 兼容网关;vLLM、SGLang、Ollama 直连本地端点,无需密钥" },
96 { title: "多提供商支持", note: "按会话动态切换提供商(DeepSeek、OpenAI、Anthropic、OpenRouter)" },
97 { title: "本地 Web 客户端", note: "已在发布版本中提供:`codewhale web` 是基于 Runtime API 与一次性引导会话边界的回环地址浏览器客户端;审批与用户输入可在页面刷新后恢复" },
98 { title: "匿名使用计数", note: "默认开启,并在首次启动时告知。关闭是会被后续版本保留的选择,显示告知绝不会代你记录任何同意。开启时,会话只会把聚合的会话、功能与错误计数发送到第一方端点,其完整源码在仓库中;它不存储 IP、国家或地理位置,记录保留三个月。永远不收集对话、代码、prompt、文件、文件/仓库/分支名、模型内容、凭据,也不发送逐轮或逐工具时间线。用 `codewhale config set telemetry false` 或 CODEWHALE_TELEMETRY=0 关闭。" },
99 ],
100 },
101 {
102 title: "进行中",
103 items: [
104 { title: "VS Code 扩展", note: "仓库已提供早期本地 Runtime 配套扩展:终端启动、健康检查、只读线程摘要和还原点浏览;完整聊天与编辑器操作尚未包含在此版本中。" },
105 { title: "记忆类型化存储", note: "原生存储已发布:以 Markdown 作为持久化事实来源,并在其上建立可重建的 SQLite FTS5 索引。图结构 Agent 记忆与多信号召回仍在规划中" },
106 { title: "飞书 / Lark 机器人", note: "基于 runtime API 的长连接桥接已发布首版;更丰富的对话能力进行中" },
107 { title: "中国市场与国际化改进", note: "本地化 UI、平台优化、区域搜索引擎" },
108 { title: "Hugging Face 模型发现 + 模型实验室", note: "在 TUI 中直接浏览、下载和管理 Hugging Face Hub 上的模型" },
109 ],
110 },
111 {
112 title: "考虑中",
113 items: [
114 { title: "Workrooms 工作间", note: "基于 Runtime API 与用户界面的持久、可寻址 Agent 工作线程" },
115 { title: "Exa 网页搜索后端", note: "内建替代 DDG + Bing 的搜索路由" },
116 { title: "Homebrew 核心仓库", note: "Tap 已有;正在争取进入 homebrew-core" },
117 { title: "Windows 原生安装器", note: "MSI / WinGet;Scoop 清单已发布" },
118 { title: "Unsloth / NeMo / Arcee 微调集成", note: "一键微调工作流,由 Unsloth、NVIDIA NeMo 和 Arcee 工具链驱动" },
119 ],
120 },
121 {
122 title: "暂不考虑",
123 items: [
124 { title: "静默或内容级遥测", note: "Codewhale 默认开启匿名使用计数,并在首次启动时告知;所有关闭选项始终有效。仍被排除在外的是:对话、代码、prompt、文件、模型内容、凭据、逐轮或逐工具时间线、对用户隐藏的收集,以及运行时二进制中的任何第三方广告或分析 SDK。选用托管 provider 时仍会发送本轮所需上下文,回环地址路由可让推理保持本地。" },
125 { title: "本地会话强制经过托管中继", note: "本地 Runtime 与自带提供商路由继续工作,无需把会话发送到 Codewhale 服务" },
126 { title: "本地 Runtime 强制注册账户", note: "本地安装和运行 Codewhale 不需要账户" },
127 { title: "赞助商模型推广", note: "模型选择器保持中立——无付费推荐位" },
128 { title: "本地会话的公开分享链接", note: "已停用的分享链接方向不会恢复;未来的分享设计将重新开始。" },
129 ],
130 },
131 {
132 title: "开放模型平台 · 构想,尚未开始",
133 items: [
134 { title: "社区模型注册中心", note: "发现、分享和评价社区微调模型,附带可复现的配方" },
135 { title: "一键部署", note: "一条命令将任意模型部署到 RunPod、Replicate 或自有基础设施" },
136 { title: "模型评测面板", note: "跨提供商、量化级别和硬件的透明、可复现对比" },
137 ],
138 },
139 ];
140
141 // Track order is shared by both languages: shipped, underway, considered,
142 // ruled out, and the open model platform direction (not started).
143 const TRACK_TONES: StatusTone[] = ["ready", "accent", "idle", "idle", "idle"];
144
145 const roadmapText = (text: string) =>
146 text.replace(/^>\s*/, "").replaceAll("**", "").replaceAll("CodeWhale", "Codewhale");
147
148 export default async function RoadmapPage({ params }: { params: Promise<{ locale: string }> }) {
149 const { locale } = await params;
150 const t = getRoadmap(locale);
151 const baseTracks = { en: tracksEn, zh: tracksZh }[pickTextLocale(locale)];
152
153 // Live feed: shipped from GitHub Releases; underway/considered/ruled-out from issue labels.
154 // Per-category fallback to the static items so unlabeled categories stay populated.
155 let tracks = baseTracks;
156 try {
157 const env = await getEnv();
158 const feed = await getCachedRoadmap(env.CURATED_KV, env.GITHUB_TOKEN);
159 if (feed) {
160 const liveByCategory: Record<string, RoadmapItem[]> = {
161 Shipped: feed.shipped,
162 Underway: feed.underway,
163 Considered: feed.considered,
164 "Ruled out": feed.ruledOut,
165 已完成: feed.shipped,
166 进行中: feed.underway,
167 考虑中: feed.considered,
168 暂不考虑: feed.ruledOut,
169 };
170 tracks = baseTracks.map((t) => {
171 const live = liveByCategory[t.title];
172 if (live && live.length > 0) {
173 return { ...t, items: live.map((it) => ({ title: it.title, note: it.note })) };
174 }
175 return t;
176 });
177 }
178 } catch {
179 /* keep static fallback */
180 }
181
182 const links = [
183 { title: "Issues", detail: t.issuesDetail, href: "https://github.com/codewhale-hq/CodeWhale/issues" },
184 { title: "Discussions", detail: t.discussionsDetail, href: "https://github.com/codewhale-hq/CodeWhale/discussions/new?category=ideas" },
185 { title: "Pull requests", detail: t.pullsDetail, href: "https://github.com/codewhale-hq/CodeWhale/pulls" },
186 ];
187
188 return (
189 <>
190 <PageHeader kicker={t.eyebrow} title={t.title} lede={t.introduction} pose="browse" />
191
192 <div className="page-body">
193 <Section
194 id="roadmap-status"
195 title={t.sectionTitle}
196 link={
197 <Link href="https://github.com/codewhale-hq/CodeWhale/issues" className="section-link">
198 {t.browseIssues}
199 <Icon name="external" className="icon" />
200 </Link>
201 }
202 >
203 <div className="roadmap-tracks">
204 {tracks.map((track, index) => (
205 <section key={track.title} className="roadmap-track" aria-labelledby={`track-${index}`}>
206 <div className="roadmap-track-head">
207 <h3 id={`track-${index}`}>
208 <Status tone={TRACK_TONES[index] ?? "idle"}>{track.title}</Status>
209 </h3>
210 <span className="page-meta tabular">{fill(track.items.length === 1 ? t.trackCountOne : t.trackCount, { count: track.items.length })}</span>
211 </div>
212 <ul className="group-card" role="list">
213 {track.items.map((item) => (
214 <li key={`${item.title}-${item.note}`} className="group-row roadmap-item">
215 <p className="roadmap-item-title">{roadmapText(item.title)}</p>
216 <p className="roadmap-item-note">{roadmapText(item.note)}</p>
217 </li>
218 ))}
219 </ul>
220 </section>
221 ))}
222 </div>
223 </Section>
224
225 <Section id="roadmap-contribute" title={t.contributeTitle} scope={t.contributeBody}>
226 <ul className="dir-list dir-list-card" role="list">
227 {links.map((link) => (
228 <li key={link.title}>
229 <Link href={link.href} className="dir-row">
230 <span className="dir-mark" aria-hidden="true">
231 <Icon name={link.title === "Pull requests" ? "git-pull-request" : link.title === "Discussions" ? "message" : "alert"} />
232 </span>
233 <span className="dir-text">
234 <span className="dir-title">{link.title}</span>
235 <span className="dir-purpose">{link.detail}</span>
236 </span>
237 <span className="dir-action" aria-hidden="true"><Icon name="external" /></span>
238 </Link>
239 </li>
240 ))}
241 </ul>
242 </Section>
243 </div>
244 </>
245 );
246 }
247
247 lines Plain Text