Cline Desktop Sidecar 架构解析:一个 Bun 进程如何把桌面 UI 适配到共享 Cline Hub
2026/9/7 7:57:56 网站建设 项目流程

Cline Desktop Sidecar 架构解析:一个 Bun 进程如何把桌面 UI 适配到共享 Cline Hub

【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline

Cline 桌面应用(@cline/code)的核心枢纽是sidecar/目录下的 sidecar 进程:它作为一个独立的 Bun 进程,负责把桌面 Webview UI 和操作系统级原生操作适配到 Cline 的共享 Hub(Cline Hub)之上。读完本文,你会理解 sidecar 的目录组织、HTTP + WebSocket 传输协议、ClineCore的 Hub 客户端接入方式、工具审批(tool approval)的客户端 Promise 决议机制、完整的命令映射表,以及如何在本地启动 sidecar 进行开发调试。

一、Sidecar 的定位与总体设计

sidecar 是一个Bun 进程,它的职责可以概括为三件事:

  1. 导入@cline/core,发现或启动规范化的共享 Hub(canonical shared Hub);
  2. 作为 Hub 客户端(client)注册到该 Hub;
  3. 通过 HTTP + WebSocket 向前端(Next.js Webview)提供服务。

一个关键的设计约束是:sidecar 不拥有私有的 agent 运行时 Hub。它复用由 CLI 发现的同一个兼容 Hub;如果桌面端是第一个客户端,则由 Core 启动规范化的 detached Hub。这保证了同一个工作区内的多个 Cline 客户端(CLI、桌面端等)收敛到同一个 Hub 进程上。

sidecar 的入口文件是 index.ts,从源码可以看到启动流程的几个关键节点:

  • runEntrypoint()先检查--telemetry-selfcheck参数(用于 CI 校验构建期内联的遥测配置),再检查claimHubDaemonProcess():如果本进程被选为 Hub daemon 宿主,则直接转入@cline/core/hub/daemon-entry,让 sidecar 二进制承担共享 Hub 守护进程的启动职责;
  • 否则进入main():解析工作区根目录、设置 home 目录、启动可观测性、创建SidecarContext、预热工作区元数据、initializeSessionManager()接入 Hub,最后调用startServer()启动 HTTP + WS 服务;
  • 就绪后向 stdout 输出一行 JSON(type: "ready"),携带endpointwsEndpoint(含approval_token查询参数)、pidmode,宿主应用据此把前端指向 sidecar;
  • 进程通过SIGINT/SIGTERM/beforeExit统一走shutdown(),并带 5 秒超时保护;watchManagedHubBuildMismatch()会在其他 Cline 安装(如更新后的 CLI)替换共享 Hub 时向前端广播hub_build_mismatch事件,提示用户更新重启。

二、目录结构与职责划分

ARCHITECTURE.md 中给出的核心目录结构如下:

sidecar/ ├── index.ts # Entry point: starts HTTP+WS server ├── server.ts # Bun HTTP server + WebSocket handlers ├── context.ts # SidecarContext type and factory ├── commands.ts # Command router ├── chat-session.ts # Shared-Hub chat session adapter ├── session-data/ # Shared discovery, messages, artifacts, search helpers ├── paths.ts # Path resolution ├── types.ts # Shared types └── ARCHITECTURE.md # This file

结合当前仓库的实际目录(sidecar/),围绕上述核心还扩展了attachments.ts(附件物化)、mcp.ts/mcp-oauth.ts(MCP 配置与 OAuth)、marketplace.ts(插件市场目录)、observability.ts(日志与遥测)、shell-path.ts(登录 shell PATH 修复)、telemetry-selfcheck.ts等模块,每个模块都配有同名.test.ts测试文件。

几个核心文件的实际职责(以源码为准):

  • types.ts:定义SidecarContext(含liveSessionspendingApprovalspendingQuestionswsClientssessionManagerhubClient等字段),并导出环境配置常量:SIDECAR_PORT = Number(process.env.CLINE_SIDECAR_PORT) || 3126SIDECAR_HOST = process.env.CLINE_SIDECAR_HOST?.trim() || "127.0.0.1"(默认仅监听回环地址,注释说明可通过CLINE_SIDECAR_HOST=0.0.0.0开放给容器端口映射等场景)。
  • context.ts:SidecarContext工厂与ClineCore初始化、Hub 客户端管理、事件路由的所在地。
  • commands.ts:命令路由器handleCommand(ctx, command, args, options),按命令名分发到各子模块。
  • paths.ts:路径解析。resolveWorkspaceRoot()git rev-parse --show-toplevel定位工作区根;sharedSessionDataDir()优先读CLINE_SESSION_DATA_DIR,否则回退到resolveSessionDataDir();MCP 设置路径默认为~/.cline/data/settings/cline_mcp_settings.json(可被CLINE_MCP_SETTINGS_PATH覆盖);会话日志写到~/.cline/apps/kanban/sessions/<sessionId>.jsonl(可被CLINE_KANBAN_DATA_DIR覆盖)。

