| 1 | import { Icon, type IconName } from "@/components/icon"; |
| 2 | import { PageHeader, Section } from "@/components/page-header"; |
| 3 | import { Status } from "@/components/status-badge"; |
| 4 | import { getFacts } from "@/lib/facts"; |
| 5 | import { getRuntime, pickText } from "@/lib/i18n/dictionaries"; |
| 6 | import { buildPageMetadata } from "@/lib/page-meta"; |
| 7 | |
| 8 | const REPO_BLOB_BASE = "https://github.com/codewhale-hq/CodeWhale/blob/main"; |
| 9 | |
| 10 | export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) { |
| 11 | const { locale } = await params; |
| 12 | const t = getRuntime(locale); |
| 13 | return buildPageMetadata({ |
| 14 | path: "/runtime", |
| 15 | locale, |
| 16 | title: t.metaTitle, |
| 17 | description: t.metaDescription, |
| 18 | }); |
| 19 | } |
| 20 | |
| 21 | interface Integration { |
| 22 | name: string; |
| 23 | icon: IconName; |
| 24 | experimental?: boolean; |
| 25 | desc: { en: string; zh: string }; |
| 26 | href: string; |
| 27 | } |
| 28 | |
| 29 | const INTEGRATIONS: Integration[] = [ |
| 30 | { |
| 31 | name: "HTTP / SSE Runtime API", |
| 32 | icon: "terminal", |
| 33 | desc: { |
| 34 | en: "Full local HTTP + Server-Sent Events runtime API on 127.0.0.1:7878. Create threads, stream turns, manage background jobs, and control approval decisions — all from any HTTP client or the bundled mobile page.", |
| 35 | zh: "完整的本地 HTTP + Server-Sent Events Runtime API,监听 127.0.0.1:7878。创建线程、流式对话、管理后台任务、控制审批决策——任意 HTTP 客户端或内置手机页面皆可调用。", |
| 36 | }, |
| 37 | href: `${REPO_BLOB_BASE}/docs/RUNTIME_API.md`, |
| 38 | }, |
| 39 | { |
| 40 | name: "ACP (Agent Client Protocol)", |
| 41 | icon: "plug", |
| 42 | desc: { |
| 43 | en: "Baseline JSON-RPC adapter over stdio for compatible editor clients such as Zed. It supports initialize, new session, prompt, and cancel with text responses; shell and file tools, checkpoint replay, and session loading remain on the full Runtime API.", |
| 44 | zh: "面向 Zed 等兼容编辑器客户端的基础 JSON-RPC stdio 适配器。它支持初始化、新建会话、提示和取消,并返回文本响应;shell 与文件工具、检查点回放和会话加载仍由完整 Runtime API 提供。", |
| 45 | }, |
| 46 | href: `${REPO_BLOB_BASE}/docs/RUNTIME_API.md`, |
| 47 | }, |
| 48 | { |
| 49 | name: "MCP (Model Context Protocol)", |
| 50 | icon: "plug", |
| 51 | desc: { |
| 52 | en: "Connect Codewhale to external tools and services through configured MCP servers over stdio or HTTP/SSE, or expose Codewhale's own tools to another MCP client.", |
| 53 | zh: "通过已配置的 MCP 服务器(stdio 或 HTTP/SSE)将 Codewhale 连接到外部工具和服务,或把 Codewhale 自身工具暴露给其他 MCP 客户端。", |
| 54 | }, |
| 55 | href: `${REPO_BLOB_BASE}/docs/MCP.md`, |
| 56 | }, |
| 57 | { |
| 58 | name: "VS Code Extension", |
| 59 | icon: "monitor", |
| 60 | desc: { |
| 61 | en: "Early companion for the local runtime. It can open Codewhale in a terminal, start and check the Runtime API, and show read-only thread summaries and restore points. It does not yet provide full chat, inline edits, or editor actions.", |
| 62 | zh: "本地 Runtime 的早期配套扩展。它可以在终端中打开 Codewhale、启动并检查 Runtime API,以及显示只读线程摘要和还原点;目前尚不提供完整聊天、内联编辑或编辑器操作。", |
| 63 | }, |
| 64 | href: "https://github.com/codewhale-hq/CodeWhale/tree/main/extensions/vscode", |
| 65 | }, |
| 66 | { |
| 67 | name: "CodeWhale GUI (VS Code)", |
| 68 | icon: "monitor", |
| 69 | desc: { |
| 70 | en: "The community-maintained graphical frontend, in a separate repository: chat, threads, live file changes, and task tracking in a VS Code sidebar over this Runtime API. Install it from the VS Code Marketplace; the source is on GitHub.", |
| 71 | zh: "社区维护的图形前端,位于独立仓库:在 VS Code 侧边栏中基于此 Runtime API 进行对话、管理线程、查看实时文件变更与任务进度。可从 VS Code Marketplace 安装;源码见 GitHub。", |
| 72 | }, |
| 73 | href: "https://marketplace.visualstudio.com/items?itemName=HengQuWorld.brotherwhale-vscode", |
| 74 | }, |
| 75 | { |
| 76 | name: "Telegram Bridge", |
| 77 | icon: "message", |
| 78 | desc: { |
| 79 | en: "First-party Telegram bot bridge. Start a headless Codewhale session, then chat with it from any Telegram client — approvals, tool results, and completions surface inline.", |
| 80 | zh: "官方 Telegram 机器人桥接。启动无头 Codewhale 会话,在任何 Telegram 客户端中与之对话——审批、工具结果和完成状态内联展示。", |
| 81 | }, |
| 82 | href: "https://github.com/codewhale-hq/CodeWhale/tree/main/integrations/telegram-bridge", |
| 83 | }, |
| 84 | { |
| 85 | name: "Feishu / Lark Bridge", |
| 86 | icon: "message", |
| 87 | desc: { |
| 88 | en: "First-party Feishu / Lark bot bridge. Chat-native agent loop inside your Feishu workspace with approval cards, session linking, and audit trail.", |
| 89 | zh: "官方飞书 / Lark 机器人桥接。在飞书工作区内实现聊天原生 Agent 循环,支持审批卡片、会话关联和审计日志。", |
| 90 | }, |
| 91 | href: "https://github.com/codewhale-hq/CodeWhale/tree/main/integrations/feishu-bridge", |
| 92 | }, |
| 93 | { |
| 94 | name: "Weixin Bridge", |
| 95 | icon: "message", |
| 96 | experimental: true, |
| 97 | desc: { |
| 98 | en: "Experimental Weixin / WeChat bridge. Receive agent completions and approvals inside WeChat; early-stage and not recommended for production deployments.", |
| 99 | zh: "实验性微信桥接。在微信中接收 Agent 完成通知和审批;早期阶段,不建议用于生产环境。", |
| 100 | }, |
| 101 | href: "https://github.com/codewhale-hq/CodeWhale/tree/main/integrations/weixin-bridge", |
| 102 | }, |
| 103 | ]; |
| 104 | |
| 105 | const TRUST = [ |
| 106 | { |
| 107 | title: { en: "Runs on your machine", zh: "本机运行" }, |
| 108 | body: { |
| 109 | en: "The Runtime API binds 127.0.0.1 by default. The local runtime does not require a Codewhale account or hosted relay.", |
| 110 | zh: "Runtime API 默认仅监听 127.0.0.1。本地运行时不需要 Codewhale 账户或托管中继。", |
| 111 | }, |
| 112 | }, |
| 113 | { |
| 114 | title: { en: "Auth required", zh: "认证必需" }, |
| 115 | body: { |
| 116 | en: "Runtime API routes (/v1/*) require a Bearer token. Pass --auth-token at startup or set CODEWHALE_RUNTIME_TOKEN. Only a loopback bind can turn this off, with --insecure-no-auth.", |
| 117 | zh: "Runtime API 路由(/v1/*)需要 Bearer Token。启动时传入 --auth-token,或设置 CODEWHALE_RUNTIME_TOKEN 环境变量。仅在回环地址上可用 --insecure-no-auth 关闭认证。", |
| 118 | }, |
| 119 | }, |
| 120 | { |
| 121 | title: { en: "Permissions user-controlled", zh: "权限用户控制" }, |
| 122 | body: { |
| 123 | en: "Remote clients submit requests and approval decisions through the authenticated Runtime API. Local mode, permission posture, and sandbox policy still apply.", |
| 124 | zh: "远程客户端通过经过认证的 Runtime API 提交请求与审批决定。本地模式、权限姿态和沙箱策略仍然生效。", |
| 125 | }, |
| 126 | }, |
| 127 | { |
| 128 | title: { en: "Open protocols", zh: "开放协议" }, |
| 129 | body: { |
| 130 | en: "The HTTP/SSE Runtime API, MCP surface, and baseline ACP stdio adapter serve different integration needs; choose the interface your compatible client supports.", |
| 131 | zh: "HTTP/SSE Runtime API、MCP 和基础 ACP stdio 适配器分别服务于不同集成场景;请根据客户端需要选择对应接口。", |
| 132 | }, |
| 133 | }, |
| 134 | ]; |
| 135 | |
| 136 | export default async function RuntimePage({ params }: { params: Promise<{ locale: string }> }) { |
| 137 | const { locale } = await params; |
| 138 | const t = getRuntime(locale); |
| 139 | const facts = await getFacts(); |
| 140 | |
| 141 | return ( |
| 142 | <> |
| 143 | <PageHeader |
| 144 | seal="接" |
| 145 | kicker={t.kicker} |
| 146 | title={t.title} |
| 147 | titleAside={t.titleAside} |
| 148 | titleAsideLang={t.titleAsideLang} |
| 149 | lede={t.lede} |
| 150 | pose="run" |
| 151 | /> |
| 152 | |
| 153 | <div className="page-body"> |
| 154 | <Section id="runtime-integrations" seal="集" title={t.integrationsTitle}> |
| 155 | <ul className="dir-list dir-list-card" role="list"> |
| 156 | {INTEGRATIONS.map((item) => ( |
| 157 | <li key={item.name}> |
| 158 | <a href={item.href} target="_blank" rel="noopener noreferrer" className="dir-row"> |
| 159 | <span className="dir-mark" aria-hidden="true"><Icon name={item.icon} /></span> |
| 160 | <span className="dir-text"> |
| 161 | <span className="dir-title">{item.name}</span> |
| 162 | <span className="dir-purpose">{pickText(item.desc, locale)}</span> |
| 163 | </span> |
| 164 | {item.experimental ? <Status tone="attention">{t.experimental}</Status> : null} |
| 165 | <span className="dir-action" aria-hidden="true"><Icon name="external" /></span> |
| 166 | </a> |
| 167 | </li> |
| 168 | ))} |
| 169 | </ul> |
| 170 | </Section> |
| 171 | |
| 172 | <Section id="runtime-trust" layout="split" seal="信" title={t.trustTitle}> |
| 173 | <dl className="ruled-list ruled-list-stacked"> |
| 174 | {TRUST.map((item) => ( |
| 175 | <div key={item.title.en}> |
| 176 | <dt>{pickText(item.title, locale)}</dt> |
| 177 | <dd>{pickText(item.body, locale)}</dd> |
| 178 | </div> |
| 179 | ))} |
| 180 | </dl> |
| 181 | </Section> |
| 182 | |
| 183 | <Section id="runtime-facts" layout="split" seal="数" title={t.factsTitle}> |
| 184 | <dl className="ruled-list"> |
| 185 | <div> |
| 186 | <dt>{t.version}</dt> |
| 187 | <dd className="tabular">{facts.version ?? "—"}</dd> |
| 188 | </div> |
| 189 | <div> |
| 190 | <dt>{t.toolCount}</dt> |
| 191 | <dd className="tabular">{facts.toolCount ?? "—"}</dd> |
| 192 | </div> |
| 193 | <div> |
| 194 | <dt>{t.sandboxBackends}</dt> |
| 195 | <dd>{facts.sandboxBackends.length ? facts.sandboxBackends.join(" · ") : "—"}</dd> |
| 196 | </div> |
| 197 | </dl> |
| 198 | {/* Crate names and the source revision are for maintainers; they |
| 199 | sit behind Details. */} |
| 200 | <details className="group-card disclosure-row mt-4"> |
| 201 | <summary> |
| 202 | {t.details} |
| 203 | <Icon name="chevron-down" className="disclosure-chevron" /> |
| 204 | </summary> |
| 205 | <dl className="disclosure-body runtime-details"> |
| 206 | <div> |
| 207 | <dt>Crates</dt> |
| 208 | <dd>{facts.crates.length ? `${facts.crates.length} · ${facts.crates.join(", ")}` : "—"}</dd> |
| 209 | </div> |
| 210 | <div> |
| 211 | <dt>{t.sourceRevision}</dt> |
| 212 | <dd><code className="inline">{facts.sourceRevision ?? "—"}</code></dd> |
| 213 | </div> |
| 214 | </dl> |
| 215 | </details> |
| 216 | </Section> |
| 217 | |
| 218 | <section className="page-section"> |
| 219 | <p className="status-line"> |
| 220 | <span>{t.docsLead}</span> |
| 221 | <a href={`${REPO_BLOB_BASE}/docs/RUNTIME_API.md`} target="_blank" rel="noopener noreferrer" className="link"> |
| 222 | {t.runtimeApiDoc} |
| 223 | </a> |
| 224 | <a href={`${REPO_BLOB_BASE}/docs/MCP.md`} target="_blank" rel="noopener noreferrer" className="link"> |
| 225 | {t.mcpDoc} |
| 226 | </a> |
| 227 | </p> |
| 228 | </section> |
| 229 | </div> |
| 230 | </> |
| 231 | ); |
| 232 | } |
| 233 |