dsh-inner-browser-use 0.37.0 → 0.38.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/docs/ARCHITECTURE.md +5 -0
- package/docs/BENCH.md +15 -0
- package/package.json +1 -1
- package/tools.js +61 -9
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -160,6 +160,11 @@ Calls that touch the browser are marked not concurrency-safe; reading state, con
|
|
|
160
160
|
requests is. `browser_open`, `browser_view`, `browser_wait` and `browser_shot` carry longer
|
|
161
161
|
timeouts than the rest.
|
|
162
162
|
|
|
163
|
+
`browser_wait` does not poll: one call hands the page a predicate and a `MutationObserver`, and the
|
|
164
|
+
picture comes back on the mutation that satisfies it. A page that changes 137 ms after the wait
|
|
165
|
+
starts answers at 138 ms, not at the next 100 ms tick — the same reason `idle` (a mutation-free
|
|
166
|
+
window) costs one round trip on an animation-heavy page instead of one per frame.
|
|
167
|
+
|
|
163
168
|
Sign-in state lives in the profile, and session cookies — the kind a sign-in usually sets,
|
|
164
169
|
which Chrome would otherwise drop on exit — are saved next to it and restored on the next
|
|
165
170
|
launch, so a restart of the app does not sign the person out.
|
package/docs/BENCH.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## 等到变化,而不是去问(0.38.0)
|
|
2
|
+
|
|
3
|
+
`browser_wait` 以前在宿主侧每 100 毫秒读一次页面判断条件,命中与否都被凑到下一个整拍上。
|
|
4
|
+
现在一次调用把谓词和 `MutationObserver` 交给页面,满足条件的那次变化直接 resolve,回程只走
|
|
5
|
+
一次。
|
|
6
|
+
|
|
7
|
+
| 同一件事 | 0.37.0(宿主轮询) | 0.38.0(页面观察) |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| 五次不同时相的等待,平均耗时 | 205 ms | 140 ms |
|
|
10
|
+
| 每次等待的探测来回 | 3 | 1 |
|
|
11
|
+
| `idle` 判定 | 固定间隔重读页面 | 350 ms 无变化即停,一次来回 |
|
|
12
|
+
|
|
13
|
+
`wait-late` 行就是上表:每次实验在等待开始的同一刻给页面装一个定时器,所以旧路径必然要走到
|
|
14
|
+
下一个 100 毫秒整拍,新路径不必。预算 600 ms,实测 140 ms。
|
|
15
|
+
|
|
1
16
|
## 只列该看的那一个(0.37.0,同一台本地页面,加了两个广告请求)
|
|
2
17
|
|
|
3
18
|
页面自己放一个 `<video src="/movie.mp4">`、一个 `<audio>`、fetch 一个 master 播放列表,再 fetch 两条广告(`/ads/preroll.mp4` 9 MB、`/px/beacon.mp4` 2 B)。不筛选时 5 条都在;默认列表剩 3 条,页面播放器那个标 `main` 排第一,`save: main` 存的就是它。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-inner-browser-use",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "An agent-owned Chromium sidecar mirrored into the DeepSeek Harness right column: the agent drives it, the person watches and can take the mouse, and logins survive restarts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
package/tools.js
CHANGED
|
@@ -1070,13 +1070,64 @@ export async function settle(page, timeout = 5000, since = 0) {
|
|
|
1070
1070
|
* @returns {Promise<boolean>} whether it ever held.
|
|
1071
1071
|
*/
|
|
1072
1072
|
async function until(page, expression, timeout) {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1073
|
+
/* The page watches itself. One round trip that the page resolves on the
|
|
1074
|
+
mutation which matters: no snapshot every 100 ms, no rounding the answer up
|
|
1075
|
+
to the next tick, and nothing to miss between two polls. */
|
|
1076
|
+
const ceiling = Math.max(200, Number(timeout) || 1000)
|
|
1077
|
+
const script = `new Promise((done) => {
|
|
1078
|
+
const deadline = Date.now() + ${String(ceiling)}
|
|
1079
|
+
let watcher = null
|
|
1080
|
+
let timer = null
|
|
1081
|
+
let frame = null
|
|
1082
|
+
const stop = (value) => {
|
|
1083
|
+
if (watcher !== null) watcher.disconnect()
|
|
1084
|
+
watcher = null
|
|
1085
|
+
if (timer !== null) clearTimeout(timer)
|
|
1086
|
+
timer = null
|
|
1087
|
+
if (frame !== null) cancelAnimationFrame(frame)
|
|
1088
|
+
frame = null
|
|
1089
|
+
done(value)
|
|
1090
|
+
}
|
|
1091
|
+
const test = () => { try { return Boolean(${expression}) } catch (error) { return false } }
|
|
1092
|
+
if (test()) return stop(true)
|
|
1093
|
+
watcher = new MutationObserver(() => { if (test()) stop(true) })
|
|
1094
|
+
if (document.documentElement !== null) watcher.observe(document.documentElement, { childList: true, subtree: true, attributes: true, characterData: true })
|
|
1095
|
+
const tick = () => {
|
|
1096
|
+
if (test()) return stop(true)
|
|
1097
|
+
if (Date.now() >= deadline) return stop(false)
|
|
1098
|
+
frame = requestAnimationFrame(tick)
|
|
1099
|
+
}
|
|
1100
|
+
frame = requestAnimationFrame(tick)
|
|
1101
|
+
timer = setTimeout(() => stop(false), deadline - Date.now())
|
|
1102
|
+
})`
|
|
1103
|
+
const answer = await evaluate(page, script, undefined, ceiling + 3000)
|
|
1104
|
+
return answer === true
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* How long the page has been still: a mutation resets the clock, and the call
|
|
1109
|
+
* comes back the moment nothing has moved for `quiet` ms. Reads no node, so an
|
|
1110
|
+
* animation-heavy page costs the same as a blank one.
|
|
1111
|
+
*/
|
|
1112
|
+
async function quietInPage(page, timeout = 5000, quiet = 350) {
|
|
1113
|
+
const ceiling = Math.max(300, Number(timeout) || 5000)
|
|
1114
|
+
const script = `new Promise((done) => {
|
|
1115
|
+
const ceiling = Date.now() + ${String(ceiling)}
|
|
1116
|
+
const settleMs = ${String(Math.max(50, Number(quiet) || 350))}
|
|
1117
|
+
let changedAt = Date.now()
|
|
1118
|
+
let frame = null
|
|
1119
|
+
const watcher = new MutationObserver(() => { changedAt = Date.now() })
|
|
1120
|
+
if (document.documentElement !== null) watcher.observe(document.documentElement, { childList: true, subtree: true, attributes: true, characterData: true })
|
|
1121
|
+
const tick = () => {
|
|
1122
|
+
const now = Date.now()
|
|
1123
|
+
if (now - changedAt >= settleMs) { watcher.disconnect(); return done(true) }
|
|
1124
|
+
if (now >= ceiling) { watcher.disconnect(); return done(false) }
|
|
1125
|
+
frame = requestAnimationFrame(tick)
|
|
1126
|
+
}
|
|
1127
|
+
frame = requestAnimationFrame(tick)
|
|
1128
|
+
})`
|
|
1129
|
+
const answer = await evaluate(page, script, undefined, ceiling + 3000)
|
|
1130
|
+
return answer === true
|
|
1080
1131
|
}
|
|
1081
1132
|
|
|
1082
1133
|
/**
|
|
@@ -3150,8 +3201,9 @@ export function sidecarTools(sidecar, refs, host = {}) { // ui-copy-ignore
|
|
|
3150
3201
|
const at = await until(page, `location.href.includes(${JSON.stringify(String(args.url))})`, timeout)
|
|
3151
3202
|
return at ? `at ${String(await evaluate(page, 'location.href'))}` : `address never contained ${String(args.url)}`
|
|
3152
3203
|
}
|
|
3153
|
-
await
|
|
3154
|
-
|
|
3204
|
+
const quiet = await quietInPage(page, timeout)
|
|
3205
|
+
if (typeof page.adoptOpened === 'function') await page.adoptOpened().catch(() => '')
|
|
3206
|
+
return quiet ? 'idle' : 'still moving'
|
|
3155
3207
|
}),
|
|
3156
3208
|
},
|
|
3157
3209
|
{
|