三、传输协议:Request / Response / Event 三件套

前端与 sidecar 之间走 WebSocket,消息格式固定为三种(文档原文,协议保持不变):

Request: { "type": "command", "id": string, "command": string, "args"?: object } Response: { "type": "response", "id": string, "ok": boolean, "result"?: unknown, "error"?: string } Event: { "type": "event", "event": { "name": string, "payload": unknown } }

在 server.ts 中可以确认这条协议的落地细节:

  • createWebSocketHandler()message()对每条入站消息做JSON.parse,成功后调用handleCommand(ctx, request.command, request.args, { connection: ws }),用jsonResponse(id, true, result)jsonResponse(id, false, undefined, message)回包;
  • 事件编码统一由encodeSidecarEvent(name, payload)(位于 context.ts)完成,即{"type":"event","event":{"name":...,"payload":...}}

/transportWebSocket 端点外,HTTP fetch 处理器还暴露了若干端点:

端点作用
GET /health返回{ ok: true, mode: "sidecar", pid },用于存活探测
POST /shutdown仅信任 origin 可调用,异步触发onShutdownprocess.exit(0)
GET /api/marketplace/catalog返回插件市场目录,失败时返回带error字段的空目录
POST /telemetry/error接收 Webview 的前端错误上报,字段截断后写入 SDK 遥测

安全边界:Origin 校验与 approval token

/transport的 WebSocket 升级有两层门禁:

  1. Origin 白名单TRUSTED_BROWSER_ORIGINS默认包含tauri://localhosthttp://tauri.localhosthttps://tauri.localhosthttp://localhost:3125http://127.0.0.1:3125,可通过CLINE_SIDECAR_TRUSTED_ORIGINS(逗号分隔)追加;
  2. approval tokenstartServer()生成(或读取CLINE_SIDECAR_APPROVAL_TOKEN)一个 token,升级请求必须携带?approval_token=...且通过timingSafeEqual恒定时间比较,才会给该连接标记canApproveTools: true。源码注释明确说明:无 origin 的本地客户端仍然可以连接,但只有携带了有效 token 的浏览器托管桌面 UI 才能接收和决议审批。审批类命令(poll_tool_approvalshub_upgrade等)在 commands.ts 中都会检查options.connection.data.canApproveTools,不满足则直接抛错。

另外,startServer()采用“首选端口 + OS 分配端口(0)”双候选策略:若3126被占用则自动回退,避免端口冲突直接启动失败。

四、核心设计决策

1. 聊天会话 —— 共享 Hub 客户端

sidecar 通过ClineCore以 Hub 模式接入,且不指定显式 endpointClineCore因此复用 CLI 发现的同一个兼容 Hub;当桌面端是第一个客户端时,则启动规范化的 detached Hub。context.ts 中initializeSessionManager()的实际实现与文档示例一致,并额外注入了能力、日志、遥测与特性开关:

const sessionManager = await ClineCore.create({ clientName: "cline-code", backendMode: "hub", capabilities: createSidecarRuntimeCapabilities(ctx), logger: ctx.logger, telemetry: ctx.telemetry, featureFlags: getDesktopFeatureFlagsService({ ... }), hub: { strategy: "require-hub", workspaceRoot: ctx.workspaceRoot, cwd: ctx.workspaceRoot, clientType: "code-sidecar", displayName: "Cline Desktop sidecar", }, });

随后:

  • sessionManager.subscribe((event) => handleCoreSessionEvent(ctx, event))订阅全部会话事件并转发给 WS 客户端;
  • ensureSharedHubClient(ctx, sessionManager.runtimeAddress)再创建第二个NodeHubClientclientType: "code-sidecar-observer"),用ensureCompatibleLocalHubUrl({ strategy: "require-hub", ... })解析 Hub URL 后connect()subscribe(handleHubLiveEvent)。这个观察客户端用于接收 Hub 级事件(如 Agenda 任务审批、task.*生命周期事件、经 Hub 附加的会话流)。

启动发现与加锁机制确保并发客户端收敛到同一个 Hub;编译后的 sidecar 还识别 Core 的 Hub daemon 启动模式(即上文claimHubDaemonProcess()分支),让桌面端在尚无 CLI 进程时也能拉起同一个 detached Hub。

