@restregular/dsh-visual-browser 0.1.1 → 0.2.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
@@ -1,129 +1,160 @@
1
- # dsh-visual-browser
2
-
3
- A DeepSeek Harness plugin that gives the Agent a **real Chrome** and gives you a **window into it**.
4
-
5
- - The Agent never reads the DOM. It reads a numbered list of *visible interactive
6
- elements* plus a short text excerpt — a few hundred tokens per page instead of a
7
- full snapshot.
8
- - Every action the Agent takes (click, type, key, scroll, navigation) is mirrored
9
- into a **面板 (panel)** with the page frame, an operation timeline you can replay,
10
- and a picture you can click to take over the browser yourself.
11
- - Cookies persist across restarts (one dedicated Chrome profile), so a login the
12
- Agent performs once keeps working.
13
-
14
- ## Install
15
-
16
- ```bash
17
- # The plugin is a normal DSH bundle; install it from the registry
18
- dsh plugin add @restregular/dsh-visual-browser
19
- ```
20
-
21
- Or from the Harness UI: **Plugins → Add → `@restregular/dsh-visual-browser`**.
22
-
23
- Requirements: Google Chrome, Microsoft Edge, or Chromium on the machine
24
- (auto-detected), and Node.js 22 or newer (the plugin ships no runtime
25
- dependencies; it talks CDP over the built-in WebSocket).
26
-
27
- ## Use
28
-
29
- 1. Click the **浏览器 / Browser** icon in the sidebar to open the panel.
30
- 2. Ask the Agent something that needs a website, for example
31
- *"打开 example.com 看看首页写了什么"*. The Agent calls `browser_open`.
32
- 3. Watch the frame update. Click the picture to click the page; wheel over it to
33
- scroll; use the toolbar for back / forward / reload / live view / stop.
34
-
35
- ## Tools the Agent gets
36
-
37
- | Tool | Purpose |
38
- | --- | --- |
39
- | `browser_open` | Open a URL (launches Chrome on first use) and return the numbered view. |
40
- | `browser_see` | Fresh numbered view of the current page. |
41
- | `browser_act` | `click` / `type` / `key` / `hover` / `scroll` / `select` / `back` / `forward` / `reload` / `wait` / `close`, then return the new view. |
42
- | `browser_text` | Read text of the page or of one element (a link also returns its `href`). |
43
- | `browser_screenshot` | Save a PNG/JPEG to disk and return the path (feed it to `read_image`). |
44
-
45
- ### What the Agent sees
46
-
47
- ```
48
- URL: https://example.com/login
49
- TITLE: 登录 - Example
50
- VIEW: 1280x800 scroll 0/2400 · 4/6 interactive
51
- [1] textbox "手机号" value=13800000000
52
- [2] textbox "密码" value=••••••
53
- [3] button "登录"
54
- [4] link "忘记密码" → /forgot
55
- HEADINGS: 用户登录 | 没有账号?
56
- TEXT: 用户登录 手机号 密码 登录 忘记密码
57
- ACTIVE: [1] textbox "手机号"
58
- ```
59
-
60
- Numbers are reassigned on every view, so an action always uses the newest
61
- numbers; a stale one fails with a clear "take a fresh snapshot" message instead
62
- of clicking the wrong element.
63
-
64
- ## Configuration
65
-
66
- Row config (in the profile's `cordis.patch.yml`, override by row id
67
- `dsh-visual-browser`):
68
-
69
- ```yaml
70
- - id: dsh-visual-browser
71
- config:
72
- headless: true # false = visible Chrome window
73
- executablePath: '' # explicit chrome.exe path
74
- userDataDir: '' # default: ~/.dsh/visual-browser/chrome-profile
75
- viewport:
76
- width: 1280
77
- height: 800
78
- maxElements: 100 # cap on the numbered list
79
- maxTextChars: 320 # cap on the text excerpt
80
- frameQuality: 62 # JPEG quality of panel frames
81
- timelineFrames: 24 # replayable frames kept in memory
82
- allowUserControl: true # allow clicks from the panel
83
- reuseExisting: true # adopt a Chrome that already owns the profile
84
- actionTimeoutMs: 12000
85
- navigationTimeoutMs: 30000
86
- ```
87
-
88
- ## How it works
89
-
90
- - **Host** (`index.js`, `lib/`): launches Chrome with
91
- `--remote-debugging-port=0 --user-data-dir=<profile>`, parses the DevTools
92
- endpoint from stderr, attaches one flattened CDP session, and exposes:
93
- - five tools through the `tools` service (registration is effect-scoped),
94
- - a same-origin prefix route `/visual-browser/api` (`/state`, `/frame`,
95
- `/live`, `/start`, `/stop`, `/capture`, `/click`, `/wheel`, `/key`, `/nav`)
96
- that the panel polls and posts to.
97
- - **Snapshot** (`lib/page-scripts.js`): one serialized function runs in the page,
98
- filters interactive elements to the viewport, assigns `data-dsh-vb-ref`
99
- numbers, and returns structured JSON. Nothing else about the DOM leaves the
100
- page.
101
- - **Frames** stay on the host: the panel displays the newest JPEG by sequence
102
- number, so a live picture costs no session tokens. The timeline holds the last
103
- N frames; clicking a row replays that moment.
104
- - **One code path**: panel clicks, wheel events and keys run the same
105
- `BrowserManager` methods the tools use.
106
- - **Survives a host restart**: Chrome writes `DevToolsActivePort` into its
107
- profile, so a restarted host (or one whose previous host crashed with Chrome
108
- still alive) attaches to that browser instead of failing on the profile lock.
109
- An adopted browser is only detached — its process is not killed.
110
-
111
- Chrome is killed with its process tree when the plugin unloads.
112
-
113
- ## Notes and limits
114
-
115
- - Cross-origin iframes are counted but not inspected; open the direct URL when a
116
- flow lives inside a frame.
117
- - Blocking JS dialogs are auto-accepted and logged in the timeline.
118
- - The panel route is served by the Harness web server on its existing origin;
119
- it performs no extra authentication, matching the other local panel routes.
120
- - Headless mode is the default. Some sites behave differently headless; set
121
- `headless: false` when a site insists on a window.
122
-
123
- ## Development
124
-
125
- ```bash
126
- npm test # syntax checks + snapshot/format/route/package smoke tests
127
- ```
128
-
129
- MIT © RestRegular
1
+ # dsh-visual-browser
2
+
3
+ A DeepSeek Harness plugin that gives the Agent a **real Chrome** and gives you a **live window into it** — as a tab in the **right Sidebar**, right under the built-in *Browser* entry.
4
+
5
+ - The Agent never reads the DOM. It reads a numbered list of *visible interactive
6
+ elements* plus a short text excerpt — a few hundred tokens per page instead of a
7
+ full DOM snapshot.
8
+ - The tab streams Chrome's own screencast (CDP `Page.startScreencast`) as a
9
+ length-prefixed JPEG stream, decoded off the main thread onto a canvas. No
10
+ screenshot polling, no per-frame request, no re-render of the panel per frame.
11
+ - Your pointer, wheel and keyboard go straight to the DevTools input domain in
12
+ animation-frame batches: click, drag, scroll and type with normal latency
13
+ instead of "screenshot after the fact" latency.
14
+ - Every action — the Agent's and yours — lands in a collapsible, scrollable
15
+ **operation log**. Records only: there is nothing to replay.
16
+ - Cookies persist across restarts (one dedicated Chrome profile), so a login the
17
+ Agent performs once keeps working.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ dsh plugin add @restregular/dsh-visual-browser
23
+ ```
24
+
25
+ Or from the Harness UI: **Plugins → Add → `@restregular/dsh-visual-browser`**.
26
+
27
+ Requirements: Google Chrome, Microsoft Edge, or Chromium on the machine
28
+ (auto-detected), and Node.js 22 or newer (the plugin ships no runtime
29
+ dependencies; it talks CDP over the built-in WebSocket).
30
+
31
+ ## Use
32
+
33
+ 1. Open the right Sidebar. Under the built-in **浏览器 / Browser** card sits the
34
+ Visual Browser row — click it, or click the **浏览器** card itself and then the
35
+ extra row beneath it. The tab opens and replaces the guide in that pane.
36
+ 2. Ask the Agent something that needs a website, for example
37
+ *"打开 example.com 看看首页写了什么"*. The Agent calls `browser_open`.
38
+ 3. Watch the picture. Click, drag, wheel and type straight into it; the toolbar
39
+ has back / forward / reload / stop, and the address bar opens any URL.
40
+
41
+ The operation log at the bottom remembers the Agent's actions and yours. Click
42
+ its header to fold it away, or scroll it when the list is longer than the pane.
43
+
44
+ ### Dark mode
45
+
46
+ The panel only uses `--dsw-alias-*` theme tokens for surfaces, text, borders,
47
+ hover and focus, so light and dark (and theme switches) need no extra rules. The
48
+ only literal colour in the bundle is the `#fff` fallback after
49
+ `--dsw-alias-label-primary-foreground`.
50
+
51
+ ## Tools the Agent gets
52
+
53
+ | Tool | Purpose |
54
+ | --- | --- |
55
+ | `browser_open` | Open a URL (launches Chrome on first use) and return the numbered view. |
56
+ | `browser_see` | Fresh numbered view of the current page. |
57
+ | `browser_act` | `click` / `type` / `key` / `hover` / `scroll` / `select` / `back` / `forward` / `reload` / `wait` / `close`, then return the new view. |
58
+ | `browser_text` | Read text of the page or of one element (a link also returns its `href`). |
59
+ | `browser_screenshot` | Save a PNG/JPEG to disk and return the path (feed it to `read_image`). |
60
+
61
+ ### What the Agent sees
62
+
63
+ ```
64
+ URL: https://example.com/login
65
+ TITLE: 登录 - Example
66
+ VIEW: 1280x800 scroll 0/2400 · 4/6 interactive
67
+ [1] textbox "手机号" value=13800000000
68
+ [2] textbox "密码" value=••••••
69
+ [3] button "登录"
70
+ [4] link "忘记密码" → /forgot
71
+ HEADINGS: 用户登录 | 没有账号?
72
+ TEXT: 用户登录 手机号 密码 登录 忘记密码
73
+ ACTIVE: [1] textbox "手机号"
74
+ ```
75
+
76
+ Numbers are reassigned on every view, so an action always uses the newest
77
+ numbers; a stale one fails with a clear "take a fresh snapshot" message instead
78
+ of clicking the wrong element.
79
+
80
+ ## Configuration
81
+
82
+ Row config (in the profile's `cordis.patch.yml`, override by row id
83
+ `dsh-visual-browser`):
84
+
85
+ ```yaml
86
+ - id: dsh-visual-browser
87
+ config:
88
+ headless: true # false = visible Chrome window
89
+ executablePath: '' # explicit chrome.exe path
90
+ userDataDir: '' # default: ~/.dsh/visual-browser/chrome-profile
91
+ viewport:
92
+ width: 1280
93
+ height: 800
94
+ maxElements: 100 # cap on the numbered list
95
+ maxTextChars: 320 # cap on the text excerpt
96
+ streamQuality: 60 # JPEG quality of the live screencast
97
+ allowUserControl: true # allow clicks/typing from the panel
98
+ reuseExisting: true # adopt a Chrome that already owns the profile
99
+ actionTimeoutMs: 12000
100
+ navigationTimeoutMs: 30000
101
+ ```
102
+
103
+ ## How it works
104
+
105
+ - **Host** (`index.js`, `lib/`): launches Chrome with
106
+ `--remote-debugging-port=0 --user-data-dir=<profile>`, parses the DevTools
107
+ endpoint from stderr, attaches one flattened CDP session, and exposes:
108
+ - five tools through the `tools` service (registration is effect-scoped),
109
+ - a same-origin prefix route `/visual-browser/api`:
110
+ | Route | Purpose |
111
+ | --- | --- |
112
+ | `GET /state` | Status, viewport and the operation log (tiny JSON poll). |
113
+ | `GET /stream` | One long response of `[len][seq][jpeg]` frames. |
114
+ | `POST /input` | A batch of pointer / wheel / key / text events. |
115
+ | `POST /start`, `POST /stop`, `POST /nav` | Open a URL, close Chrome, back/forward/reload. |
116
+ - **Live picture** (`lib/browser.js`): `Page.startScreencast` pushes a JPEG per
117
+ painted frame; the manager acks each frame and fans the same buffer out to every
118
+ panel. The first subscriber restarts the screencast so a still page appears at
119
+ once; the last one stops it, so a closed panel costs nothing. No screenshot is
120
+ taken per click, and nothing about the picture touches the model context.
121
+ - **Input** (`lib/browser.js`): panel events become `Input.dispatchMouseEvent` /
122
+ `Input.dispatchKeyEvent` / `Input.insertText` commands. Nothing waits for a
123
+ snapshot or a capture, so input latency is one round trip; the client batches
124
+ per animation frame so a drag or flick is one request, not thirty.
125
+ - **Snapshot** (`lib/page-scripts.js`): one serialized function runs in the page,
126
+ filters interactive elements to the viewport, assigns `data-dsh-vb-ref`
127
+ numbers, and returns structured JSON. Nothing else about the DOM leaves the
128
+ page.
129
+ - **Survives a host restart**: Chrome writes `DevToolsActivePort` into its
130
+ profile, so a restarted host (or one whose previous host crashed with Chrome
131
+ still alive) attaches to that browser instead of failing on the profile lock.
132
+ An adopted browser is only detached — its process is not killed.
133
+
134
+ Chrome is killed with its process tree when the plugin unloads.
135
+
136
+ ## Notes and limits
137
+
138
+ - Cross-origin iframes are counted but not inspected; open the direct URL when a
139
+ flow lives inside a frame.
140
+ - Blocking JS dialogs are auto-accepted and logged in the operation log.
141
+ - The panel route is served by the Harness web server on its existing origin;
142
+ it performs no extra authentication, matching the other local panel routes.
143
+ - The stream is a raw binary response on purpose: `multipart/x-mixed-replace`
144
+ parsing differs between browser builds, so the panel parses frames itself.
145
+ - Headless mode is the default. Some sites behave differently headless; set
146
+ `headless: false` when a site insists on a window.
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ npm test # syntax checks + snapshot/format/route/package smoke tests
152
+ ```
153
+
154
+ `.dsh-probe/` (git-ignored) holds the manual probes used while developing:
155
+ `live-probe.mjs` drives the real browser end to end, `ui-probe.mjs` renders the
156
+ real client bundle in Chrome against the real host API and drives the panel with
157
+ real DOM events, and `build-css.mjs` copies the panel CSS and theme tokens for
158
+ that harness.
159
+
160
+ MIT © RestRegular