vcorp-chrome 1.8.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -189,7 +189,7 @@ args = ["-y", "vcorp-chrome", "mcp", "--client", "codex"]
189
189
  | `navigate` `tabs` `wait` `scroll` | 导航、标签页、等待、滚动加载 |
190
190
  | `read_text` | 正文提取成 markdown,去掉导航页脚广告和头像图 |
191
191
  | `query` | 按 CSS selector 结构化提取,用于没有可用接口的站点;`contains` 按文本找元素——「页面上有没有这句话」「这个状态字现在是什么」不必再写 eval |
192
- | `upload` | 把本地文件塞进网页的上传框——系统文件对话框是扩展够不着的,这是唯一的路 |
192
+ | `upload` | 把本地文件塞进网页的上传框,首选这条;页面已经弹出了系统文件对话框,用 `desktop` 接手 |
193
193
  | `eval` | 跑一段 JS。在页面自己的世界里求值,所以受**页面** CSP 管,大站会拦 |
194
194
 
195
195
  **批处理**
@@ -210,6 +210,13 @@ args = ["-y", "vcorp-chrome", "mcp", "--client", "codex"]
210
210
  |---|---|
211
211
  | `screenshot` | 只在版式本身就是问题时用。开了高保真模式可以直接截后台标签页,不打断你。默认 60% 缩放的 JPEG(视觉 token 省四分之三),`full:true` 拿 1:1 PNG;`savePath` 落盘不进上下文 |
212
212
 