会话事件如何变成前端 chunk?handleAgentEvent()AgentEvent映射为chat_textchat_reasoningchat_tool_call_start/update/endchat_mediachat_core_logchat_usagechat_done等流,统一经emitChunk()广播并同步追加到会话日志文件(每 session 串行化写入,避免同步写阻塞事件循环)。handleHubLiveEvent()则处理观察客户端路径:把assistant.deltareasoning.deltatool.started/updated/finishedrun.started/completed/failed/aborted等 Hub 事件翻译为同样的chat_*chunk,供经 Hub 附加的会话(attachedViaHub)使用。

chat_session_command支持的动作集合在 types.ts 的ChatSessionCommandRequest中定义:startattachsendstopabortforkresetrestore_checkpointpending_promptssteer_promptupdate_pending_promptremove_pending_prompt

2. 工具审批 —— 客户端持有 Promise 决议

共享 Hub 把审批请求路由回创建该会话的客户端。桌面端在 Webview 在线期间用内存 Promise Map 处理审批,context.ts 中的实现(requestSidecarToolApproval)要点:

  • ctx.wsClients中找到第一个canApproveTools === true的连接作为 owner;若没有,直接拒绝并附原因"No trusted desktop approval surface is connected"
  • { item, owner, resolve }存入ctx.pendingApprovals,再向 owner 推送tool_approval_state事件(携带该会话全部待审批项);
  • 前端响应respond_tool_approval命令时 resolve 对应 Promise;owner 断连时cancelSidecarToolApprovalsForOwner()会把它名下的所有 pending 以{ approved: false, reason: "Desktop approval surface disconnected" }决议,防止审批永远挂起;
  • 连接集合变化(开关 WS)时,syncSidecarApprovalReadiness()会向 Hub 更新客户端能力:只要有能审批的 webview 在线,就声明HUB_CLIENT_TOOL_APPROVAL_CAPABILITY("Cline Code has a live user surface for tool review.");
  • 对于经 Hub 触发的任务审批(approval.requested事件),handleHubApprovalRequest()先走同一套桌面审批 Promise,再把结果通过hubClient.command("approval.respond", { approvalId, approved, reason }, sessionId)回传 Hub。

与审批同构的还有ask_question机制:requestSidecarAskQuestion()把问题推送给前端并挂起 Promise,带ASK_QUESTION_TIMEOUT_MS = 5 分钟超时,超时以ask_question_cancelled事件通知前端并 reject。

3. 供应商管理 —— 直接使用 ProviderSettingsManager

import { ProviderSettingsManager, listLocalProviders, ... } from "@cline/core"; const manager = new ProviderSettingsManager();

供应商相关命令(list_provider_cataloglist_provider_modelssave_provider_settingsadd_providerrun_provider_oauth_login)都直接调用@cline/core的本地供应商 API,不经过 Hub。

4. 会话存储 —— 直接使用 SqliteSessionStore

import { SqliteSessionStore, resolveSessionBackend } from "@cline/core"; const store = new SqliteSessionStore();

list_chat_sessions组合SqliteSessionStore与文件发现;update_chat_session_titleresolveSessionBackend().updateSessiondelete_chat_session执行SqliteSessionStore.delete并清理文件产物。

5. 例行计划(Routine Schedules)—— 直连 Hub 命令

例行操作复用与聊天会话观察相同的已连接 Hub 客户端,绝不启动第二个进程内 Hub:

await ctx.hubClient.command("schedule.list", { limit: 200 });

6. 原生命令

  • pick_workspace_directory—— macOS 用osascript、Linux 用zenity弹出目录选择器;
  • open_mcp_settings_file—— 用open/xdg-open打开文件。

7. 前端连接

前端 desktop-client.ts 直接连接 sidecar 的 WebSocket:

  • 优先从window.__SIDECAR_WS_ENDPOINT__发现端点(由 sidecar 的 HTML scaffold 注入);
  • 未注入时回退到默认值ws://127.0.0.1:3126/transport(与 types.ts 中的默认端口一致);
  • 不依赖 Tauri,保持相同的invoke()/subscribe()API。

五、命令映射表(Command Map)

以下命令映射表完整继承自 ARCHITECTURE.md,并可与 commands.ts 中的handleCommand实现一一对应:

