Cline Debug Harness:以 HTTP API 驱动 VSCode 扩展调试与 UI 自动化的实战指南
本文基于 Cline 仓库中 VSCode 扩展的 Debug Harness(apps/vscode/src/dev/debug-harness/)展开。它是 Cline 团队为 VSCode 扩展自研的"HTTP 控制的调试服务器":通过一个常驻的 HTTP API,你可以用 curl 对扩展宿主进程(Node.js)打断点、对 Webview 打断点、用 Playwright 做 UI 自动化、拦截 openExternal() 实现的浏览器 URL 捕获,从而完成 Cline OAuth / MCP OAuth / Provider OAuth 等登录流程的端到端测试。读完后,你将掌握完整的启动方式、全部 API 方法、OAuth 测试的三条流程,以及断点 sourcemap 解析、数据隔离等底层实现原理。
一、Debug Harness 是什么
Debug Harness 是一个 HTTP 控制的调试服务器,专为 Cline VSCode 扩展设计,提供以下五类编程化能力:
- 扩展宿主调试(Node.js):通过 CDP 对扩展宿主进程打断点、求值(evaluate)、单步、暂停/恢复;
- Webview 调试(Chrome):通过 CDP 对 Webview 打断点、求值;
- UI 自动化:基于 Playwright 的点击、输入、截图、打开侧边栏;
- Sourcemap 解析:按"原始源码文件 + 行号"设置断点(自动反解到打包后的位置);
- 数据隔离:被调试方(debugee)使用独立的
~/.cline2配置目录,避免干扰调试方; - OAuth 测试:浏览器 URL 捕获、token 检查、回调模拟。
它的核心设计意图是可以被 Agent 循环驱动——所有交互都是一条 curl 命令,天然适合脚本化与自动化测试。
整个实现集中在 server.ts(约 1600 行),配套的 MCP OAuth 测试服务器位于 mcp-oauth-test-server/server.ts。
二、快速开始
2.1 最小启动方式
# 终端 1:启动 debug harness 服务器
# 注意:必须用 node 运行,不能用 bun ——
# Playwright 的 _electron.launch() 在 bun 下会超时
node src/dev/debug-harness/server.ts --auto-launch --skip-build
# 终端 2:用 curl 交互
curl localhost:19229/api -d '{"method":"status"}'
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
curl localhost:19229/api -d '{"method":"ui.screenshot"}'
"必须用 node 而非 bun" 这条约束在 server.ts 的文件头注释中有明确解释:Electron 进程在 bun 下虽然能启动,但 _electron.launch() 永远无法附着成功而超时;同样的启动在 node 下正常。另外文件头注明 Node >= 22.6 可直接以 type stripping 方式运行该 .ts 文件。
2.2 服务器命令行参数
node src/dev/debug-harness/server.ts [options]
Options:
--skip-build 跳过扩展/webview 构建(使用现有 dist/)
--auto-launch 启动时自动拉起 VSCode
--workspace PATH 要打开的工作区目录(默认 /tmp/cline-debug-workspace)
--port PORT 服务器端口(默认 19229)
--cline-dir PATH 覆盖被调试方的 CLINE_DIR(默认 ~/.cline2)
源码中实际还实现了文档未列出的两个选项,从 server.ts 的配置常量区可以确认:
--launch-timeout MS:Playwright_electron.launch的超时时间,默认 120000ms。文件头注释解释:冷启动(首次 profile 初始化、慢速 CI/VM)可能超过旧的 60s 默认值,因此放宽并允许覆盖;--no-browser-capture:让openExternal()打开真实浏览器窗口(用于交互式 OAuth 测试),默认是关闭捕获的。
另外环境变量 VSCODE_TEST_VERSION 可以指定被下载的 VSCode 版本,默认 1.103.0——源码注释指出捆绑的 Playwright 版本无法驱动最新 "stable" 的 Electron,因此默认钉住该版本,需要时可设为 "stable" 或具体版本号。
2.3 首次完整构建 + 启动
# 会构建 protos、扩展(不压缩 + sourcemap)、webview(不压缩 + sourcemap),
# 下载 VSCode、启动它,并通过 CDP 连上扩展宿主
node src/dev/debug-harness/server.ts --auto-launch
三、数据隔离:~/.cline2 的原理
被调试方默认以 CLINE_DIR=~/.cline2 运行,与你的真实 ~/.cline 完全分开。这防止了三类问题:
- 被调试方登出时把调试方一起登出;
- 任务历史、API key、设置在两个实例之间互相泄漏;
- 共享
secrets.json导致的状态损坏。
隔离目录会在 status() 和 launch() 的响应中报告:
curl localhost:19229/api -d '{"method":"status"}'
# → { "clineDir": "/Users/you/.cline2", ... }
想换目录时用 --cline-dir /tmp/test-cline-dir。
源码级原理
server 侧的隔离在 server.ts 中定义:DEFAULT_CLINE_DIR 硬编码为 ~/.cline2,并在 _electron.launch 时通过 CLINE_DIR 环境变量注入被调试方环境(CLINE_DIR_ARG 可覆盖)。
被调试方一侧的读取链路则在 storage-context.ts 中:resolveDataDirFromEnv() 按 CLINE_DATA_DIR(去空白)> CLINE_DIR + "/data" > ~/.cline/data 的优先级解析数据目录;随后 createStorageContext() 基于该目录生成所有存储文件的绝对路径——globalState.json、secrets.json(文件权限 0o600,注释明确说明是为了保护 API key)、以及按工作区路径哈希隔离的 workspaces/<hash>/workspaceState.json。由于所有读写都收敛到这一个解析器,只要 harness 注入了 CLINE_DIR,被调试方的全部状态就落在独立目录中。
四、浏览器捕获与 OAuth 测试
4.1 URL 捕获机制
Debug Harness 启动 VSCode 时设置 CLINE_CAPTURE_BROWSER=1,这会拦截被调试方所有的 openExternal() 调用。URL 不再打开真实浏览器,而是被三重处理:
- 落盘:追加到
$CLINE_DIR/data/debug-captured-urls.jsonl; - 实时上报:POST 到 debug harness 服务器的
/captured-url端点; - 可查询:通过
oauth.captured_urlsAPI 方法读取。
这段逻辑的实现就在 env.ts 的 openExternal() 中:当 CLINE_CAPTURE_BROWSER 为 "1" 或 "true" 时走 captureBrowserUrl() 分支——先把 {timestamp, url} 以 JSONL 格式追加到 $CLINE_DIR/data/debug-captured-urls.jsonl(CLINE_DIR 未设时回退到 ~/.cline),再若检测到 CLINE_DEBUG_HARNESS_PORT 环境变量,则以 fire-and-forget 方式 POST 到 127.0.0.1:<port>/captured-url。注意这里捕获是"只记不改":非捕获模式下 openExternal 走 host bridge RPC,失败时回退到 open npm 包(JetBrains 等宿主的路径)。
4.2 OAuth API 一览
| 方法 | 参数 | 说明 |
|---|---|---|
oauth.captured_urls |
{clear?} |
获取被调试方试图打开的 URL(由浏览器拦截捕获) |
oauth.read_stored_token |
读取被调试方 secrets.json 中的 auth token 是否存在 | |
oauth.simulate_callback |
{path, code?, state?, provider?, token?} |
构造 vscode:// 回调 URI(用于 MCP/Provider OAuth) |
oauth.read_captured_urls_file |
读取磁盘上的捕获 URL JSONL 文件 |
4.3 测试 Cline OAuth(登录流程)
Cline OAuth 流程使用 SDK 的本地回调服务器。用户点击 "Login" 后,SDK 依次:
- 在随机端口启动本地 HTTP 服务器;
- 调用
openExternal(authorizationUrl)——被我们捕获; - 用户在浏览器中认证——这一步需要我们来模拟;
- Provider 带着
?code=...重定向回本地回调服务器; - SDK 捕获 code 并交换为 token。
完整测试步骤:
# 1. 在被调试方侧边栏点击 "Login"
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
# 先关闭弹窗(见下文 "Dismissing Promotional Overlays")
curl localhost:19229/api -d '{"method":"ui.locator","params":{"text":"Login to Cline","frame":"sidebar","action":"click"}}'
# 2. 检查捕获的 URL,找到 authorization URL
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# → { "urls": [{ "url": "https://api.cline.bot/auth/authorize?callback_url=http://127.0.0.1:PORT/..." }] }
# 3. authorization URL 中的 callback_url 指向 SDK 本地服务器。
# 要完成流程,二选一:
# a. 在真实浏览器中打开 authorization URL(会自动重定向回 SDK 本地回调服务器)
# b. 提取 callback_url,带 code 参数直接 curl 模拟重定向:
curl "http://127.0.0.1:PORT/auth/callback?code=TEST_CODE" 2>/dev/null
# 4. 验证 token 已存储
curl localhost:19229/api -d '{"method":"oauth.read_stored_token"}'
# → { "found": true, "hasAccountId": true, "keys": ["cline:clineAccountId"] }
# 5. 截图验证 UI 显示已认证状态
curl localhost:19229/api -d '{"method":"ui.screenshot"}'
注意文档同时提醒:伪造 code 会导致 token 交换失败(provider 不认识该 code),你需要真实授权码(在浏览器完成流程获得)或者 mock 掉 token 交换端点。
4.4 测试 MCP OAuth
需要 OAuth 的 MCP server 走的是另一条流程:浏览器重定向到由扩展 URI handler 处理的 vscode:// URI。授权方(如 Linear)决定 code;端到端测试时搭配本地 MCP OAuth 测试服务器(bun run dev:mcp-oauth-test-server,详见 mcp-oauth-test-server/README.md)——它是一个零依赖(仅 Node http)服务器,同时扮演 OAuth 2.0 授权服务器(RFC 8414 / RFC 7591 DCR / RFC 7636 PKCE)和 MCP StreamableHTTP 资源服务器两个角色,能签发真实的 code/token,还支持 --slow-authorize、--auto-deny 等选项用于复现 state 过期、拒绝授权等失败模式。
# 1. 触发 MCP OAuth(例如点击某个 server 的 "Authenticate" 按钮)
# 2. 在捕获的 URL 中找 authorization URL
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# authorize URL 含 redirect_uri=vscode://saoudrizwan.claude-dev/mcp-auth/callback/HASH
# 3. 从授权服务器获取真实授权码:跟随捕获的 authorize URL
# (测试服务器自动批准并 302 到 vscode:// 回调,带 ?code=...&state=...):
curl -s -D - -o /dev/null "<captured-authorize-url>" | grep -i '^location:'
# 4. 把 vscode:// 回调投递给扩展。VSCode 只会把真实 vscode:// URI
# 路由给已注册的 handler,而 harness 无法合成它——且扩展宿主是 ESM,
# 无法 require() handler 模块。因此调用 __clineHandleUri 钩子(见下):
curl localhost:19229/api -d '{
"method": "ext.evaluate",
"params": {
"awaitPromise": true,
"expression": "globalThis.__clineHandleUri(\"vscode://saoudrizwan.claude-dev/mcp-auth/callback/HASH?code=REAL_CODE&state=SAVED_STATE\")"
}
}'
# 5. 验证 token 已存储
curl localhost:19229/api -d '{"method":"oauth.read_stored_token"}'
globalThis.__clineHandleUri(url)— 仅限调试的 URI 投递钩子。 它在扩展激活期间注册于 extension.ts,仅当CLINE_CAPTURE_BROWSER设置时才注册(harness 总会设置它),因此绝不会进入生产构建。它调用与 VSCode 真实registerUriHandler相同的SharedUriHandler.handleUri(url),返回Promise<boolean>(配合awaitPromise: true使用)。可用于任何vscode://回调——MCP、OpenRouter、/auth等。oauth.simulate_callback只负责构造 URI,而该钩子负责真正投递它。
源码验证:extension.ts 中该钩子的注册条件正是 process.env.CLINE_CAPTURE_BROWSER === "1" || "true",且实现就是一行 SharedUriHandler.handleUri(url) 的委托——与真实 handler(同文件 L175 的 vscode.window.registerUriHandler)调用的是同一个方法。
4.5 测试 Provider OAuth(OpenRouter 等)
# 1. 触发 provider 登录(例如 "Get OpenRouter API Key" 按钮)
# 2. 检查捕获的 URL
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# 3. 模拟重定向回调
curl localhost:19229/api -d '{
"method": "oauth.simulate_callback",
"params": {"path": "/openrouter", "code": "TEST_CODE"}
}'
五、实战技巧
5.1 关闭促销弹窗
全新启动时可能出现一个或多个全屏促销弹窗(如 "Introducing Cline Kanban"),会挡住侧边栏的全部交互,让截图毫无用处。必须在打开侧边栏后立即、且在任何其他交互之前关闭它们:
# 先打开侧边栏
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
# 关闭所有弹窗(多个弹窗可能需要执行两次)
curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
这段表达式遍历所有 sr-only(屏幕阅读器专用、视觉隐藏)元素并点击其父级——即每个弹窗的关闭按钮所在容器。
5.2 用命令在视图间导航
与其在侧边栏头部找小图标点击,不如通过命令面板执行 VSCode 命令。这些命令注册在 registry.ts 中:
| 命令 | 打开的视图 |
|---|---|
cline.accountButtonClicked |
账户 / 登录视图 |
cline.historyButtonClicked |
任务历史视图 |
cline.settingsButtonClicked |
设置视图 |
cline.mcpButtonClicked |
MCP 服务器视图 |
cline.plusButtonClicked |
新任务(聊天视图) |
cline.worktreesButtonClicked |
Worktrees 视图 |
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.historyButtonClicked"}}'
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.settingsButtonClicked"}}'
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.mcpButtonClicked"}}'
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.plusButtonClicked"}}'
5.3 典型会话工作流
# 1. 启动(若没用 --auto-launch)
curl localhost:19229/api -d '{"method":"launch","params":{"skipBuild":true}}'
# 2. 打开侧边栏并关闭弹窗
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
# 3. 检查状态(确认 CLINE_DIR、browser capture 等)
curl localhost:19229/api -d '{"method":"status"}'
# 4. 导航到目标视图
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
# 5. 交互并验证
curl localhost:19229/api -d '{"method":"ui.screenshot"}'
# 6. OAuth 流程时检查捕获的 URL
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# 7. 结束后关闭
curl localhost:19229/api -d '{"method":"shutdown"}'
六、完整 API 参考
所有命令均为 POST /api,JSON body 为 {"method": "...", "params": {...}}。响应格式:成功时 {"result": {...}},失败时 {"error": "..."}。
便捷端点:
GET /health— 返回{"status": "ok"}GET /status— 完整 harness 状态POST /captured-url— 内部端点:接收被调试方上报的捕获 URL(对应 env.ts 中的 fire-and-forget POST)
6.1 生命周期
| 方法 | 参数 | 说明 |
|---|---|---|
launch |
{workspace?, skipBuild?} |
构建 + 启动 VSCode |
shutdown |
关闭 VSCode 及 CDP 连接 | |
status |
所有组件的当前状态 | |
connect_webview |
连接 Webview 的 CDP(侧边栏打开后调用) |
6.2 扩展宿主调试(Node.js)
| 方法 | 参数 | 说明 |
|---|---|---|
ext.set_breakpoint |
{file, line, column?, condition?} |
按源码文件设置断点(经 sourcemap 解析) |
ext.set_breakpoint_raw |
{url?, urlRegex?, scriptId?, lineNumber, columnNumber?, condition?} |
用原始 CDP 参数设置断点 |
ext.remove_breakpoint |
{breakpointId} |
移除断点 |
ext.evaluate |
{expression, callFrameId?} |
求值表达式(在断点处或全局) |
ext.pause |
暂停执行 | |
ext.resume |
恢复执行 | |
ext.step_over |
单步跳过 | |
ext.step_into |
单步进入 | |
ext.step_out |
单步跳出 | |
ext.call_stack |
获取调用栈(暂停时) | |
ext.scripts |
{filter?} |
列出已加载脚本 |
ext.source_files |
从 sourcemap 列出源码文件 | |
ext.get_properties |
{objectId} |
获取对象属性 |
ext.get_script_source |
{scriptId} |
获取脚本源码文本 |
6.3 Webview 调试(Chrome)
侧边栏打开后需先调用 connect_webview(仅断点/单步需要)。
| 方法 | 参数 | 说明 |
|---|---|---|
web.set_breakpoint |
{url, line, column?, condition?} |
按 URL 模式设置断点 |
web.remove_breakpoint |
{breakpointId} |
移除断点 |
web.evaluate |
{expression, callFrameId?} |
在侧边栏(Playwright)或断点处(CDP)求值 |
web.post_message |
{message} |
通过暴露的 vsCodeApi 向扩展宿主发 postMessage |
web.pause |
暂停 | |
web.resume |
恢复 | |
web.step_over/into/out |
单步 |
6.4 UI 自动化(Playwright)
| 方法 | 参数 | 说明 |
|---|---|---|
ui.screenshot |
{fullPage?} |
截图 → 返回 {path}(用 read_file 读该路径,不要 open 文件) |
ui.sidebar_screenshot |
聚焦侧边栏的截图 → 返回 {path} |
|
ui.click |
{selector, frame?, delay?} |
点击元素(Webview 用 frame: "sidebar") |
ui.fill |
{selector, text, frame?} |
填充输入框 |
ui.press |
{key} |
按键(如 "Enter"、"Meta+Shift+p") |
ui.type |
{text, delay?} |
输入文本 |
ui.open_sidebar |
打开 Cline 侧边栏 | |
ui.frames |
列出所有 frame | |
ui.wait_for_selector |
{selector, frame?, timeout?} |
等待元素 |
ui.command_palette |
{command} |
打开命令面板并执行命令 |
ui.get_text |
{selector, frame?} |
获取元素文本 |
ui.locator |
{role?, name?, testId?, text?, frame?, action?, value?} |
富 Playwright 定位器(侧边栏自动带 frame 刷新重试) |
ui.react_input |
{text, selector?, clear?, submit?} |
通过 execCommand('insertText') 设置 React 受控 textarea 的值 |
ui.send_message |
{text, images?, files?, responseType?} |
绕过 textarea 直接发送聊天消息(走 gRPC postMessage) |
6.5 OAuth 与浏览器捕获
| 方法 | 参数 | 说明 |
|---|---|---|
oauth.captured_urls |
{clear?} |
获取被调试方试图在浏览器打开的 URL |
oauth.read_stored_token |
检查被调试方 secrets.json 中的 token | |
oauth.simulate_callback |
{path, code?, state?, provider?, token?} |
构造 MCP/Provider OAuth 的 vscode:// 回调 URI(不投递) |
oauth.read_captured_urls_file |
读取磁盘上的捕获 URL JSONL 日志 |
要真正投递 vscode:// 回调,需通过 ext.evaluate(带 awaitPromise: true)调用调试钩子:globalThis.__clineHandleUri("vscode://saoudrizwan.claude-dev/...?code=...&state=...")。它调用与 VSCode 真实 URI handler 相同的 SharedUriHandler.handleUri,且仅在设置 CLINE_CAPTURE_BROWSER 时注册(生产环境永不注册)。
6.6 组合方法
| 方法 | 参数 | 说明 |
|---|---|---|
wait_for_pause |
{timeout?} |
阻塞直到任一被调试方命中断点 |
七、示例工作流
7.1 设置断点并观察执行
curl localhost:19229/api -d '{
"method": "ext.set_breakpoint",
"params": {"file": "src/extension.ts", "line": 25}
}'
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
curl localhost:19229/api -d '{"method":"wait_for_pause","params":{"timeout":10000}}'
curl localhost:19229/api -d '{"method":"ext.call_stack"}'
curl localhost:19229/api -d '{"method":"ext.resume"}'
7.2 OAuth 登录流程测试
# 关闭弹窗后点击 Login
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
curl localhost:19229/api -d '{"method":"ui.locator","params":{"text":"Login to Cline","frame":"sidebar","action":"click"}}'
# 查看捕获了什么 URL
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# URL 中含 callback_url=http://127.0.0.1:PORT/...
# 在真实浏览器中打开它完成认证,或模拟:
# (先从捕获的 URL 中提取端口)
curl "http://127.0.0.1:PORT/callback?code=real_or_test_code"
# 验证 token 已存储
curl localhost:19229/api -d '{"method":"oauth.read_stored_token"}'
7.3 导航到 Account 视图并检查认证状态
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
curl localhost:19229/api -d '{"method":"ui.screenshot"}'
八、底层实现原理(How It Works)
harness 的完整数据流可拆为八步,均能在 server.ts 中找到对应实现:
-
构建:esbuild 打包
src/extension.ts→dist/extension.js(不压缩、带 sourcemap);Vite 构建webview-ui/→webview-ui/build/(不压缩、内联 sourcemap)。不压缩 + sourcemap 是断点可用性的前提。 -
启动:用
@vscode/test-electron下载 VSCode,再用 Playwright 的_electron.launch()以--inspect-extensions=9230(Node.js inspector 端口,源码中常量EXT_INSPECT_PORT = 9230)和--extensionDevelopmentPath(加载我们自己的扩展)启动。 -
数据隔离:在 debugee 环境注入
CLINE_DIR=~/.cline2。被调试方侧由 storage-context.ts 的createStorageContext()读取该变量,决定globalState.json、secrets.json、任务历史、workspace 状态的存储位置。 -
浏览器捕获:在 debugee 环境注入
CLINE_CAPTURE_BROWSER=1与CLINE_DEBUG_HARNESS_PORT=19229。当 env.ts 的openExternal()被调用时,检测到捕获模式就记录 URL 到 JSONL 文件并 POST 到 harness 服务器,而不打开真实浏览器——这是无头测试 OAuth 流程的关键。 -
扩展 CDP:通过 9230 端口的 WebSocket 连接扩展宿主的 V8 inspector,启用
Debugger与Runtime域,跟踪scriptParsed事件与paused/resumed状态。 -
Sourcemap 解析:按源码文件设置断点时,读取
dist/extension.js.map,用 VLQ 解码的 sourcemap 映射把"原始文件 + 行号"反解为"打包文件 + 行号"。server.ts 内置了一个自研的 VLQ 解码器(标准 64 字符 base64 变体 + 5 位 continuation 位解析),resolveSourceMapPosition()先精确匹配 source 路径、再做后缀匹配。 -
Webview CDP:侧边栏加载后为 Webview frame 创建 Playwright CDP 会话,启用调试命令;失败时回退到
frame.evaluate()做表达式求值——所以即使 CDP 连不上,web.evaluate依然可用。 -
UI 自动化:Playwright 的 Page/Frame API 提供点击、填充、输入、截图、定位器查询等;侧边栏 Webview 作为 VSCode 窗口内的一个 Frame 访问。
九、注意事项与故障排查
9.1 注意事项
- 数据隔离:debugee 默认使用
~/.cline2。若要带真实数据测试,先复制:cp -r ~/.cline ~/.cline2。注意这样做会共享 secrets(API key、auth token)。 - "Introducing Cline Kanban" 弹窗:全新启动时侧边栏可能出现全屏促销弹窗,阻塞所有交互并让截图失效。打开侧边栏后、做任何事之前立即关闭(命令见 5.1 节)。
- 截图:
ui.screenshot与ui.sidebar_screenshot将 PNG 保存到/tmp/cline-debug/(可用SCREENSHOT_DIR环境变量配置,源码中默认为os.tmpdir()/cline-debug),响应返回{path}。不要open该文件——macOS 上会弹出 Preview.app 遮住 VSCode 窗口,应使用read_file读取。 - 真实 Provider 的 OAuth:浏览器捕获只拦截 debugee 试图打开的 URL。Cline OAuth 中 SDK 的本地回调服务器仍在运行、能接收重定向;Provider OAuth(OpenRouter、MCP)则需要模拟
vscode://回调 URI(见第四章)。 - 伪造 code 的 Cline OAuth:用假 code 模拟回调时,SDK 的 token 交换会失败(provider 不认识该 code)。需要真实授权码或 mock token 交换端点。
9.2 故障排查
| 症状 | 原因与处置 |
|---|---|
| "Inspector not available on port 9230" | 扩展宿主尚未启动。等待更久,或确认扩展构建正确 |
| "Sidebar frame not found" | Cline 侧边栏未打开,先执行 ui.open_sidebar |
| "Webview CDP not connected" | 侧边栏打开后调用 connect_webview;失败时 Webview 断点不可用,但 web.evaluate 仍可通过 Playwright 工作 |
| Sourcemap 解析失败 | 先用 ext.source_files 查看 sourcemap 包含的路径,再用 ext.set_breakpoint_raw 配合 urlRegex 模式 |
| 截图目录 | 保存到 /tmp/cline-debug/(可经 SCREENSHOT_DIR 配置) |
| debugee 仍在用 ~/.cline | 检查 status() 响应中是否出现 CLINE_DIR;缺失可能是 debugee 在 harness 设置环境变量之前就被启动了。Shutdown 后重新启动 |
十、小结与适用前提
Debug Harness 把"启动一个带 inspector 的 VSCode → 附着 CDP → 驱动 Playwright → 拦截外部浏览器调用"这一整套 VSCode 扩展调试链路收敛成了一个 19229 端口的 HTTP 服务,使断点调试、UI 断言与 OAuth 端到端测试都可以用纯 curl 序列表达,这也是它"专为 Agent 循环设计"的落地方式。使用前提:Node >= 22.6、以 node(而非 bun)运行、被调试方版本受 VSCODE_TEST_VERSION(默认 1.103.0)约束。仓库中相关入口文件:debug-harness/README.md、debug-harness/server.ts、mcp-oauth-test-server/README.md、env.ts、storage-context.ts、extension.ts、registry.ts。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00