Files
claude-code/packages/remote-control-server
claude-code-best 084e487943 feat: 新增 artifacts 功能 (#1278)
* feat: 新增 cloud-artifacts 包(Cloudflare Worker HTML artifact 托管)

POST /upload 鉴权上传 HTML 到 R2 返回 hash URL,GET /<7d|30d>/<id>.html
由 Worker 代理读取并直出 text/html。R2 lifecycle rule 自动 7/30 天删除。
独立服务,不被主 CLI 引用(类似 packages/remote-control-server/ 定位)。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs: 完善 cloud-artifacts 文档并统一出口域名

- CLAUDE.md 加 cloud-artifacts 到 Workspace Packages 表和新增 HTML Artifact Hosting 段落
- docs.json 注册 cloud-artifacts 到运行模式 group
- README 加 Quickstart、架构图(含 Deno Deploy 代理层)、Security Considerations、Troubleshooting
- 统一出口域名为 https://cloud-artifacts.claude-code-best.win(wrangler.toml PUBLIC_URL、test.sh 默认 WORKER_URL、所有文档示例)
- test.sh expect() 加 [via body] fallback:经 Deno Deploy 代理(status 抹平为 200)时按 body 的 error 字段断言

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs: 修正 CLAUDE.md cloud-artifacts 引用死链

之前指向不存在的 docs/features/cloud-artifacts.md(用户未注册到 docs.json),
改为指向已存在的 packages/cloud-artifacts/README.md,并补充生产出口域名
与 Deno Deploy status 抹平副作用的说明。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* chore: 同步 cloud-artifacts 测试默认 TOKEN 到新值

用户已通过 wrangler secret put 把生产 TOKEN 改为 claude-code-best,
test.sh 的默认值(之前用旧 token 作 fallback)和注释示例同步更新。
现在直接 bash scripts/test.sh 即可跑通(无需显式传 TOKEN)。

src/index.ts 不依赖具体 token 值(只读 env.TOKEN 做比较),
wrangler.toml 不含 secret,README/.dev.vars.example 用 <your-token>
占位符故无需改。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* docs: add artifacts feature implementation plan

* feat(artifact): add cloud-artifacts config with token/URL defaults

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): add HTTP client with body-error parsing

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): add tool name, description, and prompt

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): add buildTool definition with file validation

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* test(artifact): add end-to-end tool tests for upload/error paths

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): export ArtifactTool from builtin-tools barrel

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): register ArtifactTool in tools list

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): add /use-artifacts bundled skill

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): add extractArtifacts message scanner

Scans Message[] for artifact tool_use/tool_result pairs, parses URL/id/expires
from the upload response string, and returns ArtifactInfo[] newest-first.

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(artifact): scanner type narrowing and url regex

- Use double assertion (`as unknown as Record<string, unknown>`) at lines 30
  and 90 to fix TS2352 per project convention
- Tighten URL_REGEX to avoid capturing trailing punctuation (parens,
  quotes, commas) when URL is embedded in text
- Add test case for array-form tool_result content path

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): add ArtifactsMenu Ink component

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): add /artifacts slash command entry

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): register /artifacts command

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(artifact): use setClipboard instead of pbcopy for cross-platform support

* fix(artifact): drop userFacingName override so display matches /artifacts

* fix(rcs): add resJson helper to resolve strict mode type errors in tests

Hono Response.json() returns Promise<unknown> under strict TypeScript,
causing 121 TS errors across middleware and routes test files.

Co-Authored-By: glm-5-turbo <zai-org@claude-code-best.win>

* fix(cloud-artifacts): add type stubs so tsc passes without worker-configuration.d.ts

The wrangler-generated worker-configuration.d.ts is gitignored, causing CI to
fail with missing ExportedHandler/Env/R2Bucket types. This file provides minimal
stubs for all Cloudflare Workers types used by the artifact upload Worker.

Co-Authored-By: glm-5-turbo <zai-org@claude-code-best.win>

* fix(query): shallow-copy messages before stripping toolUseResult

Previously the per-query cleanup mutated messagesForQuery entries in
place via `delete msg.toolUseResult`. Those entries are references
shared with mutableMessages (UI state), so the delete stripped the
field from the live message object. The next query can start within
milliseconds of tool_result creation — before the React UI commit
lands — so UserToolSuccessMessage's `!message.toolUseResult` check
returned null and tool.renderToolResultMessage was never called,
leaving tool-result rows blank.

Map to a stripped copy instead so mutableMessages keeps the original
for the UI. Downstream API transformations (applyToolResultBudget,
snip, microcompact) already build new arrays via .map(), so they
compose cleanly with this copy.

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* feat(artifact): show uploaded URL inline below ExecuteExtraTool

Deferred tools (shouldDefer: true) are invoked via SearchExtraTools →
ExecuteExtraTool, so their tool_result rows used to render blank —
the UI looked up ExecuteExtraTool, which had no renderToolResultMessage,
and returned null. Add a generic delegation in ExecuteTool that forwards
renderToolResultMessage to the inner tool when it defines one, unwrapping
the { result, tool_name } envelope and the params from the input shape.
All 28 deferred tools can now render their own UI by defining
renderToolResultMessage.

For ArtifactTool specifically, render the uploaded URL as an OSC 8
hyperlink (Link component) in warning color so it's visually prominent,
with the expiry timestamp on a second line and a separate error branch.
Also add `error: z.string().optional()` to outputSchema — zod's default
strip mode was dropping the field, so error states never reached the UI.

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

* fix(cloud-artifacts): make Env stubs actually take effect in CI