213
+ **网页以外**
214
+
215
+ | 工具 | 干什么 |
216
+ |---|---|
217
+ | `desktop` | 桌面应用和系统对话框(页面弹出的打开/保存框也算),经 [agent-desktop](https://github.com/vercel-labs/agent-desktop) 读辅助功能树:`list-windows` → `snapshot` → 按完整 ref(`@s…:eN`)操作 → 读效果行。默认后台,不抢鼠标;`headed:true` 才动真实键鼠。浏览器主窗口、密码管理器一律拒绝 |
218
+ | `devices` | 这个会话能驱动的设备:连着的每个 Chrome,和这台电脑的桌面。连着两个 Chrome 时 `tabs {device}` 指定开在哪一个,之后同一个 tabId 自动跟过去 |
219
+
213
220
  这套顺序不用你教给 agent——MCP server 在握手时就把它作为 `instructions` 下发了。
214
221
 
215
222
  ## 每个操作都要交待「到底动没动」
@@ -423,6 +430,18 @@ offscreen 万一建不起来,扩展不能整个哑掉。
423
430
  (`form.submit()`、直接 fetch 下单接口仍然绕得过:eval 本质是把页面的
424
431
  执行权交出去,这道防线是提高门槛,不是保证。)
425
432
 
433
+ 10. **桌面一侧不给扩展开后门**——Chrome 的主窗口在辅助功能树里整个敞开:地址栏、钱包按钮、
434
+ 密码管理,连本扩展侧栏里的「暂停」「断开并忘记这个服务器」都有 ref。桌面能点到它们,
435
+ 上面每一道闸就都白设了。所以 `desktop` 对浏览器只认它弹出的系统文件对话框(按控件的
436
+ 固定自动化 id 认,不认标题),浏览器发出的通知不替你点(任务批准就在那里),付款类按钮拒绝,
437
+ 发送 / 删除不进批处理。用户写的文字一律不会被当成命令行开关(`--opt=值`,自由文本放在 `--` 之后)。
438
+ 规矩在 `extension/desktop-policy.js`,纯函数,`test/desktop-policy.test.js` 逐条守着。
439
+
440
+ 云端任务(maker.vcorp.ai 这类)要用桌面,得你先在侧栏点「启用桌面能力」并运行一次
441
+ `npx vcorp-chrome desktop install`(登记原生消息宿主,只允许本扩展调用)。每个任务要操作的应用
442
+ 逐个点头;终端、注册表、编辑器这类能拿到整台机器的应用云端一律不碰;云端没有截图、剪贴板读取、
443
+ 通知和真实键鼠。宿主那边把参数再审一遍,浏览器主窗口的标题和树不出这台电脑。
444
+
426
445
  还没做完的,如实说:
427
446
 
428
447
  | | 状态 |
@@ -629,3 +629,56 @@ act 步的期望落空按停机处理,和「零效果就停」同一个哲学
629
629
  `toolparamdescription` 与它在快照里的 ref、是否 `toolautosubmit`。填还是 `fill`,提交还是 `click`,
630
630
  效果证据与支付闸门照旧。命令式 API(`document.modelContext.getTools()`)住在页面主世界,
631
631
  隔离世界够不着,等 origin trial 稳定后再议。
632
+
633
+ ## 十二、多设备与桌面(v1.9)
634
+
635
+ ### 设备
636
+
637
+ 一个用户名下有多台**设备**,每台报自己能做什么(`caps`):
638
+
639
+ | kind | id | 来自 | caps |
640
+ |---|---|---|---|
641
+ | `browser` | 本地桥里 `browser-<instanceId>`;云端是服务器配对时给的 deviceId | 扩展 | `browser` |
642
+ | `desktop` | `desktop-<machineId>`,machineId 形如 `m_<16 hex>`,存在 `~/.vcorp-chrome/machine.json` | 本地 MCP 进程,或扩展经原生宿主上报 | `desktop` |
643
+ | `phone` | 预留 | — | — |
644
+
645
+ 桌面设备没有自己的连接,挂在一台浏览器下面(`parent`):云端是「服务器 → 扩展 → 原生宿主」,
646
+ 本地是 MCP 进程直接起 agent-desktop,不经过桥。
647
+
648
+ ### 信封
649
+
650
+ 命令可带 `to: {device, cap}`,回执带 `from: {device, kind}`:
651
+
652
+ - 本地桥:`devices` 由桥自己回答(welcome 里 `devices: true` 表示桥懂这条);带 `to.device` 的命令只发给那个实例,
653
+ 不在线回 `DEVICE_OFFLINE`。老桥不懂 `to`,MCP 侧在发之前就报 `BRIDGE_OLD`,不让它静默发错地方。
654
+ - 云端扩展:`to.device` 不是自己、也不是自己挂着的桌面,回 `DEVICE_NOT_HERE`;`cap:'desktop'` 交给原生宿主。
655
+ - 服务器:`devices` 自己答;`desktop` / `apps` 发给挂着那台桌面的浏览器。同一个任务一旦用过某台浏览器 / 桌面就绑定它,
656
+ 它掉线回 `DEVICE_OFFLINE`,不悄悄换一台。多台可选又没指定回 `AMBIGUOUS_DEVICE`,一台都没有回 `NO_DEVICE`。
657
+ 回执的 `from` 和发去的设备对不上回 `DEVICE_MISMATCH`。结果里带 `device`。
658
+ - 审计每行记 `device`。
659
+
660
+ ### 桌面命令
661
+
662
+ `desktop {cmd, …}` 是 agent-desktop 子命令的白名单,参数在 `extension/desktop-policy.js` 里组装成 argv:
663
+ 值一律写成 `--opt=值`,自由文本(type / set-value / select / clipboard-set / launch 的应用名 / 通知按钮)放在 `--` 之后。
664
+
665
+ 实测(Windows,agent-desktop 1.0.2):
666
+
667
+ - `type @ref --headed` 里的 `--headed` 被当成开关吃掉了——这就是上面那条规矩的由来。
668
+ - `list-windows` 约 1.7 s,一次快照约 1.9 s,`list-apps` 约 0.2 s。
669
+ - Chrome 主窗口整棵树都在 UIA 里,含扩展侧栏的按钮;系统文件对话框的「文件名」框和「打开」按钮有固定 native id(1148 / 1),
670
+ 按它认对话框,不按标题。
671
+
672
+ ref 形如 `@s<快照 id>:eN`,必须给完整的;快照过期回 `STALE_REF`。
673
+
674
+ ### 原生宿主(云端用)
675
+
676
+ - 名字 `ai.vcorp.desktop`,Node 脚本 `native-host/host.js`,`npx vcorp-chrome desktop install` 写启动器和清单到
677
+ `~/.vcorp-chrome/desktop-host/`,Windows 登记在 `HKCU\Software\{Google\Chrome,Microsoft\Edge}\NativeMessagingHosts`,
678
+ `allowed_origins` 只有本扩展(id 由 manifest 的 `key` 算出)。`uninstall` / `status` 对应撤销与自检。
679
+ - 帧:4 字节小端长度 + JSON。回包上限 1 MB,超了回 `TOO_LARGE`,不截断。
680
+ - `hello` → `{device, ready}`;`run {argv}` → agent-desktop 的信封。
681
+ - 宿主不信任扩展:argv 再审一遍(选项白名单、裸词只能是 ref / 数字 / 按键组合、只放行 `cloud` 命令、会话名必须以 `cloud-` 开头),
682
+ `list-windows` 滤掉浏览器主窗口,快照里出现浏览器主窗口也拒绝。
683
+ - 扩展侧:`nativeMessaging` 是可选权限,在侧栏点「启用桌面能力」时申请。每个任务要用的应用逐个批准(`apps`),
684
+ 付款类按钮拒绝,发送 / 删除一类先确认(免确认开着则跳过),`steps` 不开放,每个任务最多 4 次快照。
@@ -1,53 +1,55 @@
1
- # 浏览器操控方案对比矩阵
2
-
3
- 核查日期 2026-09-09。
4
-
5
- 研究员逐格核实,每格落在官方文档 / 官方 README / 官方 issue / 仓库源码上。
6
- README「为什么需要它」和安装引导页附录 B 的对比表是这张矩阵的精简版。改表前先看末尾「必须小心的说法」——竞品在动,这里的每一格都有过期的一天,发现不对请开 issue。
7
-
8
- `✅` 具备且有官方明文 · `⚠️` 部分具备或有前置条件 · `❌` 不具备 · `➖` 结构上不适用 · `?` 官方未说明
9
-
10
- C 维度按「用户要不要额外折腾浏览器」记分:`✅` = 不需要。
11
-
12
- | | **vcorp-chrome** | Claude in Chrome | Codex for Chrome | playwright-mcp | chrome-devtools-mcp | Browser MCP | Browser Use | 通用 computer-use |
13
- |---|---|---|---|---|---|---|---|---|
14
- | **A** 日常 profile 真实登录态 | ✅ 就是当前浏览器 | ✅ 共享你的登录态 | ✅ 装扩展那个 profile | ⚠️ 须走 `--extension` | ⚠️ 须 Chrome144 autoConnect | ✅ 用现有 profile | ⚠️ Harness 接管/库要先退 Chrome | ❌ 容器或云沙盒浏览器 |
15
- | **B** 任意 MCP agent 可接 | ✅ 20+ agent 通用 | ❌ 限 Anthropic 客户端 | ❌ 限 ChatGPT 桌面 App | ✅ 官方列 20+ 客户端 | ✅ 标准 MCP server | ✅ 标准 stdio MCP | ✅ 三个 MCP server | ❌ API 内置工具,非 MCP |
16
- | **C** 免改启动参数/调试端口 | ✅ 只手动装扩展 | ✅ 只装扩展 | ✅ 扩展+桌面 App | ⚠️ 扩展模式免,否则装浏览器 | ❌ 要开远程调试或专用 profile | ✅ 扩展+Node | ❌ 装 Chromium 或开 chrome://inspect | ➖ 不走浏览器 |
17
- | **D** 多会话隔离 | ✅ 按会话分槽+双色告警 | ⚠️ 每会话一标签组 | ? 官方未描述 | ✅ 每客户端一标签组 | ⚠️ 靠实验开关路由 | ❌ 单槽且抢端口 | ⚠️ 本地共用一条道会抢 | ⚠️ 各自容器/mac 声称并行 |
18
- | **E** 跨会话记忆站点经验 | ✅ 出厂+本机 learnings | ❌ 无站点级 | ⚠️ 通用 Memories | ❌ 无 | ❌ 无 | ❌ 无 | ⚠️ domain-skills 默认关 | ⚠️ 仅通用 memory tool |
19
- | **F** 后台标签页不抢焦点 | ✅ 全程后台 | ❌ 可见窗口实时操作 | ✅ 商店文案承诺后台 | ? 官方未表态 | ⚠️ 置顶可选,文档未承诺 | ? 官方未表态 | ⚠️ Harness 明令后台 | ⚠️ mac 后台/Win 抢焦点 |
20
- | **G** 看得见谁在控哪页 | ✅ 标签组+描边+侧栏 | ✅ 彩色标签组 | ? 官方未描述 | ⚠️ 按客户端命名标签组 | ❌ 无标记 | ⚠️ 只见连接状态 | ⚠️ 标题加🐴不分 agent | ⚠️ VNC/实时预览看整桌面 |
21
- | **H** 人工介入交接 | ✅ `ask`+扩展内支付确认 | ✅ 遇登录/验证码暂停 | ⚠️ 敏感动作确认,验证码未明 | ❌ 无交接工具 | ❌ 无交接工具 | ❌ 主张绕过验证码 | ✅ 云端 takeover | ⚠️ Anthropic 只给规范 |
22
- | **I** 读网络层响应 | ✅ `network`+`fetch` | ✅ 可读网络请求 | ⚠️ 需开 full CDP | ✅ 含 headers+body | ✅ 含响应体 | ❌ 只有 console | ⚠️ 只能自走 CDP | ❌ 只有截图坐标 |
23
- | **J** 开源+许可证 | ✅ MIT | ❌ 未开源 | ❌ 扩展未开源 | ✅ Apache-2.0 | ✅ Apache-2.0 | ⚠️ Apache-2.0 但扩展闭源 | ✅ MIT(云端闭源) | ⚠️ demo MIT,模型闭源 |
24
- | **K** 定价 | ✅ 开源免费(成本=自己 agent 的 token) | ❌ 需付费订阅 | ❌ 付费+地区限制 | ✅ 免费 | ✅ 免费 | ✅ 实际免费 | ⚠️ 本地免费,云按量 | ⚠️ 按 token,工具定义约 4500 tok |
25
-
26
- ## 关键来源
27
-
28
- - Claude in Chrome:https://code.claude.com/docs/en/chrome (API key / Bedrock / Vertex / Foundry 一概不行;遇登录页/验证码暂停);https://claude.com/claude-in-chrome (GA,全部付费套餐)
29
- - Codex for Chrome(商店名「ChatGPT」):https://learn.chatgpt.com/docs/chrome-extension ;「Browser isn't available in Codex CLI or the Codex IDE extension」https://learn.chatgpt.com/docs/browser ;CLI 拿不到扩展后端 issue 仍 open https://github.com/openai/codex/issues/26820 ;商店文案「work in the background without ever taking over your active browsing session」
30
- - playwright-mcp:https://github.com/microsoft/playwright-mcp ;扩展模式 https://github.com/microsoft/playwright/tree/main/packages/extension (「Each one gets its own tab group … a tab can belong to a single client at a time」);扩展 v0.4.0 2026-09-02
31
- - chrome-devtools-mcp:autoConnect(Chrome ≥ M144,chrome://inspect/#remote-debugging 逐次授权)https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/advanced-usage.md ;https://developer.chrome.com/docs/devtools/agents/use-cases/auto-connect ;issue #2283 默认 profile autoConnect 失败 closed as not planned
32
- - Browser MCP:https://github.com/BrowserMCP/mcp (pushed_at 2025-04-24,停更约 16 个月;单槽 `killProcessOnPort`;扩展闭源 issue #72;无 network issue #39)
33
- - Browser Use:https://docs.browser-use.com/open-source/customize/browser/real-browser (库要先关 Chrome);Harness https://github.com/browser-use/browser-harness (SKILL.md:「treat local Chrome as one shared browser lane」「Two agents … can race」;`BH_DOMAIN_SKILLS=1` 默认关;🐴 标题标记)
34
- - computer-use:Anthropic `computer_toolset_20260801` https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool (工具定义约 4,500 input tokens);Anthropic 另有独立 browser use tool(含 read_network);OpenAI Operator / ChatGPT agent 已下线 https://help.openai.com/en/articles/11752874-chatgpt-agent ;`computer-use-preview` 2026-07-23 废弃
35
-
36
- ## 最能拉开差距的维度(证据最硬的先)
37
-
38
- 1. **A×B 交集**:既是真实登录态、又对任意 MCP agent 开放——同时满足的只有 vcorp-chrome、Browser MCP(已停更)、playwright-mcp 扩展模式。要写成「和 Playwright 扩展模式二选一」,不是「独一份」。
39
- 2. **E 跨会话站点经验**:竞品里只有 Browser Use Harness 有同构能力且默认关闭、不随包分发。vcorp-chrome 出厂即带 21 站已验证经验(含负面结论、带实测日期)。建议主打。
40
- 3. **H 人工交接**:三个开源 MCP 方案都没有交接工具;vcorp-chrome「支付确认闸在扩展里、agent 够不着」是架构级论证。
41
- 4. **D+G 组合**:会话隔离 + 页内标记 + 截图幕帘(agent 看不见自己的标记)同时具备的只有它。截图幕帘无任何竞品提到。
42
- 5. **F 不建议当主打**:Codex 商店文案和 Browser Use Harness 都有更硬的官方措辞。
43
-
44
- ## 必须小心的说法
45
-
46
- 1. 「Playwright MCP 只能开新的无头浏览器」已经错了:扩展模式连已登录 Chrome,还有按客户端命名着色的标签组隔离。它真实短板:无站点记忆、无人工交接、默认不是扩展模式、首次要人工选标签页。
47
- 2. chrome-devtools-mcp 的 Chrome 136 限制官方已有 `--autoConnect` 绕法(Chrome ≥ M144,chrome://inspect 逐次授权)。README 现有「❌ Chrome 136 起封了默认 profile 的远程调试」只对一半,改成「要在 chrome://inspect 里逐次授权、且只有 Chrome 144+」。
48
- 3. Browser Use Harness 在 E/F/G 上都追上来了,「只有我们有站点记忆/只有我们后台干活」站不住;改成「出厂即带 21 站已验证经验、默认开启」和「三处露出+截图幕帘」。它的短板是 D。
49
- 4. Claude in Chrome 已 GA、全部付费套餐,不再限 Max;「API key / Bedrock 不行」仍准确。
50
- 5. 「Codex for Chrome」不是官方名(商店叫「ChatGPT」);引官方文档「Browser isn't available in Codex CLI」比引 issue 更硬。
51
- 6. computer-use 世代已翻:Operator / ChatGPT agent 下线,`computer-use-preview` 废弃,现行 `computer_toolset_20260801`。说「读不到网络层」要补一句「Anthropic 另有 browser use tool 可以,但浏览器要自己提供、fresh profile 无凭据」。
52
- 7. 「官方未说明」的格子(Codex 的 D/G、Browser MCP 的 F、playwright-mcp 的 F)别写成 ❌。
53
- 8. vcorp-chrome 的 K:README 没写定价,写「开源免费,成本=你自己 agent 的 token」,别和竞品的现金支出并列成一个 ✅。
1
+ # 浏览器操控方案对比矩阵
2
+
3
+ 核查日期 2026-11-01。
4
+
5
+ 研究员逐格核实,每格落在官方文档 / 官方 README / 官方 issue / 仓库源码上。
6
+ README「为什么需要它」和安装引导页附录 B 的对比表是这张矩阵的精简版。改表前先看末尾「必须小心的说法」——竞品在动,这里的每一格都有过期的一天,发现不对请开 issue。
7
+
8
+ `✅` 具备且有官方明文 · `⚠️` 部分具备或有前置条件 · `❌` 不具备 · `➖` 结构上不适用 · `?` 官方未说明
9
+
10
+ C 维度按「用户要不要额外折腾浏览器」记分:`✅` = 不需要。
11
+
12
+ | | **vcorp-chrome** | Claude in Chrome | Codex for Chrome | playwright-mcp | chrome-devtools-mcp | Browser MCP | Browser Use | ego-lite | 通用 computer-use |
13
+ |---|---|---|---|---|---|---|---|---|---|
14
+ | **A** 日常 profile 真实登录态 | ✅ 就是当前浏览器 | ✅ 共享你的登录态 | ✅ 装扩展那个 profile | ⚠️ 须走 `--extension` | ⚠️ 须 Chrome144 autoConnect | ✅ 用现有 profile | ⚠️ Harness 接管/库要先退 Chrome | ⚠️ 须换用其专用浏览器(首次导入 Chrome) | ❌ 容器或云沙盒浏览器 |
15
+ | **B** 任意 MCP agent 可接 | ✅ 20+ agent 通用 | ❌ 限 Anthropic 客户端 | ❌ 限 ChatGPT 桌面 App | ✅ 官方列 20+ 客户端 | ✅ 标准 MCP server | ✅ 标准 stdio MCP | ✅ 三个 MCP server | ⚠️ 走 CLI+Skill(非 MCP,需 shell 跑 JS) | ❌ API 内置工具,非 MCP |
16
+ | **C** 免改启动参数/调试端口 | ✅ 只手动装扩展 | ✅ 只装扩展 | ✅ 扩展+桌面 App | ⚠️ 扩展模式免,否则装浏览器 | ❌ 要开远程调试或专用 profile | ✅ 扩展+Node | ❌ 装 Chromium 或开 chrome://inspect | ❌ 须另装专用浏览器+CLI | ➖ 不走浏览器 |
17
+ | **D** 多会话隔离 | ✅ 按会话分槽+双色告警 | ⚠️ 每会话一标签组 | ? 官方未描述 | ✅ 每客户端一标签组 | ⚠️ 靠实验开关路由 | ❌ 单槽且抢端口 | ⚠️ 本地共用一条道会抢 | ✅ 每任务独立 TaskSpace | ⚠️ 各自容器/mac 声称并行 |
18
+ | **E** 跨会话记忆站点经验 | ✅ 出厂+本机 learnings | ❌ 无站点级 | ⚠️ 通用 Memories | ❌ 无 | ❌ 无 | ❌ 无 | ⚠️ domain-skills 默认关 | ⚠️ README 标 coming soon(源码 3 站) | ⚠️ 仅通用 memory tool |
19
+ | **F** 后台标签页不抢焦点 | ✅ 全程后台 | ❌ 可见窗口实时操作 | ✅ 商店文案承诺后台 | ? 官方未表态 | ⚠️ 置顶可选,文档未承诺 | ? 官方未表态 | ⚠️ Harness 明令后台 | ✅ 后台 TaskSpace 隔离运行 | ⚠️ mac 后台/Win 抢焦点 |
20
+ | **G** 看得见谁在控哪页 | ✅ 标签组+描边+侧栏 | ✅ 彩色标签组 | ? 官方未描述 | ⚠️ 按客户端命名标签组 | ❌ 无标记 | ⚠️ 只见连接状态 | ⚠️ 标题加🐴不分 agent | ✅ 独立 Space 视图+光标动作标签 | ⚠️ VNC/实时预览看整桌面 |
21
+ | **H** 人工介入交接 | ✅ `ask`+扩展内支付确认 | ✅ 遇登录/验证码暂停 | ⚠️ 敏感动作确认,验证码未明 | ❌ 无交接工具 | ❌ 无交接工具 | ❌ 主张绕过验证码 | ✅ 云端 takeover | ✅ `handOff`/`takeOver`+系统权限自动交接 | ⚠️ Anthropic 只给规范 |
22
+ | **I** 读网络层响应 | ✅ `network`+`fetch` | ✅ 可读网络请求 | ⚠️ 需开 full CDP | ✅ 含 headers+body | ✅ 含响应体 | ❌ 只有 console | ⚠️ 只能自走 CDP | ⚠️ 有 `page.fetch`,抓包需自走 `page.cdp` | ❌ 只有截图坐标 |
23
+ | **J** 开源+许可证 | ✅ MIT | ❌ 未开源 | ❌ 扩展未开源 | ✅ Apache-2.0 | ✅ Apache-2.0 | ⚠️ Apache-2.0 但扩展闭源 | ✅ MIT(云端闭源) | ⚠️ SDK/Skill MIT,浏览器本体闭源 | ⚠️ demo MIT,模型闭源 |
24
+ | **K** 定价 | ✅ 开源免费(成本=自己 agent 的 token) | ❌ 需付费订阅 | ❌ 付费+地区限制 | ✅ 免费 | ✅ 免费 | ✅ 实际免费 | ⚠️ 本地免费,云按量 | ✅ 免费(成本=自己 agent 的 token) | ⚠️ 按 token,工具定义约 4500 tok |
25
+
26
+ ## 关键来源
27
+
28
+ - Claude in Chrome:https://code.claude.com/docs/en/chrome (API key / Bedrock / Vertex / Foundry 一概不行;遇登录页/验证码暂停);https://claude.com/claude-in-chrome (GA,全部付费套餐)
29
+ - Codex for Chrome(商店名「ChatGPT」):https://learn.chatgpt.com/docs/chrome-extension ;「Browser isn't available in Codex CLI or the Codex IDE extension」https://learn.chatgpt.com/docs/browser ;CLI 拿不到扩展后端 issue 仍 open https://github.com/openai/codex/issues/26820 ;商店文案「work in the background without ever taking over your active browsing session」
30
+ - playwright-mcp:https://github.com/microsoft/playwright-mcp ;扩展模式 https://github.com/microsoft/playwright/tree/main/packages/extension (「Each one gets its own tab group … a tab can belong to a single client at a time」);扩展 v0.4.0 2026-09-02
31
+ - chrome-devtools-mcp:autoConnect(Chrome ≥ M144,chrome://inspect/#remote-debugging 逐次授权)https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/advanced-usage.md ;https://developer.chrome.com/docs/devtools/agents/use-cases/auto-connect ;issue #2283 默认 profile autoConnect 失败 closed as not planned
32
+ - Browser MCP:https://github.com/BrowserMCP/mcp (pushed_at 2025-04-24,停更约 16 个月;单槽 `killProcessOnPort`;扩展闭源 issue #72;无 network issue #39)
33
+ - Browser Use:https://docs.browser-use.com/open-source/customize/browser/real-browser (库要先关 Chrome);Harness https://github.com/browser-use/browser-harness (SKILL.md:「treat local Chrome as one shared browser lane」「Two agents … can race」;`BH_DOMAIN_SKILLS=1` 默认关;🐴 标题标记)
34
+ - ego-lite:https://github.com/citrolabs/ego-lite (独立闭源 Chromium 发行版+MIT 开源的 `ego-browser` Node SDK/Skill;首次启动可选迁移 Chrome 数据,后续与原 Chrome 独立;非 MCP,通过 `ego-browser nodejs` 执行 `.mjs` 脚本调 `globalThis.ego` 原生绑定;内置 `TaskSpace` 隔离与 `handOff`/`takeOver` 及 `EGO_TASK_SPACE_USER_IN_CONTROL`;README 标「Experience accumulation (coming soon)」,仓库 `skills/ego-browser/learnings` 现有 github/google/x-com 3 站;无高层网络录制缓冲池,有 `page.fetch` 与 `page.cdp`)
35
+ - computer-use:Anthropic `computer_toolset_20260801` https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool (工具定义约 4,500 input tokens);Anthropic 另有独立 browser use tool(含 read_network);OpenAI Operator / ChatGPT agent 已下线 https://help.openai.com/en/articles/11752874-chatgpt-agent ;`computer-use-preview` 2026-07-23 废弃
36
+
37
+ ## 最能拉开差距的维度(证据最硬的先)
38
+
39
+ 1. **A×B×C 交集**:既直接跑在用户现有的日常 Chrome 里(不用换浏览器、不用迁数据)、又对任意 MCP agent 开放——同时满足的只有 vcorp-chrome、Browser MCP(已停更)、playwright-mcp 扩展模式。ego-lite 虽支持多 agent,但要求用户换用其闭源专用浏览器且只走 CLI 脚本而非 MCP。
40
+ 2. **E 跨会话站点经验**:竞品里只有 Browser Use Harness(默认关、不随包分发)和 ego-lite(README 标 coming soon,仓库仅 3 个海外站)有同构设计。vcorp-chrome 出厂即带 28 站已验证经验(含负面结论、带实测日期、支持 `act` 剧本采纳)。建议主打。
41
+ 3. **H 人工交接**:三个开源 MCP 方案都没有交接工具;ego-lite 有 TaskSpace 级 `handOff`/`takeOver` 和系统权限拦截,但无敏感业务操作(提交/删除/付款)的硬拦截;vcorp-chrome「支付确认闸在扩展里、agent 够不着、`act` 遇敏感按钮自动停下」是架构级论证。
42
+ 4. **D+G 组合**:在「不换浏览器」的前提下,会话隔离 + 页内标记 + 截图幕帘(agent 看不见自己的标记)同时具备的只有 vcorp-chrome(ego-lite 靠重造一个专用浏览器用 TaskSpace 实现隔离)。截图幕帘无任何竞品提到。
43
+ 5. **F 不建议当主打**:Codex 商店文案、Browser Use Harness 和 ego-lite 都有明确的后台运行机制与官方措辞。
44
+
45
+ ## 必须小心的说法
46
+
47
+ 1. 「Playwright MCP 只能开新的无头浏览器」已经错了:扩展模式连已登录 Chrome,还有按客户端命名着色的标签组隔离。它真实短板:无站点记忆、无人工交接、默认不是扩展模式、首次要人工选标签页。
48
+ 2. chrome-devtools-mcp 的 Chrome 136 限制官方已有 `--autoConnect` 绕法(Chrome ≥ M144,chrome://inspect 逐次授权)。README 现有「❌ Chrome 136 起封了默认 profile 的远程调试」只对一半,改成「要在 chrome://inspect 里逐次授权、且只有 Chrome 144+」。
49
+ 3. Browser Use Harness 和 ego-lite 在 D/E/F/G/H 上都有追赶,「只有我们有站点记忆/只有我们后台干活/只有我们有人机交接」站不住;要卡住前提:「在**不换掉用户日常 Chrome** 的扩展方案里」或强调「出厂即带 28 站已验证经验、默认开启」、「三处露出+截图幕帘」以及「扩展侧硬件级付款确认闸」。
50
+ 4. 评价 ego-lite 时要准确区分「开源部分」和「闭源部分」:它的 GitHub 仓库(`ego-browser` SDK 与 Skill)是 MIT,但核心卖点所依赖的 `ego lite` 浏览器本体是闭源二进制安装包(仅 macOS/Windows,Linux 未出);且它不是 MCP server,而是让 agent 调 `ego-browser nodejs` 跑 JS 代码(在 Windows PowerShell 下多行/中文引号限制较多,官方 SKILL.md 要求每轮落盘写 `.mjs` 文件再执行)。
51
+ 5. Claude in Chrome 已 GA、全部付费套餐,不再限 Max;「API key / Bedrock 不行」仍准确。
52
+ 6. 「Codex for Chrome」不是官方名(商店叫「ChatGPT」);引官方文档「Browser isn't available in Codex CLI」比引 issue 更硬。
53
+ 7. computer-use 世代已翻:Operator / ChatGPT agent 下线,`computer-use-preview` 废弃,现行 `computer_toolset_20260801`。说「读不到网络层」要补一句「Anthropic 另有 browser use tool 可以,但浏览器要自己提供、fresh profile 无凭据」。
54
+ 8. 「官方未说明」的格子(Codex 的 D/G、Browser MCP 的 F、playwright-mcp 的 F)别写成 ❌。
55
+ 9. vcorp-chrome 的 K:README 没写定价,写「开源免费,成本=你自己 agent 的 token」,别和竞品的现金支出并列成一个 ✅。
@@ -0,0 +1,288 @@
1
+ # 能力包(load/call)与嘉立创EDA:交接文档
2
+
3
+ 2026-10-30 完成,跨两个仓库:
4
+
5
+ - **vcorp-chrome**(本仓库,分支 `my-dev`):扩展里的能力包机制,以及 `eda` 能力包。
6
+ - **opus-video-maker**(分支 `main`):`opus-eda` 沙箱镜像、`easyeda` skill,以及网页表单里的「专用工具」。
7
+
8
+ 第 4 节写怎么升级上游 easyeda-agent,平时最常用的是这一节。
9
+
10
+ ---
11
+
12
+ ## 1. 现状
13
+
14
+ | 东西 | 位置 / 版本 | 状态 |
15
+ |---|---|---|
16
+ | 扩展 | `my-dev` @ `d8d8ba8`(Release 1.8.1) | 已推送 |
17
+ | eda 能力包 | `1.15.0+7a73a68.f00c96`(上游 easyeda-agent 1.15.0 @ `7a73a68`) | 随扩展发布 |
18
+ | 网页 | opus-video-maker `main` @ `a08d3aa` | Coolify 已部署,任务表单有「专用工具:嘉立创EDA(原理图、PCB)」 |
19
+ | 镜像 | `opus-eda`,宿主 192.168.68.2 | 2026-10-30 22:09 上线,1.21 GB,default_model=`openai/gpt-6.1-terra` |
20
+
21
+ 已经实测通过:
22
+
23
+ - 只读任务:在测试工程上跑原理图检查,用时 3 分 45 秒,花费 $0.15,出了报告。
24
+ - 真实工程上的布线任务:1.8.1 修好 `project.find` 之后能跑通。
25
+ - 云端调用删除类 action 时的确认弹窗,三种结果都试过:同意、拒绝/超时、全局免确认。
26
+
27
+ ---
28
+
29
+ ## 2. 整体怎么连起来
30
+
31
+ ```
32
+ 网页任务(kind=browse,load=eda)
33
+ → gen.js selectImage 选 opus-eda 镜像(GBX_EDA_IMAGE,默认 opus-eda)
34
+ → 沙箱里的 agent 读 skills/easyeda,用 browser.mjs 发 load / call
35
+ → extlink.js(云端通道)→ 用户 Chrome 里的扩展 cloud.js
36
+ → cloud-policy.js 查白名单 / 站点 / 标签页归属;confirm 清单里的 action 先弹窗
37
+ → background.js:load 注入 extension/loads/eda/agent.js(MAIN world),call 用结构化克隆调用
38
+ → 页面里的 window.__vcorpLoads.eda.call(action, payload) → 上游 runAction → 编辑器的 _EXTAPI_ROOT_
39
+ ```
40
+
41
+ ### vcorp-chrome 里的文件
42
+
43
+ | 文件 | 作用 |
44
+ |---|---|
45
+ | `loads/eda/load.json` | 清单。包括 hosts、waitFor、limits,`vendor`(上游仓库 + **固定 40 位 commit**),`actions.read` / `actions.write`(白名单,现在 63 / 90),`confirm`(14 个,云端调用前要用户点头),`denied`(15 个,带不开放的理由) |
46
+ | `loads/eda/setup.js` | 跑在上游代码之前,补全局变量:`eda = _EXTAPI_ROOT_`,再把 `E*_` / `I*_` 枚举从 `_EXTAPI_SCRIPT_SPACES_` 搬到 window |
47
+ | `loads/eda/entry.js` | 页面侧入口,注册 `__vcorpLoads.eda = { version, ready(), call() }`。<br>`ready()`:地址里带工程 id、但工程还没打开时回「没准备好」。<br>`call()`:再查一次白名单。<br>**`project.find` 在这里被覆盖了**,见 6.4 |
48
+ | `loads/eda/actions-md.py` | 生成 opus 那边的 `skills/easyeda/references/actions.md` |
49
+ | `scripts/build-loads.mjs` | `npm run build:loads [eda]`。按 commit 把上游 clone 到 `.cache/loads/eda@<12位>/`,用 esbuild 打成 `extension/loads/eda/agent.js`(不压缩),再写注册表 `extension/loads/index.js` |
50
+ | `extension/loads/**` | **构建产物,入库**。MV3 不允许远程代码,能力包只能是扩展包里的文件。`.gitattributes` 设了 `-text`,保证 sha256 在 Windows 上也一致 |
51
+ | `extension/load.js` | 校验:`loadDef`、`checkLoadParams`、`checkCallParams`、`actionKind`、`confirmText`、`loadHostAllowed` |
52
+ | `extension/background.js` | `load` / `call` 两个命令处理器,以及 `ensureLoad`:查 host,等 waitFor,版本不对就重新注入,再轮询 `ready()`,最长 30 秒 |
53
+ | `extension/cloud-policy.js`、`cloud.js` | 云端可以用 load / call。confirm 标记**只从注册表来**,服务器发来的 `confirm` 字段一律丢掉;`confirmCall()` 负责弹窗 |
54
+ | `src/mcp-server.js` | 本地 MCP 的 `load` / `call` 工具 |
55
+ | `test/loads.test.js` | 检查注册表和 load.json 是否同步(含版本指纹)、sha256、白名单里的 action 上游是否都有、confirm 是否都在 write 里、hosts 判定 |
56
+
57
+ ### opus-video-maker 里的文件
58
+
59
+ | 文件 | 作用 |
60
+ |---|---|
61
+ | `gooseboxd-image-eda/` | 镜像定义:image.json、Dockerfile(带自检)、CLAUDE.md、managed-settings.json |
62
+ | `skills/easyeda/SKILL.md` | 工作流程:读现状 → 出计划 → ask 确认 → 小批写入并读回 → 保存 → 写报告 |
63
+ | `skills/easyeda/references/actions.md` | **生成的**,不要手改 |
64
+ | `skills/easyeda/references/pitfalls.md` | 手写的踩坑记录 |
65
+ | `server.js` / `gen.js` / `images.js` / `public/index.html` | `load` 字段,以及按它选镜像 |
66
+ | `extlink.js`、`skills/user-browser/.../browser.mjs` | 云端命令清单里加了 load、call |
67
+
68
+ ---
69
+
70
+ ## 3. 安全设计(改动前先看)
71
+
72
+ - **云端只能按名字点能力包、按白名单调 action,不能下发任何 JS。** 云端拿不到 `eval`。
73
+ - **白名单查三遍**:cloud-policy.js(云端),background.js(扩展),entry.js(页面)。
74
+ - **会删东西或整片重做的 action 由扩展自己弹确认**,不靠 agent 记得先 ask。全局免确认和「本任务内都同意」可以盖过它。拒绝或 5 分钟没人点,都回 `CONFIRM_DECLINED`。
75
+ - **能力包运行在用户已登录的编辑器页面里**,权限和用户一样,能读写他所有工程。所以每次升级上游都要审代码,见 4.2。
76
+ - 用户的真实设计上不做试验。测试工程:
77
+ - 名称:`API-Test-20261030`
78
+ - uuid:`da5c2483a06745698a2acd1085e5aebe`
79
+ - 原理图页:`34f3d48784a1ed0b`
80
+ - `HS-Toy-4G-Lierda`(`507f709b…`)是用户的真实设计。
81
+
82
+ ---
83
+
84
+ ## 4. 上游 easyeda-agent 怎么更新
85
+
86
+ 上游是 https://github.com/zhoushoujianwork/easyeda-agent (MIT AND Apache-2.0)。我们只用它的 `extension/src/actions.ts` 这一层(`runAction` 和 HANDLERS),**不用** `transport.ts`:那是它连自己本地服务的通道。
87
+
88
+ 选它而不选 easyeda-api-skill,是因为后者的提交历史是假的:commit 时间全挤在半年前。
89
+
90
+ ### 4.1 什么时候升级
91
+
92
+ - 上游修了我们碰到的问题,或者加了我们要的 action。
93
+ - 嘉立创EDA 编辑器大版本更新后,有 action 坏了。
94
+ - **不需要追着上游升级**。commit 是钉死的,不升级就不会变。
95
+
96
+ ### 4.2 步骤
97
+
98
+ 以下命令都在 vcorp-chrome 仓库根目录、Git Bash 里跑。`OLD` 是 `load.json` 里现在的 commit,`NEW` 是要升到的 commit。
99
+
100
+ **1. 拿到新 commit,看上游改了什么。** 缓存目录本身是完整的 clone,可以直接 fetch:
101
+
102
+ ```bash
103
+ C=.cache/loads/eda@7a73a6834d8d # 现在的缓存目录;没有就先 npm run build:loads eda
104
+ git -C $C fetch -q origin
105
+ git -C $C log --oneline origin/HEAD -5 # 选一个 NEW,记下完整 40 位
106
+ OLD=7a73a6834d8d76fd9765dd06f9d4e4bdd6b0556a; NEW=<40位>
107
+ git -C $C log --oneline $OLD..$NEW
108
+ git -C $C diff --stat $OLD $NEW -- extension/src extension/package.json internal/protocol/actions.go
109
+ ```
110
+
111
+ **2. 审代码。不能跳过。** 新代码会以用户身份在他的编辑器里运行。
112
+
113
+ ```bash
114
+ git -C $C diff $OLD $NEW -- extension/src | grep -nE '^\+.*(fetch\(|XMLHttpRequest|WebSocket|sendBeacon|new Function|eval\(|importScripts|https?://)'
115
+ ```
116
+
117
+ 逐条看命中的地方,确认没有把数据发到编辑器以外的地方,也没有执行字符串代码。可疑就不升级。同时看一眼提交作者和时间是否正常。
118
+
119
+ **3. 对比 action 目录,决定白名单。**
120
+
121
+ ```bash
122
+ for c in $OLD $NEW; do git -C $C show $c:internal/protocol/actions.go | sed -nE 's/.*Name:[[:space:]]*"([^"]+)".*/\1/p' | sort > /tmp/act-${c:0:7}.txt; done
123
+ comm -3 /tmp/act-${OLD:0:7}.txt /tmp/act-${NEW:0:7}.txt # 左列:上游删了的;右列:新加的
124
+ ```
125
+
126
+ - **上游删掉或改名的 action**:如果在我们的白名单里,就从 `load.json` 去掉或改名。不处理的话,`test/loads.test.js` 会报「上游没有 action X」。
127
+ - **新加的 action 默认不开放。** 白名单之外的 action 一律拒绝,所以新 action 不加进 `load.json` 就什么都不会发生。要开放时,按上游的 `Mutates` 决定放进 `read` 还是 `write`:
128
+ - 会删东西、整片重做或清空的,同时加进 `confirm`,值是给用户看的中文说明,格式是「要……」后面接的那半句。上游的 `NeedsConfirm` 可以参考,但 confirm 清单是我们自己定的。
129
+ - 会删整页、删板子、删库、导入导出工程文件、碰账号的,放进 `denied`,写明理由。
130
+ - 上游改了某个 action 的参数或语义,就去 opus 的 `pitfalls.md` 里查有没有相关条目,过时的删掉。
131
+
132
+ **4. 看依赖和全局变量有没有变。**
133
+
134
+ ```bash
135
+ git -C $C diff $OLD $NEW -- extension/package.json
136
+ git -C $C diff $OLD $NEW -- extension/src | grep -nE "^\+import .* from '[^.]" # 新的第三方依赖
137
+ ```
138
+
139
+ - 上游的第三方依赖要装在**本仓库**的 devDependencies 里,现在只有 `jszip`。esbuild 通过 `nodePaths` 从本仓库的 `node_modules` 找依赖。没装的话构建会报 Could not resolve。
140
+ - 上游开始用新的全局变量(不在 `E*_` / `I*_` 枚举里)时,在 `setup.js` 里补上。
141
+ - `entry.js` 用的是 `import { runAction } from '@upstream/extension/src/actions'`。上游改了文件路径或导出名,这里跟着改,同时改 `load.json` 的 `vendor.entry`。
142
+
143
+ **5. 改 `load.json`,重新构建,跑测试。**
144
+
145
+ ```bash
146
+ # load.json: vendor.commit = NEW(完整 40 位);vendor.version = 上游 extension/package.json 的 version
147
+ git -C $C show $NEW:extension/package.json | grep '"version"'
148
+ npm run build:loads eda # 会新 clone 一份到 .cache/loads/eda@<NEW前12位>/
149
+ npm test
150
+ git status --short # 应该是 load.json、extension/loads/eda/agent.js、extension/loads/index.js,可能还有 LICENSE/NOTICE
151
+ ```
152
+
153
+ 能力包的版本号由构建脚本自动生成,格式是 `上游版本+commit前7位.适配层指纹`,不用手改。
154
+
155
+ **6. 检查 `project.find` 的覆盖还要不要(见 6.4)。** 如果上游修好了不带 `teamUuid` 时清单为空的问题,就删掉 `entry.js` 里的 `findAcrossTeams`,改回直接走 `runAction`。
156
+
157
+ **7. 在真实浏览器里验证。** 先在 `chrome://extensions` 重新加载 unpacked 扩展,然后在**测试工程**上跑:
158
+
159
+ - `load eda`:回执里的版本号应该是新的。
160
+ - 只读 action:`project.current`、`schematic.components.list`、`schematic.check`;有 PCB 时再跑 `pcb.drc.check`。
161
+ - 本次改动涉及的 action。
162
+ - 放一个元件再删掉,然后 `schematic.save`。
163
+
164
+ 放元件可以用的器件:
165
+
166
+ ```json
167
+ {"libraryUuid":"0819f05c4eef4c71ace90d822a990e87","uuid":"b948db94476e4027ac8953235755ec96","x":900,"y":500}
168
+ ```
169
+
170
+ 注意:编辑器加载后的前 20 秒左右,调用可能卡到超时,先等一会儿再验证(6.2)。
171
+
172
+ **8. 重新生成 opus 那边的 actions.md。**
173
+
174
+ ```bash
175
+ python loads/eda/actions-md.py .cache/loads/eda@<NEW前12位>/internal/protocol/actions.go loads/eda/load.json ../opus-video-maker/skills/easyeda/references/actions.md
176
+ ```
177
+
178
+ 脚本最后会打印 `missing from catalog: [...]`,应该是空列表。不空说明白名单里有 action 不在上游目录里,回到第 3 步检查。
179
+
180
+ **9. 发布。顺序不能反。**
181
+
182
+ 1. **先发扩展。**
183
+ - 三处版本号一起改(`package.json`、`package-lock.json`、`extension/manifest.json`),提交、打 tag、推 `my-dev`。
184
+ - 打商店包并提交审核,`npm publish`。
185
+ - 详细步骤见 opus-video-maker 的 `docs/DEPLOY.md` 第 3 节。
186
+ 2. **再更新镜像。** 只有 actions.md 或 skill 变了才需要:opus-video-maker 提交、推 `main`,然后在宿主上 build、push(见第 5 节)。
187
+
188
+ 原因:新开放的 action 在旧扩展里会被白名单拒绝(`LOAD_DENIED`)。商店审核要几小时到几天,这期间旧扩展的用户还会存在,所以 skill 里不要写「必须用某个新 action 才能完成」这类说法。
189
+
190
+ ### 4.3 回滚
191
+
192
+ 把 `load.json` 的 `vendor.commit` 和 `version` 改回去,重新构建,发一个新版本的扩展。`extension/loads/` 是入库的,直接 revert 那个提交也可以。
193
+
194
+ ---
195
+
196
+ ## 5. opus-eda 镜像怎么重建
197
+
198
+ 宿主是 `wizard@192.168.68.2`,目录在 `~/ovm-images/`。
199
+
200
+ **宿主上的 `opus-video-maker` 不是 git 仓库**,是导出的副本,所以 `remote.sh update` 用不了。按下面的办法送代码过去,旧副本改名留作回滚:
201
+
202
+ ```bash
203
+ # 本机,D:\working\opus-video-maker,main 已经推送
204
+ git -c core.autocrlf=false archive --format=tar main | ssh wizard@192.168.68.2 'set -e; cd ~/ovm-images; rm -rf opus-video-maker.new; mkdir opus-video-maker.new; tar -x -C opus-video-maker.new; mv opus-video-maker opus-video-maker.prev-$(date +%m%d%H%M); mv opus-video-maker.new opus-video-maker'
205
+
206
+ # 构建 + 推送(约 1–2 分钟,基础层走缓存)
207
+ ssh wizard@192.168.68.2 'cd ~/ovm-images && bash opus-video-maker/gooseboxd-host/remote.sh build opus-eda && bash opus-video-maker/gooseboxd-host/remote.sh push opus-eda --model openai/gpt-6.1-terra'
208
+ ssh wizard@192.168.68.2 'cd ~/ovm-images && bash opus-video-maker/gooseboxd-host/remote.sh status' | grep -A2 opus-eda
209
+ ```
210
+
211
+ - `core.autocrlf=false` 一定要带,否则脚本会变成 CRLF。
212
+ - 2026-10-30 的旧副本还留在 `opus-video-maker.prev-01ca40e`。
213
+ - `skills/user-browser` 或 `huashu-chrome-notes` 改了的话,五张浏览器类镜像都要重建(见 DEPLOY.md 2.5)。
214
+
215
+ ---
216
+
217
+ ## 6. 实测踩过的坑(都已处理)
218
+
219
+ ### 6.1 嘉立创EDA 加载时会整页重写,扩展的浮层被挂丢
220
+
221
+ 编辑器的 `loader.js` 用 `document.open(); document.writeln(…); document.close()` 换掉整个 `<html>`。window 和扩展 isolated world 里的状态都还在,但 `mark.js` 挂上去的浮层节点脱离了文档。
222
+
223
+ 结果是:EDA 页面上从来没有出现过边框和光标,确认弹窗画进了一个看不见的节点,后台一直等到超时。
224
+
225
+ 修复在 `8784cc4`:`ensureHost` 发现节点脱离了文档就重新挂回去。以后别的站点如果出现「弹窗看不见、ask 超时」,先查这一点。
226
+
227
+ ### 6.2 编辑器刚打开的头 20 秒左右不稳定
228
+
229
+ 这段时间 `schematic.page.open`、`schematic.component.place` 可能卡到超时,不返回也不报错。等一会儿再调就正常。
230
+
231
+ 这一条已写进 `pitfalls.md`:`load` 之后先等 20 秒,前几次调用的 timeout 给 30000。
232
+
233
+ ### 6.3 后台标签页里 `document.current` 会误报
234
+
235
+ 后台标签页里它可能报 `No active document`,其实页面是开着的。判断页面开没开,用 `schematic.components.list` 这类读操作。已写进 `pitfalls.md`。
236
+
237
+ ### 6.4 `project.find` 不给 teamUuid 时永远找不到(1.8.1 修)
238
+
239
+ 上游在没有 `teamUuid` 时调 `getAllProjectsUuid()`,V4 上这个不带团队的清单是空的。能力包里也没有哪个 action 能拿到 teamUuid。
240
+
241
+ 第一次真实布线任务就卡在这里:agent 正确地没去猜工程,停下来向用户要链接。
242
+
243
+ `entry.js` 里的 `findAcrossTeams` 改成遍历用户所在的每个团队,查团队根目录和每个文件夹,返回结构和判定规则照上游。
244
+
245
+ **文件夹那条路还没有用真实数据验证过**:用户的工程都不在文件夹里。
246
+
247
+ ### 6.5 其他
248
+
249
+ - **刷新页面后的时序**:页面刷新后 `_EXTAPI_ROOT_` 先出现,工程过几秒才打开。由 `ready()` 回「没准备好」、`ensureLoad` 轮询来兜住。
250
+ - **适配层改动要能被页面感知**:只改了 entry.js 或 setup.js 时,页面上那份旧代码也要被换掉,所以版本号里带了适配层的指纹。
251
+ - **从没切到前台过的后台 EDA 标签页**:有一次 `load` 回了 `LOAD_NOT_READY`(工程一直没打开),之后没能复现,后台标签页 `load` 一般 8–11 秒就成功。如果再出现,先把标签页切到前台看一眼。
252
+
253
+ ---
254
+
255
+ ## 7. 开发时的验证工具
256
+
257
+ - **单元测试**:vcorp-chrome 跑 `npm test`,应该 146 个全过。opus-video-maker 跑 `npm test`,有 3 个 sandbox 归档用例在干净代码上也失败,是旧问题。
258
+ - **本机直连 bridge**:写一个最小 WebSocket 客户端,读 `~/.vcorp-chrome/bridge.json` 拿端口和 token,发 `hello` 再发 `cmd`。不要用 `BridgeClient.connect()`,它会杀掉版本不一致的桥。
259
+ - **审计日志**:`~/.vcorp-chrome/audit.jsonl` 记了每条命令和结果,时间是 UTC。排查「卡住了」时先看这里,能分清是扩展慢,还是客户端没收到回包。
260
+ - **在 Node 里跑真实的 cloud.js**:mock 掉 `chrome.storage`,假装一个服务器 socket,`deps.run` 转发到 bridge。这样可以不经过线上服务器测云端确认。
261
+ - **假服务器必须回 `ping`**,否则 cloud.js 过了 `DEAD_MS` 会主动断开,长时间等确认之后的回包会丢掉。
262
+ - 这套脚本是临时写的,没有入库。
263
+
264
+ ---
265
+
266
+ ## 8. 还没做的
267
+
268
+ - `project.find` 的文件夹分支,等有在文件夹里的工程时验证一次。
269
+ - `pro.easyeda.com`(国际版,V3)只做过早期探索,没有用能力包完整跑过。
270
+ - 能力包机制是通用的,加新站点照 `loads/eda/` 的结构来:
271
+ 1. 写 `load.json`、`setup.js`、`entry.js`。
272
+ 2. 跑 `npm run build:loads <name>`。
273
+ 3. opus 那边在 `BROWSE_LOADS` 加名字,加镜像目录和环境变量,表单里加选项。
274
+
275
+ ## 9. 相关提交
276
+
277
+ - vcorp-chrome `my-dev`:
278
+ - `dc87780` load/call 和 eda 能力包
279
+ - `e552e12` 刷新后等工程打开、版本带适配层指纹
280
+ - `4f57bd2` 云端确认
281
+ - `8784cc4` mark.js 被整页重写后的修复
282
+ - `07f111f` project.find 跨团队查找
283
+ - `d8d8ba8` Release 1.8.1
284
+ - opus-video-maker `main`:
285
+ - `f75885b` opus-eda 镜像
286
+ - `20e2e3e` skill:⚠ action 由扩展确认
287
+ - `6db8cdf` pitfalls:启动时序
288
+ - `a08d3aa` 合并
@@ -659,6 +659,22 @@ async function runCloud(cmd, params, tabId, sid, label) {
659
659
 
660
660
  const liveList = async () => (await chrome.storage.session.get(LIVE_SIDS))[LIVE_SIDS] || [];
661
661
 
662
+ // 桥的名单只有桥能更新,而桥一死就没人再推了——扩展不自己清,死会话会一直算在线,
663
+ // 它们留在页面上的标记也摘不掉。只在确认重连失败之后调(见 hc-keepalive),
664
+ // 瞬断时不能清,否则标记会闪一下。桥回来时会在 welcome 后补一份真名单。
665
+ async function dropBridgeSessions() {
666
+ const { [BRIDGE_SIDS]: bridge = [] } = await chrome.storage.session.get(BRIDGE_SIDS).catch(() => ({}));
667
+ if (bridge.length) await onBridgeEvent({ event: 'sessions', live: [] });
668
+ }
669
+
670
+ // 面板显示用:没连着桥时只有云端名单可信。比 dropBridgeSessions 更早生效,
671
+ // 否则「未连接」旁边最多还会挂 30 秒已经过期的本机 agent。
672
+ async function shownSids() {
673
+ if (await connected()) return liveList();
674
+ const { [CLOUD_SIDS]: cloud = [] } = await chrome.storage.session.get(CLOUD_SIDS);
675
+ return cloud;
676
+ }
677
+
662
678
  // 这个标签页此刻的主人们。只算**还连着**的会话——判据和 liveOwnersOf 一致:
663
679
  // 已结束的会话留下的槽不算数,否则关掉一个终端窗口之后,它的标记会永远挂在那儿。
664
680
  //
@@ -2857,7 +2873,9 @@ chrome.alarms?.onAlarm.addListener(async () => {
2857
2873
  const kicked = await chrome.runtime.sendMessage({ __hcBridge: 'kick' }).catch(() => null);
2858
2874
  if (kicked?.connected) return setBadge(true);
2859
2875
  await directConnect();
2860
- setBadge(await connected());
2876
+ const up = await connected();
2877
+ setBadge(up);
2878
+ if (!up) await dropBridgeSessions();
2861
2879
  });
2862
2880
  chrome.tabs.onRemoved.addListener(async (tabId) => {
2863
2881
  // 全局槽 + 所有会话级槽一次扫清,别留指向死 tab 的槽
@@ -2968,7 +2986,7 @@ chrome.runtime.onMessage.addListener((m, sender, sendResponse) => {
2968
2986
  if (m.__hcPopup === 'sessions') {
2969
2987
  (async () => {
2970
2988
  try {
2971
- const [all, lives] = await Promise.all([chrome.storage.local.get(null), liveList()]);
2989
+ const [all, lives] = await Promise.all([chrome.storage.local.get(null), shownSids()]);
2972
2990
  const clients = await chrome.storage.session.get(lives.map(sidClientKey));
2973
2991
  const rows = [];
2974
2992
  for (const sid of lives) {
@@ -24,6 +24,7 @@
24
24
  // 自动允许只放行这一个任务,不往名单里加。一个任务新批的网站最多 SITES_MAX 个。
25
25
 
26
26
  import { checkLoadParams, checkCallParams } from './load.js';
27
+ import { cleanApps } from './desktop-policy.js';
27
28
 
28
29
  export const DEFAULT_ORIGIN = 'https://maker.vcorp.ai';
29
30
 
@@ -37,6 +38,9 @@ export const CLOUD_CMDS = new Set([
37
38
  // 能力包:只能按名字装扩展包里自带的包、按白名单调 action,payload 是纯数据(见 load.js)。
38
39
  // 这是云端拿到「有类型的站点能力」的唯一途径,eval 依旧没有。
39
40
  'load', 'call',
41
+ // 桌面:经同机的原生宿主转给 agent-desktop。按任务批准应用(apps),规矩在 desktop-policy.js;
42
+ // 用户没在侧栏里启用桌面能力时宿主根本连不上,这几条只会报「没有桌面设备」。
43
+ 'desktop', 'apps', 'devices',
40
44
  ]);
41
45
 
42
46
  // ---------- 站点申请 ----------
@@ -348,6 +352,16 @@ export function checkCommand(cmd, params, { hosts, origin }) {
348
352
  if (!sites.length && !bad.length) throw err('CLOUD_DENIED', 'sites 要给 add:要访问的网站,如 ["zhipin.com"]');
349
353
  return { add: sites, bad, reason: String(p.reason || '').replace(/\s+/g, ' ').trim().slice(0, 200) };
350
354
  }
355
+ case 'apps': {
356
+ // add 可以是空的:那是在问「本任务能操作哪些应用」
357
+ const { apps, bad, overflow } = cleanApps(p.add);
358
+ return { add: apps, bad, overflow, reason: String(p.reason || '').replace(/\s+/g, ' ').trim().slice(0, 200) };
359
+ }
360
+ // desktop 的参数要对着本任务的应用和快照户口审,在 cloud.js 里过 checkDesktop
361
+ case 'desktop':
362
+ return p;
363
+ case 'devices':
364
+ return {};
351
365
  case 'folders': {
352
366
  // use 可以是空的:那是在问「这个浏览器挂了哪些」,不是在申请。
353
367
  const { names, bad } = cleanFolderNames(p.use);