Command实现
chat_session_commandClineCore走共享 Hub
list_provider_catalogProviderSettingsManager+listLocalProviders
list_provider_modelsgetLocalProviderModels
save_voice_input_settings校验并持久化所选转写供应商/模型
create_streaming_transcription_session签发短时效、绑定转写用途的浏览器 token,不暴露供应商凭据
transcribe_audio已配置的语音输入选择 + 供应商凭据
save_provider_settingssaveLocalProviderSettings
add_provideraddLocalProvider
run_provider_oauth_loginloginLocalProvider
list_chat_sessionsSqliteSessionStore+ 文件发现
list_discovered_sessions合并发现(merged discovery)
read_session_messages会话数据读取器(commands.ts中默认maxMessages: 800
read_session_hooks会话数据读取器(默认limit: 300
delete_chat_sessionSqliteSessionStore.delete+ 文件清理
update_chat_session_titleresolveSessionBackend().updateSession
list_mcp_servers直接文件 I/O
authorize_mcp_server_oauth显式 Connect 动作 → 可取消的authorizeMcpServerOAuth+ 系统浏览器
cancel_mcp_server_oauth取消挂起的 MCP OAuth 回调等待
upsert_mcp_server直接文件 I/O
delete_mcp_server直接文件 I/O
get_git_branch异步execFile("git", ...)
list_git_branches异步execFile("git", ...)
checkout_git_branch异步execFile("git", ...)
search_workspace_filesgetFileIndex
get_process_context内存上下文(返回 workspaceRoot、平台、app 版本、运行中会话数、Hub 连接状态等)
poll_tool_approvals内存 pending map(需可信连接)
respond_tool_approval内存 Promise 决议
poll_ask_questions内存 pending map
respond_ask_question内存 Promise 决议
list_routine_schedules共享 Hub schedule 命令
list_user_instruction_configs直接 core API
pick_workspace_directoryOS 原生对话框
open_mcp_settings_fileOSopen命令

从源码结构看,handleCommand还包含文档表格未逐一列出的命令,例如proceed_while_running(转发run.proceed_while_runningHub 命令)、list_session_agentshub_upgrade(强制升级托管 Hub,同样要求可信桌面连接)等,阅读 commands.ts 可获得完整清单。

六、开发工作流

文档给出的开发命令(可在 package.json 中核对对应脚本):

bun run dev:headless # Start sidecar and Next.js with a fresh shared approval credential bun run dev:sidecar # Start only the sidecar (no browser approval surface) bun run dev:web # Start only Next.js (no authenticated approval connection) bun run dev # Both concurrently

脚本实际定义:dev:sidecar执行bun run sidecar/index.tsdev:web执行next dev webview -p 3125 --turbo(Next.js 跑在 3125 端口,正好命中 sidecar 的默认信任 origin 列表);dev:headlessscripts/dev-headless.ts以全新共享审批凭据同时拉起 sidecar 与 Next.js;dev则是tauri dev完整桌面壳。

七、关键环境变量速查

综合 types.ts、server.ts 与 paths.ts 的解析逻辑:

环境变量默认值作用
CLINE_SIDECAR_PORT3126sidecar 首选监听端口;被占用时自动回退到 OS 分配端口
CLINE_SIDECAR_HOST127.0.0.1监听地址;设为0.0.0.0可接受外部连接(如 Docker 端口发布),但对外通告的 dial 地址仍为回环
CLINE_SIDECAR_TRUSTED_ORIGINS逗号分隔的额外信任 origin(容器内非标准端口的 dev server 等场景)
CLINE_SIDECAR_APPROVAL_TOKEN运行时随机 UUID审批 token;前端 WS 连接需携带才能标记canApproveTools
CLINE_HUB_PORT显式钉住 Hub 端点;设置后跳过启动期的 build mismatch 检测(该宿主仅保持协议兼容)
CLINE_SESSION_DATA_DIRresolveSessionDataDir()共享会话数据目录
CLINE_MCP_SETTINGS_PATH~/.cline/data/settings/cline_mcp_settings.jsonMCP 设置文件路径
CLINE_TOOL_APPROVAL_DIR<会话数据目录>/tool-approvals工具审批目录(保留用于文件清理兼容)
CLINE_KANBAN_DATA_DIR~/.cline/apps/kanban会话日志(.jsonl)根目录

八、小结

sidecar 的设计可以浓缩为三条主线:传输层保持极简的三消息协议(command/response/event)并叠加 origin 白名单 + approval token 双门禁;运行时层坚持“共享 Hub、不私起 Hub”,通过ClineCorerequire-hub策略)与NodeHubClient观察客户端两条通道接入,让桌面端与 CLI 等客户端共享同一 Hub 实例;交互层把审批、提问等跨端等待统一实现为“推送事件 + 内存 Promise 决议 + 断连自动取消/拒绝”的模式,保证了 UI 离线时 agent 流程不会被永久阻塞。理解了 ARCHITECTURE.md 描述的协议骨架,再对照 index.ts、server.ts、context.ts 与 commands.ts 的实现,即可完整掌握 Cline Desktop sidecar 从进程启动到事件回传的全链路。

【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询