dsh-inner-browser-use 0.27.0 → 0.34.95
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 +53 -18
- package/README.zh.md +26 -14
- package/client.js +262 -28
- package/docs/ARCHITECTURE.md +15 -12
- package/docs/ARTIFACTS.md +19 -0
- package/docs/BENCH.md +568 -17
- package/docs/FLASH.md +84 -0
- package/docs/ROADMAP.md +107 -13
- package/docs/VERIFY.md +20 -10
- package/index.js +456 -45
- package/package.json +20 -3
- package/read.js +734 -29
- package/review.js +213 -0
- package/runtime/flash-app/main.js +232 -0
- package/runtime/flash-app/package.json +1 -0
- package/runtime/flash-setup.mjs +151 -0
- package/runtime/swf.mjs +233 -0
- package/scripts/check-flash-local.mjs +104 -0
- package/scripts/check-flash-mirror.mjs +73 -0
- package/scripts/check-flash-tools.mjs +74 -0
- package/scripts/check-flash-window.mjs +58 -0
- package/scripts/doctor.mjs +3 -1
- package/sidecar.js +1763 -106
- package/tools.js +1667 -191
package/docs/FLASH.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Flash 之后:把兼容性放在正确的位置上
|
|
2
|
+
|
|
3
|
+
Chrome 88(2021 年 1 月)拿掉 Flash,不是商业决定,是插件模型的失败:一个能直接操作音频、图形、网络和本地文件的第三方二进制,安全面等于浏览器本身,而它承担的内容又都可以用开放标准重写。结论对自研同样成立:**不要再造插件槽位,要造内容层的运行时。**
|
|
4
|
+
|
|
5
|
+
## 兼容的到底是什么
|
|
6
|
+
|
|
7
|
+
一个 SWF 只有四样东西是「约定」:
|
|
8
|
+
|
|
9
|
+
1. **标签流**——Shape/Sprite/Bitmap/Font/Sound/Frame 的二进制结构,决定画面怎么组出来;
|
|
10
|
+
2. **时间线**——`ShowFrame` 与帧率构成保留模式动画,帧可以跳、可以循环;
|
|
11
|
+
3. **AVM1 / AVM2 语义**——脚本看到的 DisplayObject、事件、`MovieClip`、`SharedObject`;
|
|
12
|
+
4. **安全模型**——跨域、socket 策略、本地存储。
|
|
13
|
+
|
|
14
|
+
只要这四样对得上,游戏就能跑。渲染用 Canvas2D 还是 WebGPU、时钟是实时还是虚拟、存储落 IndexedDB 还是宿主磁盘,**游戏看不见**。这就是「参考原理重做一个更好的」的立足点:兼容性锁在 AVM + 标签流,创新空间在其余全部。
|
|
15
|
+
|
|
16
|
+
## 分层设计
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
SWF ──▶ 摄入(读取标签流, 建 IR) ──▶ 显示列表 + 时间线 ──▶ 渲染(WebGPU, 损伤跟踪)
|
|
20
|
+
│
|
|
21
|
+
├─ AVM 兼容核(Ruffle wasm / 自研 AVM1 解释器)
|
|
22
|
+
├─ 虚拟时钟(固定步长, 输入带时间戳)
|
|
23
|
+
├─ 能力沙箱(网络/存储/剪贴板 走宿主代理, 可录可回放)
|
|
24
|
+
└─ 对象图内省(每帧把状态压成 JSON 差异, 给 agent 读)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
四层的分工决定了「比 Flash 好」不是同一维度的竞速:
|
|
28
|
+
|
|
29
|
+
| 维度 | Flash 的做法 | 这里的做法 | 可测指标 |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| 启动 | 装插件、NPAPI/PPAPI 握手 | 页面内 wasm,零安装 | 首帧 ms |
|
|
32
|
+
| 安全 | 插件进程 + 沙箱补丁 | 无原生代码,网络存储全代理 | 无提权面 |
|
|
33
|
+
| 确定性 | 真实时钟,帧率随机器 | 固定步长虚拟时钟 + 输入时间戳 | 同输入同结果 |
|
|
34
|
+
| 内省 | 无(黑盒像素) | 显示列表与 AVM 状态可导出 | 状态 → JSON 字节数 |
|
|
35
|
+
| 传输 | 屏幕像素 | 损伤区域 + 增量 | 每帧 KB |
|
|
36
|
+
| 回放 | 无 | 事件日志可重放 | 重放一致率 |
|
|
37
|
+
|
|
38
|
+
## 已落地:PPAPI 分支做成插件里的开关(本版)
|
|
39
|
+
|
|
40
|
+
「兼容层」和「能立刻玩」不是二选一。插件现在有两套运行时,同一个 CDP 管线、同一个镜像、同一套工具,切换只是一次重启:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
browser_open({ flash: true, url: 'http://aola.100bt.com/play/play.html?from_h5=true' })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
开的时候宿主用 Electron 11.3.0(Chromium 87,最后一个还认 PPAPI 的 Chromium)当浏览器,关的时候切回 Chrome。输入注入、导航、读页面、镜像全都不用改,因为两边都说 CDP。
|
|
47
|
+
|
|
48
|
+
三条必须同时成立的条件(缺一条就报「Flash 缺失」,且症状会骗人):
|
|
49
|
+
|
|
50
|
+
1. `--ppapi-flash-path` 指向 bundle **里的 Mach-O 可执行文件** `PepperFlashPlayer.plugin/Contents/MacOS/PepperFlashPlayer`,不是 `.plugin` 目录。指错目录时插件报 `No PPP_GetInterface in plugin library`——那是假象,不是导出缺失。
|
|
51
|
+
2. 必须加 `--no-sandbox`。否则 ppapi 进程报 `dlopen(...) (file system sandbox blocked open())`,页面提示没有 Flash。沙箱只对这一个隐藏的独立 profile 关,正常浏览器不关。
|
|
52
|
+
3. 宿主必须是 x86_64(Apple 芯片跑 Rosetta):插件只有 i386/x86_64 切片,arm64 Electron 装不进它。所以下载固定取 `darwin-x64`。
|
|
53
|
+
|
|
54
|
+
Electron 11 的两个真实限制,代码里已按它们写:
|
|
55
|
+
|
|
56
|
+
- 它不会用当前 Chrome 的 profile 目录(会降级并上锁),Flash 分支固定用 `<profile>-flash` 独立目录;两边登录态各自独立。
|
|
57
|
+
- `/json/new` 在 Chromium 87 上是 404,单窗口应用也没有第二个窗口,所以 Flash 分支不建新页签:会话要页签时把唯一那个窗口导航过去。
|
|
58
|
+
|
|
59
|
+
实测数字(Apple 芯片 + Rosetta,1440×900):
|
|
60
|
+
|
|
61
|
+
| 指标 | 值 |
|
|
62
|
+
|---|---|
|
|
63
|
+
| 冷启动到能驱动页面 | 1389 ms(首次含解压;同机热启 545–927 ms) |
|
|
64
|
+
| 页面首帧出图 | 约 8–10 s(SWF 自己的加载),之前是 11 KB 白帧 |
|
|
65
|
+
| 单帧 JPEG q72 | 96–103 KB |
|
|
66
|
+
| `capturePage` 取一帧 | 16 ms |
|
|
67
|
+
| 镜像 SSE 节奏 | 15 fps(66 ms 一轮),4 s 收 60 帧,均值 97.8 KB |
|
|
68
|
+
| PC 版判定 | 页面里出现 `EMBED .../weblogin/20171026_1.swf`、登录框是「多多号/手机/邮箱」而不是 H5 的样式 |
|
|
69
|
+
|
|
70
|
+
**不能随包分发 Adobe Flash Player**:插件本体留在本机状态目录(`~/.dsh/inner-browser-use/flash/plugin/`),由 `npm run flash:setup` 从本机已有的安装里找/拷,找不到就明说;Ruffle 那条路(MIT/BSD)仍是可分发的那条。
|
|
71
|
+
|
|
72
|
+
## 分阶段
|
|
73
|
+
|
|
74
|
+
- **M0 摄入(已发布)**:读 SWF 标签流,给出尺寸/帧率/AVM/资源构成。`runtime/swf.mjs` + `npm run swf`。
|
|
75
|
+
- **MP 立刻能玩(本版)**:PPAPI 分支 + 插件开关,见上节;`npm run flash:setup` 备运行时。
|
|
76
|
+
- **M1 能跑**:自托管 Ruffle + 宿主路由 + `embed/object` 自动 polyfill,在一个真实 SWF 上量首帧、fps、字节数。
|
|
77
|
+
- **M2 能读**:把 Ruffle 的 player 对象与显示列表接到 `browser_eval`,agent 直接读游戏状态,不用截图。
|
|
78
|
+
- **M3 更快**:损伤跟踪 + 增量帧传输;对同一游戏给出「Flash 版 vs 这里」的毫秒与 CPU 对比。
|
|
79
|
+
|
|
80
|
+
## 诚实边界
|
|
81
|
+
|
|
82
|
+
- AVM2 的兼容上限由兼容核决定;Ruffle 官方口径是「多数游戏能玩,仍有 API 缺口」,具体游戏要实测。
|
|
83
|
+
- Stage3D、加密 SWF、依赖本地插件的网游登录器,不在 M1–M3 的承诺内。
|
|
84
|
+
- ZWS(LZMA)本版明确拒绝;补 LZMA 是一天的事,但先要有真实影片来验证。
|
package/docs/ROADMAP.md
CHANGED
|
@@ -50,7 +50,7 @@ stealing the column from other chats.
|
|
|
50
50
|
|
|
51
51
|
## Phase 2 — one call, many steps (target: a login flow in 1 tool call)
|
|
52
52
|
|
|
53
|
-
- `
|
|
53
|
+
- `browser_do`: ordered steps with per-step waits (`wait_text`, `wait_gone`,
|
|
54
54
|
`wait_idle`, `wait_url`), one result: what changed, the final URL, tokens spent
|
|
55
55
|
on one read instead of five.
|
|
56
56
|
- Auto-wait after every action: navigation start/finish, DOM quiet, and
|
|
@@ -65,7 +65,7 @@ stealing the column from other chats.
|
|
|
65
65
|
- File upload (`DOM.setFileInputFiles`), download capture with a returned path,
|
|
66
66
|
print-to-PDF, full-page and element screenshots.
|
|
67
67
|
- Tabs: new / switch / close / list; iframes addressed by frame id; dialog
|
|
68
|
-
accept/dismiss; `
|
|
68
|
+
accept/dismiss; `browser_eval` for the last 1% of cases; cookies read/write for
|
|
69
69
|
debugging; console + failed requests surfaced in the state read.
|
|
70
70
|
- Keyboard: modifiers, shortcuts, clipboard paste, IME composition unchanged.
|
|
71
71
|
|
|
@@ -86,14 +86,14 @@ stealing the column from other chats.
|
|
|
86
86
|
|
|
87
87
|
- Human takeover (0.17.1): real input in the pane (click / text / wheel / key)
|
|
88
88
|
holds the agent's next action for 6 s and says so; hover and move do not count;
|
|
89
|
-
reads stay open; `
|
|
89
|
+
reads stay open; `browser_resume` takes the page back at once.
|
|
90
90
|
- Guarded actions (upload, download, close tab, buy-ish submits) can require a
|
|
91
91
|
confirm flag. Shipped in 0.14.0 for the narrow case that matters most: an
|
|
92
92
|
overlay in the way is cleared only when its own way out is a safe word
|
|
93
93
|
(accept / agree / close / 同意 / 关闭 …) and the overlay says nothing about
|
|
94
94
|
deleting, paying or signing out; otherwise it is reported, never taken.
|
|
95
95
|
Since 0.15.0 the same clearing runs before every control-naming step of a
|
|
96
|
-
`
|
|
96
|
+
`browser_do` batch, so a multi-step flow through a cookie wall fails zero times.
|
|
97
97
|
- Crash recovery: attach to a still-running Chrome, rebuild the page, keep the
|
|
98
98
|
profile; a killed host never leaves a zombie Chrome.
|
|
99
99
|
- Every failure names what happened, what state the page is in, and the next
|
|
@@ -124,10 +124,10 @@ finding the control again by identity (kind + exact words + link destination),
|
|
|
124
124
|
refusing when the words are not unique. Measured at 462 ms against 445 ms for a
|
|
125
125
|
fresh control — 17 ms when needed, nothing when not.
|
|
126
126
|
|
|
127
|
-
Since 0.16.1 the count is visible: `
|
|
127
|
+
Since 0.16.1 the count is visible: `browser_state` reports `refs followed N`,
|
|
128
128
|
and breadth asserts it moved after a redraw, so the next rounds can tell from
|
|
129
129
|
real sites whether redrawing is frequent enough to justify more work here.
|
|
130
|
-
Next in this direction: the same identity recovery for `
|
|
130
|
+
Next in this direction: the same identity recovery for `browser_do` steps that
|
|
131
131
|
name a field by its label when the label element is rebuilt (today the step
|
|
132
132
|
falls back to a fresh read).
|
|
133
133
|
|
|
@@ -142,7 +142,7 @@ then repeat the loop with whatever the numbers say is worst.
|
|
|
142
142
|
Two things were wrong at the edge of the tool surface, and both were only
|
|
143
143
|
reachable on real sites.
|
|
144
144
|
|
|
145
|
-
The first was that the shape of the browser could not change. `
|
|
145
|
+
The first was that the shape of the browser could not change. `browser_open`
|
|
146
146
|
accepted `headless: false` and answered success; when a browser was already
|
|
147
147
|
running nothing happened, because the flag was only read at launch. A site that
|
|
148
148
|
refuses a windowless browser (chatgpt.com on this machine: a "just a moment"
|
|
@@ -150,7 +150,7 @@ page forever, versus a login page in a windowed one) therefore had no way out
|
|
|
150
150
|
except killing a process by hand. Since 0.17.0 a request for the other shape
|
|
151
151
|
restarts the browser on the same profile, cookie jar and address — 21.6 s
|
|
152
152
|
windowless → windowed, 22.1 s back, 0 ms when the shape already matches — and
|
|
153
|
-
both `
|
|
153
|
+
both `browser_open` and `browser_state` say which shape is in front of the
|
|
154
154
|
person.
|
|
155
155
|
|
|
156
156
|
The second was silence on a check page. A site asking for proof of a person is
|
|
@@ -181,7 +181,7 @@ The tool used to treat "the control that says Email" as "the field called
|
|
|
181
181
|
Email". Real forms separate the two, so the writing step now resolves the words
|
|
182
182
|
to the element that takes text, and says which one it used.
|
|
183
183
|
|
|
184
|
-
Next in this direction: the same resolution for `
|
|
184
|
+
Next in this direction: the same resolution for `browser_type` when the caller
|
|
185
185
|
names a field by its words instead of a ref, and for date/time pickers where the
|
|
186
186
|
visible box is a div and the real input is hidden beside it.
|
|
187
187
|
|
|
@@ -189,15 +189,15 @@ visible box is a div and the real input is hidden beside it.
|
|
|
189
189
|
|
|
190
190
|
Typing by name is in. The same resolution is what other steps should be able to
|
|
191
191
|
use when the caller knows the destination but not the number — the next place
|
|
192
|
-
worth doing this is `
|
|
192
|
+
worth doing this is `browser_select` (choose an option by the field it belongs
|
|
193
193
|
to), and after that the date/time case where the visible box is a div and the
|
|
194
194
|
real input sits hidden beside it.
|
|
195
195
|
|
|
196
196
|
## Phase 12 — the same words, the same place, 0.21.0
|
|
197
197
|
|
|
198
|
-
`
|
|
198
|
+
`browser_select` joined `browser_type` and `browser_do` on the shared resolver.
|
|
199
199
|
Left in this direction: the date/time case where the box the user sees is a div
|
|
200
|
-
and the real input is hidden beside it, and `
|
|
200
|
+
and the real input is hidden beside it, and `browser_click` on a control whose
|
|
201
201
|
words are split across sibling nodes (`<span>Sign</span><span>in</span>`).
|
|
202
202
|
|
|
203
203
|
## Phase 13 — the page's spelling is not the caller's, 0.22.0
|
|
@@ -216,7 +216,7 @@ name, so the fallback has to be a picture plus a click, not a name lookup.
|
|
|
216
216
|
|
|
217
217
|
## Phase 16 — a file, not a path, 0.25.0
|
|
218
218
|
|
|
219
|
-
`
|
|
219
|
+
`browser_grab` returns a path. Next: say one line about the picture (size, and
|
|
220
220
|
whether it is mostly one colour) so a model that cannot open files still learns
|
|
221
221
|
"the chart is blank" without a full read.
|
|
222
222
|
|
|
@@ -232,3 +232,97 @@ A picture is only an answer if it loaded. Next: report a large picture element
|
|
|
232
232
|
whose source failed (`naturalWidth === 0`) as a broken picture rather than
|
|
233
233
|
staying silent, since that is the commonest way a generated image "disappears".
|
|
234
234
|
|
|
235
|
+
## Phase 19 — the same picture again, 0.28.0
|
|
236
|
+
|
|
237
|
+
Reads are stateless: the same page costs the same tokens twice. Next: remember
|
|
238
|
+
the last read of a page by address and say only what changed, with a hard reset
|
|
239
|
+
when the address or the document changes.
|
|
240
|
+
|
|
241
|
+
## Phase 20 — two browsers, one conversation, 0.29.0
|
|
242
|
+
|
|
243
|
+
The column is a real browser, so the model inside it can be a reviewer, not just
|
|
244
|
+
a tool: hand it the repository address, ask for a review, read the answer back
|
|
245
|
+
through the same mirror, and take the next round from it. Nothing opens on the
|
|
246
|
+
desktop while that happens: a site that refuses a headless build gets a window
|
|
247
|
+
at -3000,-3000, and the column shows the only picture.
|
|
248
|
+
|
|
249
|
+
## Phase 21 — a review round that closes the loop, 0.30.0
|
|
250
|
+
|
|
251
|
+
`browser_ask` sends and returns an answer. Next: a round trip that carries the
|
|
252
|
+
repository address there and the review back, and records in the round log which
|
|
253
|
+
commit the review was about, so a later round can tell whether the advice was
|
|
254
|
+
already taken.
|
|
255
|
+
|
|
256
|
+
## Phase 22 — the reviewer sees the code, not a description of it, 0.31.0
|
|
257
|
+
|
|
258
|
+
The round log now records what was asked and what came back. Next: paste the
|
|
259
|
+
diff the review is about into the same message (bounded, so the column does not
|
|
260
|
+
fill with code), and let a later round say which points were already acted on,
|
|
261
|
+
so the loop stops repeating advice.
|
|
262
|
+
|
|
263
|
+
## Phase 23 — the drawing half, 0.31.0
|
|
264
|
+
|
|
265
|
+
A page that never shipped an image still has a canvas or a video frame; take
|
|
266
|
+
that surface as the picture when the page has no file to grab.
|
|
267
|
+
|
|
268
|
+
## Phases 24–39, condensed
|
|
269
|
+
|
|
270
|
+
The phases above were written one at a time as the work happened; the run from
|
|
271
|
+
0.32 to 0.34.18 is dense enough that the per-phase prose lives in
|
|
272
|
+
`docs/BENCH.md` and the changelog. What each one changed, in one line:
|
|
273
|
+
|
|
274
|
+
| Version | Phase | What it moved |
|
|
275
|
+
| --- | --- | --- |
|
|
276
|
+
| 0.32.0 | 22 | the diff a review is about travels with the request |
|
|
277
|
+
| 0.33.0 | 22b | the mirror outlives the visit: claim, takeover, remembered address |
|
|
278
|
+
| 0.33.2 | 22c | a duplicate registration cannot take the pane down |
|
|
279
|
+
| 0.34.0 | 23 | one tab per chat, a UA that does not say "headless", typing that looks like hands |
|
|
280
|
+
| 0.34.1 | 24 | a ref is checked against what the control says before it is used |
|
|
281
|
+
| 0.34.2 | 25 | one chat cannot take another chat's page; a password never comes back in a result |
|
|
282
|
+
| 0.34.3 | 26 | a picture reaches the model as a picture; read state is per chat |
|
|
283
|
+
| 0.34.4 | 27 | an empty mirror says what to do; remembering an address is written, not crashed |
|
|
284
|
+
| 0.34.5 | 28 | one ownership gate for every acting route |
|
|
285
|
+
| 0.34.6 | 29 | one tab per chat even when two calls race |
|
|
286
|
+
| 0.34.7 | 30 | a live cookie is never overwritten by a stale copy |
|
|
287
|
+
| 0.34.8 | 31 | a cold first tab in 0.6 s instead of 21 s |
|
|
288
|
+
| 0.34.9 | 32 | a plugin reload no longer closes the browser |
|
|
289
|
+
| 0.34.10 | 33 | a call log never shows another chat's calls |
|
|
290
|
+
| 0.34.11 | 34 | the column offers "restore the last page" in one click |
|
|
291
|
+
| 0.34.12 | 35 | every write needs the chat that asked for it |
|
|
292
|
+
| 0.34.13 | 36 | a review round is verifiable: each point marked fixed / verified / invalid / open |
|
|
293
|
+
| 0.34.14 | 36b | a reload must not cost the browser: the watchdog follows the application pid |
|
|
294
|
+
| 0.34.15 | 37 | an observation admits its blind spot: pictures, canvases, videos, frames; an error page says so |
|
|
295
|
+
| 0.34.16 | 38 | a reload does not blank the mirror: the owning chat's poll adopts the running browser |
|
|
296
|
+
| 0.34.17 | 39 | the blind-spot frame count follows the screen, not the DOM |
|
|
297
|
+
| 0.34.18 | — | the adoption rule is a named function with a test |
|
|
298
|
+
|
|
299
|
+
## 页游这一档(2026-10-28 起)
|
|
300
|
+
|
|
301
|
+
- 操作方式已分叉(0.34.29):`browser_mode` 每个聊天记一种,`human` / `dom` 单次可覆盖。页游默认 human,登录/表单默认 dom 更省事。
|
|
302
|
+
- Flash 判死:headless Chrome 里 Ruffle 交得上 swf 但起不了画面(26 秒,canvas 0、metadata 0),不承诺。真页游只能走站方 H5 / 微端 / 老 360 浏览器,页面上线前先用 `scripts/probe-flash.mjs` 问一句它要不要 Flash。
|
|
303
|
+
- 开玩前先体检:`browser_prepare` 一次调用给出输入面、帧率、按键是否真的到得了框架、以及 Flash 结论(0.34.30)。4399《怪物小汽车》实测页面 58 fps、框架 59 fps —— 不少「Flash 小游戏」其实已经是 H5,先体检再下结论;`npm run ready <url>` 可复现。
|
|
304
|
+
- 读 SWF 标签流(`npm run swf`):尺寸、帧率、AVM 版本、资源构成,FWS/CWS 都读,ZWS 明确报不支持(0.34.32)。
|
|
305
|
+
- 空白画面的两种原因分开报(`viewers` / `listening chats` / `asks.log`),并在「浏览器在跑、没人画」时自己向所属会话补发一次开页签请求(0.34.31)。
|
|
306
|
+
|
|
307
|
+
输入这一层已经到位:按住键、组合键、按住鼠标,一项 300 ms 的按住整次调用 305 ms,事件由页面自己证实。实时画面沿用同一条 CDP 帧路(60 fps、镜像延迟中位数 8 ms)。
|
|
308
|
+
|
|
309
|
+
还差的一层是「看」:4399 / 7k7k / 奥拉星这类页游把画面画在 canvas 上,文本树里没有可读的东西,判断走位、Boss 血条、按钮位置都要靠像素。本会话的模型没有视觉能力,所以这里的正确做法不是假装能玩,而是把可判读的信号做出来:canvas 局部像素采样、颜色/区域变化率、画面是否在动(`blind` 行已经在报 canvas 数量)。有视觉能力的模型接着用同一套帧路就能玩。
|
|
310
|
+
|
|
311
|
+
## Where the next round should go
|
|
312
|
+
|
|
313
|
+
Ranked by what a real task still loses:
|
|
314
|
+
|
|
315
|
+
1. **End-to-end proof of the places unit tests cannot reach.** The restore-in-one-click
|
|
316
|
+
path (0.34.11) and the mirror coming back after a reload (0.34.16) were checked by
|
|
317
|
+
HTTP and by pid, not by a person clicking. A browser-level test that opens the
|
|
318
|
+
column, kills the driver, and clicks restore is the missing evidence.
|
|
319
|
+
2. **One-click "open here" when this chat has no seat.** `state.chats` can lack this
|
|
320
|
+
chat, and the column then only tells the model to call `browser_open`. The button
|
|
321
|
+
belongs in the column.
|
|
322
|
+
3. **Competitor behaviours still unmatched.** Playwright MCP's accessibility refs are
|
|
323
|
+
ours, and the blind line covers the vision-less read; Stagehand v4's
|
|
324
|
+
act/observe/extract split with cached selectors is only partly ours, and its
|
|
325
|
+
self-healing of a selector between rounds has no equivalent here.
|
|
326
|
+
4. **Token numbers for a real session, not a synthetic page.** Reads are measured on
|
|
327
|
+
fixed pages; an actual 20-step task has never had its cumulative token cost
|
|
328
|
+
recorded.
|
package/docs/VERIFY.md
CHANGED
|
@@ -33,7 +33,7 @@ Expected before any tool call: `"alive":false`, `"profile":"default"`,
|
|
|
33
33
|
|
|
34
34
|
## 2. A tool opens its own page
|
|
35
35
|
|
|
36
|
-
Ask the model `用
|
|
36
|
+
Ask the model `用 browser_open 打开 https://www.bing.com`, or call it yourself.
|
|
37
37
|
Expected: the result names the address and title, and this chat's right column
|
|
38
38
|
shows the picture in a tab called **后台浏览器** or **Background browser**.
|
|
39
39
|
`doctor.mjs` now reads `attached, headless`, `viewers 1`, frames climbing.
|
|
@@ -43,8 +43,8 @@ shows the picture in a tab called **后台浏览器** or **Background browser**.
|
|
|
43
43
|
- `"alive":false` with a non-empty `"error"`: Chrome did not start. Check the
|
|
44
44
|
binary at a listed path, and on macOS that the launch carried
|
|
45
45
|
`--use-mock-keychain`.
|
|
46
|
-
- Frames flat while viewers is 1: the screencast stalled. `
|
|
47
|
-
`
|
|
46
|
+
- Frames flat while viewers is 1: the screencast stalled. `browser_stop`, then
|
|
47
|
+
`browser_open` again.
|
|
48
48
|
- `"pointer":null`: no tool input yet; it fills on the first click or move.
|
|
49
49
|
|
|
50
50
|
## 3. Only the chat that asked
|
|
@@ -60,18 +60,28 @@ A mirror that opens in every chat means the page half is not comparing
|
|
|
60
60
|
- **Hover** the search field: your cursor turns into an I-beam, the black arrow
|
|
61
61
|
does not move. An arrow that follows the mouse means the pointer paths are
|
|
62
62
|
crossed.
|
|
63
|
-
- **Type** Chinese with `
|
|
63
|
+
- **Type** Chinese with `browser_type { "text": "…", "submit": true }`: the
|
|
64
64
|
results page appears and the field holds characters, not boxes.
|
|
65
|
-
- **Backspace** with `
|
|
65
|
+
- **Backspace** with `browser_key { "key": "Backspace" }` in a filled field:
|
|
66
66
|
one character goes. Nothing happening means the key reached the document.
|
|
67
|
-
- **Screenshot** with `
|
|
67
|
+
- **Screenshot** with `browser_shot {}`: an absolute path under
|
|
68
68
|
`~/.dsh-inner-browser-use/screenshots`. A black picture means the screencast
|
|
69
69
|
was not running when the frame was taken.
|
|
70
|
+
- **The whole keyboard, on this machine**: `npm run keys` starts a real
|
|
71
|
+
background Chrome, presses all 150 keys into a field, and checks what the page
|
|
72
|
+
reported for each one (`key`, `code`, `keyCode`, shift) plus what the field
|
|
73
|
+
typed. It then runs copy / cut / paste / select-all / undo / redo, caret keys,
|
|
74
|
+
Tab focus, Enter, and a 180 ms hold. One JSON line, exit code 0 when every key
|
|
75
|
+
matched. CI runs it on macOS and Windows in the `keys` job.
|
|
76
|
+
- **A shortcut by hand**: type `Hi`, press `browser_key { "key": "Mod+a" }`,
|
|
77
|
+
type `X`: the field holds `X` and nothing before it. `Mod` is Command on macOS
|
|
78
|
+
and Control elsewhere; `Ctrl+C/X/V/A/Z/Y` and `Ctrl+Shift+Z` run the same
|
|
79
|
+
editing commands a person would.
|
|
70
80
|
|
|
71
81
|
## 5. Stop, then open again
|
|
72
82
|
|
|
73
|
-
`
|
|
74
|
-
`
|
|
83
|
+
`browser_stop` reads `"alive":false` and leaves the profile alone.
|
|
84
|
+
`browser_open https://www.bing.com` brings the same profile back, still signed
|
|
75
85
|
in from the first pass.
|
|
76
86
|
|
|
77
87
|
Signed out after the restart: Chrome came up on another profile, or was killed
|
|
@@ -140,13 +150,13 @@ broke. Two of them cover a cross-site frame, which Chrome runs in its own
|
|
|
140
150
|
process. Three cover a control under an overlay: the click clears a cookie
|
|
141
151
|
banner and lands (`under clicked`), typing into a field under the same banner
|
|
142
152
|
works, and a modal that offers to delete the account is reported without
|
|
143
|
-
anything being pressed. Two more drive the same overlay through `
|
|
153
|
+
anything being pressed. Two more drive the same overlay through `browser_do`:
|
|
144
154
|
a batch step clears the banner before it clicks, and a batch step fills a field
|
|
145
155
|
that was under it. One more counts the recoveries: after the redraw check, the
|
|
146
156
|
state line says at least one ref was followed to its new number. Three cover a
|
|
147
157
|
hand on the mirror: while the person is using it a read still works
|
|
148
158
|
(`hold-allows-read`), an action is refused and names both ways out
|
|
149
|
-
(`hold-refuses-action`), and `
|
|
159
|
+
(`hold-refuses-action`), and `browser_resume` hands the page back so the same
|
|
150
160
|
click goes through (`resume-lets-go`).
|
|
151
161
|
|
|
152
162
|
## Who the mirror serves
|