The previous stub file (2e29e362) wrapped `interface Env` in
`declare global { ... }`, but the file has no top-level import/export so
it's a script, not a module. TS2669 forbids `declare global` in scripts,
and in .d.ts files that error is silently swallowed — so the Env stubs
were never merged into the global scope. Locally typecheck passed only
because worker-configuration.d.ts (gitignored) provided Env separately;
in CI / fresh clones, `BUCKET`, `MAX_BYTES`, `DEFAULT_TTL_DAYS`,
`PUBLIC_URL` were all missing on Env.

Drop the wrapper. Top-level `interface Env` in a script .d.ts is already
global ambient and merges with worker-configuration.d.ts via interface
declaration merging, so both environments typecheck cleanly.

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>

---------

Co-authored-by: glm-5.2 <zai-org@claude-code-best.win>
2026-06-20 19:52:08 +08:00
..
2026-06-20 19:52:08 +08:00
2026-05-01 21:39:30 +08:00

Remote Control Server (RCS)

Remote Control Server 是 Claude Code 的远程控制后端,允许你通过浏览器 Web UI 远程监控和操作 Claude Code 会话。

功能

  • 会话管理 — 创建、监控、归档 Claude Code 会话
  • 实时消息流 — WebSocket / SSE 双向传输,实时查看对话和工具调用
  • 权限审批 — 在 Web UI 中审批 Claude Code 的工具权限请求
  • 多环境管理 — 注册多个运行环境,支持心跳和断线重连
  • 认证安全 — API Key + JWT 双层认证

快速开始

Docker 部署(推荐)

docker run -d \
  --name rcs \
  -p 3000:3000 \
  -e RCS_API_KEYS=your-api-key-here \
  -v rcs-data:/app/data \
  ghcr.io/claude-code-best/remote-control-server:latest

环境变量

服务器配置

变量 默认值 说明
RCS_PORT 3000 监听端口
RCS_HOST 0.0.0.0 监听地址
RCS_API_KEYS (空) API 密钥列表,逗号分隔。客户端和 Worker 连接时需要提供
RCS_BASE_URL (自动) 外部访问地址,例如 https://rcs.example.com。用于生成 WebSocket 连接 URL
RCS_VERSION 0.1.0 服务版本号,显示在 /health 响应中

超时与心跳

变量 默认值 说明
RCS_POLL_TIMEOUT 8 V1 轮询超时(秒)
RCS_HEARTBEAT_INTERVAL 20 心跳间隔(秒)
RCS_JWT_EXPIRES_IN 3600 JWT 令牌有效期(秒)
RCS_DISCONNECT_TIMEOUT 300 断线判定超时(秒)

Claude Code 客户端配置

连接到自托管服务器

在 Claude Code 所在环境设置以下变量:

# 指向你的 RCS 服务器地址
export CLAUDE_BRIDGE_BASE_URL="https://rcs.example.com"

# 认证令牌(与 RCS_API_KEYS 中的值对应)
export CLAUDE_BRIDGE_OAUTH_TOKEN="your-api-key-here"

然后启动远程控制模式:

ccb --remote-control

注意:远程控制功能需要启用 BRIDGE_MODE feature flag。开发模式下默认启用。

环境变量参考

变量 说明
CLAUDE_BRIDGE_BASE_URL RCS 服务器地址,覆盖默认的 Anthropic 云端地址
CLAUDE_BRIDGE_OAUTH_TOKEN 认证令牌,用于连接 RCS 服务器
CLAUDE_BRIDGE_SESSION_INGRESS_URL WebSocket 入口地址(默认与 BASE_URL 相同)
CLAUDE_CODE_REMOTE 设为 1 时标记为远程执行模式

Docker Compose 示例

version: "3.8"
services:
  rcs:
    build:
      context: .
      dockerfile: packages/remote-control-server/Dockerfile
      args:
        VERSION: "0.1.0"
    ports:
      - "3000:3000"
    environment:
      - RCS_API_KEYS=sk-rcs-change-me
      - RCS_BASE_URL=https://rcs.example.com
    volumes:
      - rcs-data:/app/data
    restart: unless-stopped

volumes:
  rcs-data:

ACP 兼容的 remote-control

ACP_RCS_URL=http://localhost:3000 ACP_RCS_TOKEN=test-my-key acp-link ccb-bun -- --acp

反向代理配置

使用 Nginx 或 Caddy 反向代理时,需要支持 WebSocket 升级:

server {
    listen 443 ssl;
    server_name rcs.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 86400s;
    }
}

Caddy 配置更简单,自动处理 WebSocket

rcs.example.com {
    reverse_proxy localhost:3000
}

架构概览

┌─────────────┐     WebSocket/SSE      ┌──────────────────┐
│  Claude Code │ ◄──────────────────► │  Remote Control  │
│  (Bridge CLI)│     HTTP API          │     Server       │
└─────────────┘                        │                  │
                                       │  ┌────────────┐  │
┌─────────────┐     HTTP/SSE          │  │ Event Bus   │  │
│  Web UI      │ ◄────────────────── │  └────────────┘  │
│  (/code/*)   │                      │  ┌────────────┐  │
└─────────────┘                       │  │ In-Memory   │  │
                                      │  │ Store       │  │
                                      │  └────────────┘  │
                                      └──────────────────┘
  • 传输层WebSocketV1和 SSE + HTTP POSTV2
  • 存储纯内存存储Map重启后数据清除
  • 认证API Key客户端+ JWTWorker
  • 前端:原生 JS SPA通过 /code/* 路径访问

开发

# 安装依赖
bun install

# 开发模式(热重载)
bun run dev

# 类型检查
bun run typecheck

# 运行测试
bun test packages/remote-control-server/