Files
claude-code/packages/remote-control-server
claude-code-best cd222b8e65 Fixture/flick (#1280)
* fix: 终端内容溢出 viewport 时的重影 bug

主屏幕模式下 frame 持续溢出 viewport 时,cursor-restore LF 把内容滚入 scrollback
导致相对光标追踪漂移,可见区 diff 落到错误行产生重影(重复 banner / 错位)。
扩展 log-update overflow 分支为无条件 fullReset(含 \x1b[3J 清 scrollback),
并将主屏 self-healing 清屏从 ERASE_SCREEN (CSI 2 J) 换成 ERASE_DOWN (CSI J),
避免 xterm.js / VSCode 集成终端的 scrollback 边界副作用。

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

* chore: 删除 3 个孤立诊断脚本

- scripts/verify-autofix-pr.ts: 一次性 autofix-pr 验证脚本,全仓零引用
- scripts/smoke-test-commands.ts: 开发期冒烟测试脚本,无任何 import
- scripts/probe-subscription-endpoints.ts: 手动 endpoint 探针,无引用

均不在 package.json scripts、build.ts、vite.config.ts、CI workflows 中。

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

* chore: 移除 self-hosted-runner stub 及其 cli.tsx fast-path

- 删除 src/self-hosted-runner/main.ts(自动生成的 Promise.resolve() stub)
- 同步移除 src/entrypoints/cli.tsx 中 feature('SELF_HOSTED_RUNNER') 守卫的 fast-path 分支
- 该 flag 不在 build.ts DEFAULT_BUILD_FEATURES 也不在 dev 默认列表,所有默认配置下整段为构建期死代码

删除 stub 单独会留下未解析的动态 import,必须协同拆除。

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

* chore: 删除 agentSdkTypes 中三个 not-implemented stub

移除 watchScheduledTasks、buildMissedTaskNotification、connectRemoteControl 三个 stub 函数(函数体仅 throw new Error('not implemented')),以及仅被这些 stub 引用的孤儿类型(ScheduledTasksHandle、ConnectRemoteControlOptions、RemoteControlHandle、InboundPrompt 等)。

全仓零外部引用。buildMissedTaskNotification 在 src/utils/cronScheduler.ts 有真实可用实现,未受影响。

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

* chore: 移除 Cursor.ts 中未引用的 kill ring 访问器

- 删除 getKillRingItem、getKillRingSize、clearKillRing、canYankPop(全仓零引用的独立 export)
- 移除 VIM_WORD_CHAR_REGEX 的 export 关键字(仍由 isVimWordChar 内部使用,保留常量本体)

kill ring 特性本身仍活跃(getLastKill/pushToKillRing/yankPop 在 useSearchInput/useTextInput 使用),仅这几个孤儿 helper 未接入。

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

* chore: 移除 insights.ts 中未引用的导出

- 删除 deduplicateSessionBranches(全仓零调用,含 JSDoc)
- 删除 buildExportData(全仓零调用,原 S3 上传路径实际用 HTML 而非 JSON)
- InsightsExport 仅移除 export 关键字(保留类型本体,仍作为内部返回类型)

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

* chore: 移除 autonomyCommandSpec.ts 中未引用的导出

- 删除 AUTONOMY_CLI(CLI 子命令描述对象,零引用;handler 仅用 AUTONOMY_USAGE)
- 删除 AUTONOMY_COMMAND_DESCRIPTION(值已在 main.tsx:5181 内联)
- ParsedAutonomyCommand 仅移除 export 关键字(保留类型作为 parseAutonomyArgs 返回类型)

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

* chore: 移除 binaryCheck/claudeAiLimits/codeIndexing 中未引用的导出

- binaryCheck.ts: 删除 clearBinaryCache(零调用,binaryCache 仍由 isBinaryInstalled 使用)
- claudeAiLimits.ts: 删除 RATE_LIMIT_DISPLAY_NAMES 常量 + getRateLimitDisplayName(互为唯一消费者)
- codeIndexing.ts: 删除 detectCodeIndexingFromMcpTool(同胞 detectCodeIndexingFromCommand/McpServerName 仍活跃)

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

* chore: 移除多处仅内部使用的 export 关键字

下列符号均仅在本文件内被引用,export 关键字冗余;保留符号本体不动:

- internalLogging.ts: getContainerId(line 88 内部调用)
- api/errors.ts: isMediaSizeError(line 151 内部调用)
- api/withRetry.ts: parseMaxTokensContextOverflowError(line 389/724 内部调用)
- statsCache.ts: STATS_CACHE_VERSION(7 处内部使用)
- startupProfiler.ts: logStartupPerf(line 128 内部调用)
- bashCommandHelpers.ts: CommandIdentityCheckers(3 处内部参数类型)

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

* chore: 清理注释代码块与 legacy shim

注释代码(已死的、引用不存在符号的注释块):
- Onboarding.tsx: 注释化的 preflight if-block(引用不存在的 preflightStep)
- ultraplan.tsx: 两处引用不存在符号的注释(ULTRAPLAN_INSTRUCTIONS、getUltraplanModel)
- types/hooks.ts: 禁用的 type-fest IsEqual 类型断言块
- types/global.d.ts: 已被真实模块取代的 Ultraplan ambient declares
- types/textInputTypes.ts: 注释化的 onMessage interface 成员

legacy shim:
- cli/bg.ts: 删除 handleBgFlag 别名 export(同胞 handleBgStart 已被所有调用点使用)

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

* chore: 移除 ccshareResume stub 及 main.tsx 的 ccshare fast-path

- 删除 src/utils/ccshareResume.ts(parseCcshareId 恒返回 null、loadCcshare 恒抛错的 stub)
- 同步移除 src/main.tsx 中 USER_TYPE === 'ant' 守卫下的 if (ccshareId) {...} else {...} 双分支
- 提升 else 块(文件路径 resume 处理)为直接进入 if (options.resume) 块内

ccshare 是 Anthropic 内部特性(go/ccshare URL),stub 未实现导致 ccshareId 恒为 null,整个 ccshare 分支永不进入;保留的文件路径 resume 路径不变。

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

* chore: 移除 environment-runner stub 及其 cli.tsx fast-path

与 self-hosted-runner 相同模式的 sibling(工作流 1 verifier 建议同步处理):

- 删除 src/environment-runner/main.ts(自动生成的 Promise.resolve() stub)
- 同步移除 src/entrypoints/cli.tsx 中 feature('BYOC_ENVIRONMENT_RUNNER') 守卫的 fast-path 分支
- 清理两个空目录(src/self-hosted-runner/、src/environment-runner/)

BYOC_ENVIRONMENT_RUNNER flag 不在 build.ts DEFAULT_BUILD_FEATURES 也不在 dev 默认列表,所有默认配置下整段为构建期死代码。

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

* chore: 删除孤立诊断脚本 probe-local-wiring.ts

#!/usr/bin/env bun shebang 的手动诊断脚本,全仓零引用,不在 package.json/build.ts/vite.config.ts/CI workflows 中。

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

* chore: 移除 ultrareview preflight stub 及其测试

- 删除 src/services/api/ultrareviewPreflight.ts(自动生成的 stub)
- 删除 src/commands/review/UltrareviewPreflightDialog.tsx(依赖前者的 UI stub)
- 删除 src/services/api/__tests__/ultrareviewPreflight.test.ts(测试已删代码)
- 同步移除 ultrareviewCommand.test.tsx 中对 UltrareviewPreflightDialog 的 mock

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

* chore: 移除 cachedMCConfig stub 及 prompts.ts 的 CACHED_MICROCOMPACT 死代码

- 删除 src/services/compact/cachedMCConfig.ts(自动生成的 stub)
- 同步移除 src/constants/prompts.ts 中依赖该 stub 的代码:
  - getCachedMCConfigForFRC 变量(feature('CACHED_MICROCOMPACT') 守卫的 require)
  - getFunctionResultClearingSection 函数(约 18 行)
  - systemPrompt 数组中的 frc section 调用与注册

CACHED_MICROCOMPACT 不在 build.ts DEFAULT_BUILD_FEATURES 也不在 dev 默认列表,所有默认配置下整段为构建期死代码。

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

* chore: 移除 goalAudit stub 及其测试引用

- 删除 src/services/goal/goalAudit.ts(导出 COMPLETION_AUDIT_RULES/BLOCKED_AUDIT_RULES/isGoalTerminal 等未引用的 stub)
- 同步移除 tests/integration/goal-lifecycle.test.ts 中对 goalAudit 的 import 和一个测试用例(budget_limited is terminal)

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

* chore: 删除 agentSdkTypes 第二批 not-implemented stub

移除运行时函数体仅为 throw new Error 或 placeholder 的 stub:
- createSdkMcpToolDefinition、createSdkMcpServer
- query 函数重载与实现
- unstable_v2_* 系列函数
- session 操作 stub(getSessionMessages/listSessions/getSessionInfo/renameSession/tagSession/forkSession)
- AbortError 类

保留所有 export type 重导出和类型别名(仍是公共类型面)。

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

* chore: 移除 Tool.ts 中 backwards-compat 重导出 shim

删除 "// Re-export progress types for backwards compatibility" 注释块及其重导出语句。所有消费方已直接从 src/types/tools.js 导入,无需重导出转发。

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

* chore: 移除 bootstrap/state.ts 中 4 个未引用的 export

- clearRegisteredHooks(STATE.registeredHooks 仍由其他函数管理)
- getInvokedSkills(getInvokedSkillsForAgent 是活跃入口)
- getSessionSource(setSessionSource 仍活跃,sessionSource state 字段保留)
- markScrollActivity(scrollDraining/getIsScrollDraining/waitForScrollDrain 仍活跃)

仅删除孤儿访问器,不动模块级 state 副作用。

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

* chore: 移除 src/ 下多处未引用的导出

涉及 18 个文件,每处均为独立的 unreferenced export 删除或 export 关键字冗余移除:

- bridge/bridgeStatusUtil.ts、components/TrustDialog/utils.ts、context/stats.tsx
- keybindings/loadUserBindings.ts、memdir/paths.ts、remote/sdkMessageAdapter.ts
- services/acp/utils.ts(删除 nodeToWebReadable,全仓零引用)
- services/api/metricsOptOut.ts、services/lsp/LSPDiagnosticRegistry.ts、services/lsp/manager.ts
- services/mcp/utils.ts、services/skillLearning/projectContext.ts
- services/teamMemorySync/secretScanner.ts、services/teamMemorySync/watcher.ts
- skills/loadSkillsDir.ts、utils/attachments.ts、utils/filePersistence/filePersistence.ts
- utils/messageQueueManager.ts

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

* chore: 移除 packages/ 下多处未引用的导出

涉及 11 个 workspace 包文件,每处均为独立的 unreferenced export 删除或 export 关键字冗余移除:

- @ant/ink/core/termio/csi.ts(eraseLine)
- acp-link/manager/types.ts、acp-link/ws-message.ts
- builtin-tools/AgentTool/agentMemory.ts、BashTool/bashSecurity.ts、BashTool/sedEditParser.ts
- builtin-tools/ConfigTool/supportedSettings.ts、FileEditTool/utils.ts
- remote-control-server/store.ts、transport/event-bus.ts、types/messages.ts

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

* Revert "fix: 终端内容溢出 viewport 时的重影 bug"

This reverts commit 3d18e1da58.

* revert: 移除主屏幕周期性 self-healing 重绘

回退 f69c7051 中引入的 ink.tsx self-healing 机制(lastMainScreenHealTime 字段
+ 每 5 秒触发全量重绘 + needsEraseBeforePaint 主屏幕分支)。该机制在 workflow
面板持续刷新场景下表现为可见的"重复刷新",且修复效果不稳定。

alt-screen 的 needsEraseBeforePaint 路径和 prevFrameContaminated 字段保留,
它们仍服务于 handleResize / layout shift / selection 高亮。

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

* fix: /workflows 面板默认只显示运行中 run,根治 tab 行乱码

之前几次渲染层修复都失败,因为没动 tab 列表的数据源:打开 /workflows 会
自动 hydrate 最多 20 个历史 done/killed run,全部塞进一行 TabsBar,超出
终端宽度后 Ink 把字符画到屏外造成重影乱码。

- selectors.ts 加 filterActiveRuns(只留 status === 'running')和
  capTabsForDisplay(超额 fold 成 +N)两个 pure function
- WorkflowsPanel 接线 activeRuns:focus clamp、focused、nextTab/prevTab、
  TabsBar 全部基于过滤后的 activeRuns
- TabsBar 复用 truncateLabel 限制每个 tab 名 18 字符 + 最多 6 个 tab,
  多余显示 +N,从结构上钉死单行总宽度

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

* fix: /workflows 面板 phase 状态在脚本省略 phase() 时显示错乱

ultracode canonical pipeline 脚本常在 agent() 直接传 opts.phase 而不调 phase()
hook,导致 phase_started 从未发出;同时 phase_done 只在下次 phase() 触发,上一
个 phase 在 run.phases 里一直停在 running。mergePhases 之前把 actual 当权威,
于是出现 "Map 8/8 全 done 还显示 running、Find 1/4 running 反而显示 pending"。

改为派生层修复:mergePhases 新增 derivePhaseStatus——actual.status==='done'
权威;否则有 agents 就按 agents 状态推(全 done→done,否则 running);否则看
actual 是否 running。再补一层遍历,让只在 agents 上出现的 phase 也进 sidebar。
不改 store 状态语义,已有 state.json 无需迁移。

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

* docs: 更新 readme

* fix: ACP 模式未读取 settings.local.json

entry.ts 在 ACP 握手期调用的 applySafeConfigEnvironmentVariables 触发了
loadSettingsFromDisk,此时 getOriginalCwd() 还是进程启动 cwd(非项目目录),
导致 localSettings/projectSettings 按错误路径解析为空并被 session cache 锁住,
后续 createSession 里 setOriginalCwd 也无法纠正。在 setOriginalCwd 与 chdir
之后清缓存并重新应用,让 settings.local.json 和项目级 env 对
readSettingsPermissionMode 及下游可见。

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-22 09:59:36 +08:00
..
2026-06-22 09:59:36 +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/