@glittering0717/ai-browser 0.0.0-stage → 1.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/LICENSE +21 -0
- package/README.md +784 -2
- package/bin/ai-browser.cjs +78 -0
- package/examples/AGENT_INSTRUCTIONS.md +166 -0
- package/examples/mcp-config.json +12 -0
- package/llms-install.md +158 -0
- package/package.json +78 -4
- package/scripts/electron_resolve.cjs +241 -0
- package/scripts/start.cjs +60 -0
- package/src/main/axExtractor.js +436 -0
- package/src/main/canvas_hook_source.js +654 -0
- package/src/main/canvas_monitor.js +269 -0
- package/src/main/capability_probe.js +415 -0
- package/src/main/download_monitor.js +137 -0
- package/src/main/index.js +359 -0
- package/src/main/mcp_server.js +408 -0
- package/src/main/mcp_tools.js +289 -0
- package/src/main/mcp_ws.js +178 -0
- package/src/main/network_monitor.js +805 -0
- package/src/main/page_manager.js +2743 -0
- package/src/main/snapshot_diff.js +371 -0
- package/src/main/ws_server.js +440 -0
- package/src/preload/actions.cjs +190 -0
- package/src/preload/bridge.cjs +75 -0
- package/src/preload/extractor.cjs +936 -0
- package/src/preload/watcher.cjs +241 -0
- package/src/renderer/tab_bar.html +115 -0
- package/src/shared/config.js +32 -0
- package/src/shared/deadline.js +89 -0
- package/src/shared/guards.js +17 -0
- package/src/shared/protocol.js +116 -0
- package/src/shared/value_contract.cjs +132 -0
- package/src/shared/version.js +5 -0
- package/src/shared/watchdog.js +57 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/ai-browser.cjs — package entry point.
|
|
3
|
+
//
|
|
4
|
+
// ai-browser launch the visible browser window (foreground)
|
|
5
|
+
// ai-browser start same
|
|
6
|
+
// ai-browser mcp run the MCP stdio server (used by MCP clients)
|
|
7
|
+
// ai-browser --version
|
|
8
|
+
// ai-browser --help
|
|
9
|
+
//
|
|
10
|
+
// This file is CommonJS on purpose: bins run under whatever Node the user has,
|
|
11
|
+
// and the MCP server itself is ESM — loaded via dynamic import below.
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const path = require('node:path');
|
|
15
|
+
const { pathToFileURL } = require('node:url');
|
|
16
|
+
const pkg = require('../package.json');
|
|
17
|
+
|
|
18
|
+
const HELP = `ai-browser ${pkg.version}
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
ai-browser [start] Launch the visible AI Browser window
|
|
22
|
+
ai-browser mcp Run the MCP stdio server (17 browse_* tools)
|
|
23
|
+
ai-browser --version Print version
|
|
24
|
+
ai-browser --help Show this help
|
|
25
|
+
|
|
26
|
+
The browser exposes:
|
|
27
|
+
• MCP server over stdio (this command's "mcp" subcommand)
|
|
28
|
+
• Raw WebSocket JSON-RPC on ws://localhost:9223 (ui.* methods)
|
|
29
|
+
|
|
30
|
+
Docs: https://github.com/Glittering/ai-browser
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
async function main() {
|
|
34
|
+
const argv = process.argv.slice(2);
|
|
35
|
+
const command = argv[0] ?? 'start';
|
|
36
|
+
|
|
37
|
+
switch (command) {
|
|
38
|
+
case 'start':
|
|
39
|
+
case 'gui': {
|
|
40
|
+
// Foreground GUI launch; exit code is forwarded inside the launcher.
|
|
41
|
+
const { launchElectron } = require('../scripts/start.cjs');
|
|
42
|
+
const child = launchElectron();
|
|
43
|
+
child.on('exit', (code, signal) => {
|
|
44
|
+
process.exit(code === null && signal ? 1 : (code ?? 0));
|
|
45
|
+
});
|
|
46
|
+
child.on('error', (e) => {
|
|
47
|
+
console.error('[ai-browser] failed to launch Electron:', e.message);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 'mcp': {
|
|
53
|
+
// Importing the module starts the stdio server (its main() runs on load).
|
|
54
|
+
const serverPath = path.join(__dirname, '..', 'src', 'main', 'mcp_server.js');
|
|
55
|
+
await import(pathToFileURL(serverPath).href);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case '--version':
|
|
59
|
+
case '-v':
|
|
60
|
+
case 'version':
|
|
61
|
+
console.log(pkg.version);
|
|
62
|
+
break;
|
|
63
|
+
case '--help':
|
|
64
|
+
case '-h':
|
|
65
|
+
case 'help':
|
|
66
|
+
process.stdout.write(HELP);
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
console.error(`Unknown command: ${command}\n`);
|
|
70
|
+
process.stdout.write(HELP);
|
|
71
|
+
process.exit(2);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
main().catch((err) => {
|
|
76
|
+
console.error('[ai-browser]', err && err.stack ? err.stack : err);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# AI Browser — Agent Instructions
|
|
2
|
+
|
|
3
|
+
把下面**任意一节**里的代码块整段复制,粘贴进你的 agent 的 memory / instructions / system prompt
|
|
4
|
+
(Claude Code 的 `CLAUDE.md`、Cursor Rules、Codex `AGENTS.md`、或任意 MCP 客户端的自定义指令)。
|
|
5
|
+
不需要改任何东西——浏览器由 agent 自己在第一次调用时拉起。
|
|
6
|
+
|
|
7
|
+
* [中文版](#中文版copy-paste) · [English](#english-copy-paste)
|
|
8
|
+
* [配置 MCP 服务器](#配置-mcp-服务器) · [参数速查](#参数速查) · [人工介入到底是怎么发生的](#人工介入到底是怎么发生的)
|
|
9
|
+
|
|
10
|
+
> 配套文件:`mcp-config.json`(客户端配置)、`raw-ws.mjs`(不用 MCP 的最小例子)、
|
|
11
|
+
> `read-page.mjs`(读一页并列出可交互元素)、`with-human.mjs`(需要人介入时的完整流程)。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 中文版(copy-paste)
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
我可以用 AI Browser 浏览网页。它提供 MCP 服务(17 个 browse_* 工具),第一次调用时会自动
|
|
19
|
+
拉起一个真实可见的浏览器窗口,用完调用 browse_quit 释放。页面以结构化语义树返回,不是截图。
|
|
20
|
+
|
|
21
|
+
标准循环:
|
|
22
|
+
1. browse_navigate { url } 打开页面
|
|
23
|
+
2. browse_get_tree 读页面,返回语义树:每个节点有 role / label / value /
|
|
24
|
+
states / actions
|
|
25
|
+
3. browse_act { action, target } 操作元素,target 填第 2 步节点里的 id(即 data-ai-id)
|
|
26
|
+
action 可选:click / type(光标处追加)/ setContent
|
|
27
|
+
(整体替换)/ clear / focus / hover / scroll_to /
|
|
28
|
+
upload / get_value(只读回读全文)/ drag / press / wheel
|
|
29
|
+
4. browse_wait { condition } 等待条件成立:button_enabled / modal_appeared /
|
|
30
|
+
text_contains / url_contains
|
|
31
|
+
5. browse_evaluate { js } 在页面上下文跑 JS 表达式(上限 5000 字符,
|
|
32
|
+
禁用 process. / require / child_process)
|
|
33
|
+
|
|
34
|
+
省 token(browse_get_tree 参数):
|
|
35
|
+
- { "subset": "interactive" } 只保留可交互节点,砍掉纯布局分支 —— 默认就该带上
|
|
36
|
+
- { "ax": true } 走 AX 语义层,链接会带 url
|
|
37
|
+
- { "mode": "diff" } 只看上次读取以来新增 / 消失的可交互节点
|
|
38
|
+
|
|
39
|
+
验证写操作(success 不等于做成了):browse_diff 先 snapshot 基线,写操作之后再 diff,
|
|
40
|
+
verdict 显式给出 changed / unchanged。
|
|
41
|
+
|
|
42
|
+
其他:browse_scroll 滚动文档(画布类页面用 browse_act 的 wheel);browse_list_tabs /
|
|
43
|
+
browse_new_tab / browse_close_tab / browse_set_active_tab 管标签页;browse_network 拉取网络
|
|
44
|
+
请求(含 POST body,免订阅);browse_capabilities 一次探明页面该走哪条路(结构化数据 /
|
|
45
|
+
语义树 / 指针手势);browse_canvas 读 canvas 绘制调用里的文本(不是 OCR);
|
|
46
|
+
browse_downloads 查下载文件落盘路径;browse_repaint 在窗口画面陈旧时强制重绘。
|
|
47
|
+
|
|
48
|
+
需要人帮忙时(登录、扫码、短信验证码、滑块 / 人机验证,以及任何我做不了的操作):
|
|
49
|
+
浏览器窗口是真实可见的,人随时可以用鼠标键盘操作。我应该在对话里直接说一句人话请用户去窗口里
|
|
50
|
+
完成,例如"请在浏览器窗口里扫码登录,完成后回复我'好了'",然后停下来等用户回复;收到"好了"之后
|
|
51
|
+
重新 browse_get_tree 确认状态再继续。不要编造登录凭据,也不要反复重试那些注定失败的点击。
|
|
52
|
+
浏览器侧没有任何"交接"接口,也不需要——人机协作就发生在我们的对话里。
|
|
53
|
+
|
|
54
|
+
收尾:任务完成后调用 browse_quit(会关闭整个浏览器窗口和进程,释放资源)。
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## English (copy-paste)
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
I can browse the web with AI Browser. It exposes an MCP server (17 browse_* tools), auto-starts a
|
|
61
|
+
real, visible browser window on my first call, and I release it with browse_quit when done. Pages come
|
|
62
|
+
back as a structured semantic tree — no screenshots, no OCR.
|
|
63
|
+
|
|
64
|
+
Standard loop:
|
|
65
|
+
1. browse_navigate { url } load a page
|
|
66
|
+
2. browse_get_tree read the page: nodes carry role / label / value / states /
|
|
67
|
+
actions
|
|
68
|
+
3. browse_act { action, target } act on an element; target is the node's id (its data-ai-id)
|
|
69
|
+
action: click / type (append at caret) / setContent
|
|
70
|
+
(replace all) / clear / focus / hover / scroll_to /
|
|
71
|
+
upload / get_value (read-only) / drag / press / wheel
|
|
72
|
+
4. browse_wait { condition } block until: button_enabled / modal_appeared /
|
|
73
|
+
text_contains / url_contains
|
|
74
|
+
5. browse_evaluate { js } run a JS expression in page context (5000-char cap;
|
|
75
|
+
process. / require / child_process are rejected)
|
|
76
|
+
|
|
77
|
+
Saving tokens (browse_get_tree params):
|
|
78
|
+
- { "subset": "interactive" } drop layout-only branches — use this by default
|
|
79
|
+
- { "ax": true } AX semantic layer; links carry url
|
|
80
|
+
- { "mode": "diff" } only interactive nodes added/hidden since my last read
|
|
81
|
+
|
|
82
|
+
Verifying writes (success is not evidence): browse_diff snapshots a baseline, then diffs after the
|
|
83
|
+
write and returns an explicit verdict of changed / unchanged.
|
|
84
|
+
|
|
85
|
+
Also: browse_scroll (document scrolling; use the wheel action on canvas apps); browse_list_tabs /
|
|
86
|
+
browse_new_tab / browse_close_tab / browse_set_active_tab for tabs; browse_network for captured
|
|
87
|
+
requests including POST bodies (no subscription); browse_capabilities to pick a route on an
|
|
88
|
+
unfamiliar page (structured data / semantic tree / pointer gestures); browse_canvas reads text
|
|
89
|
+
straight from canvas draw calls (no OCR); browse_downloads for on-disk download paths;
|
|
90
|
+
browse_repaint when the window shows a stale page.
|
|
91
|
+
|
|
92
|
+
When I need a human (login, QR code, SMS code, slider/CAPTCHA, or anything I genuinely cannot do):
|
|
93
|
+
the browser window is real and visible — the user can just use their mouse and keyboard in it. I should
|
|
94
|
+
say so plainly in the conversation (e.g. "please scan the QR code in the browser window and reply
|
|
95
|
+
'done'"), then stop and wait for that reply. After "done", I re-read with browse_get_tree and continue.
|
|
96
|
+
I never invent credentials or loop on clicks that cannot work. There is no hand-off API on the browser
|
|
97
|
+
side, and none is needed — the collaboration happens right here in our conversation.
|
|
98
|
+
|
|
99
|
+
Wrap up: call browse_quit when the task is finished (it closes the window and exits the process).
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 配置 MCP 服务器
|
|
105
|
+
|
|
106
|
+
把下面配置复制到你的客户端配置文件里(Claude Desktop `claude_desktop_config.json`、
|
|
107
|
+
项目根目录 `.mcp.json`、Cursor `~/.cursor/mcp.json`、Codex 等)。npm 包方式无需任何本地路径:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"ai-browser": {
|
|
113
|
+
"command": "npx",
|
|
114
|
+
"args": ["-y", "@glittering0717/ai-browser", "mcp"]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
从源码运行时改用 `"command": "node"`,args 指向
|
|
121
|
+
`/你的/绝对路径/ai-browser/src/main/mcp_server.js`(必须是绝对路径)。
|
|
122
|
+
|
|
123
|
+
* 首次启动会准备 Electron 引擎(约 100 MB,仅一次);网络慢可在客户端的环境变量里设
|
|
124
|
+
`ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/`。
|
|
125
|
+
* 无需事先启动浏览器:MCP 服务器会探测 `9223` 端口,没人在监听就自己 spawn Electron(最多等 30 秒)。
|
|
126
|
+
* 想常驻浏览器也可以:在项目根目录跑 `npm start`,之后所有客户端共用这一个窗口。
|
|
127
|
+
* 不装 MCP 也能用:原始 WebSocket JSON-RPC 在 `ws://localhost:9223`,见 `raw-ws.mjs`。
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 参数速查
|
|
132
|
+
|
|
133
|
+
| 工具 | 关键参数 | 说明 |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `browse_navigate` | `url`, `tab?` | 打开 URL |
|
|
136
|
+
| `browse_get_tree` | `subset`, `ax`, `mode`, `focused_only`, `tab` | 读语义树 + `context`(弹窗、必填提示、toast、登录态、统计) |
|
|
137
|
+
| `browse_act` | `action`, `target`, `text?`, `value?`, `url?`, `keep_tab?`, `file?`, `tab?` | `target` = 节点 `id`(`data-ai-id`) |
|
|
138
|
+
| `browse_evaluate` | `js`, `tab?` | 表达式(非语句),5000 字符上限 |
|
|
139
|
+
| `browse_scroll` | `direction`, `amount`, `target`, `tab` | 滚页面或滚到某元素 |
|
|
140
|
+
| `browse_wait` | `condition`, `target?`, `text?`, `timeout_ms?` | 轮询直到成立或超时 |
|
|
141
|
+
| `browse_diff` | `operation` (`snapshot`/`diff`), `js?`, `snapshot?`, `rearm?`, `forget?` | 快照基线 + 比对,证明写操作真的生效 |
|
|
142
|
+
| `browse_capabilities` | `include_network?`, `network_limit?` | 探明页面路线:L1 结构化数据 / L2 语义树 / L3 指针手势 |
|
|
143
|
+
| `browse_network` | `operation` (`list`/`get`/`clear`/`configure`), `method?`, `url_contains?`, `status_min/max?` | 网络请求检查,含 POST body,默认免订阅 |
|
|
144
|
+
| `browse_canvas` | `operation` (`list`/`read`/`configure`/`capture`), `canvas_id?` | 读 canvas 绘制调用文本;WebGL 才用 `capture` |
|
|
145
|
+
| `browse_downloads` | — | 查下载项与落盘路径(下载异步,点完后轮询) |
|
|
146
|
+
| `browse_repaint` | — | 窗口画面陈旧时强制合成器重绘,返回 `repaint_count` |
|
|
147
|
+
| `browse_list_tabs` / `browse_new_tab` / `browse_close_tab` / `browse_set_active_tab` | `tab?`, `url?` | 标签页管理 |
|
|
148
|
+
| `browse_quit` | — | 关闭窗口并退出进程 |
|
|
149
|
+
|
|
150
|
+
> 实时事件订阅(`dom_change` / `network_response` / `js_error` 等)只在原始 WebSocket 层
|
|
151
|
+
> 提供(`ui.subscribe`),MCP stdio 不支持 server→client 通知,所以没有 `browse_subscribe`;
|
|
152
|
+
> 拉取式的检查一律用 `browse_network`。
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 人工介入到底是怎么发生的
|
|
157
|
+
|
|
158
|
+
这是本项目最容易被误解的一点,说清楚:
|
|
159
|
+
|
|
160
|
+
1. 浏览器窗口**真实可见**,人随时可以用鼠标键盘操作它——登录、扫码、滑块验证码都行。
|
|
161
|
+
2. agent 做不了的事,**在对话里说一句人话请求用户去做**,然后停下来等回复。
|
|
162
|
+
3. 用户做完回一句"好了",agent 重新 `browse_get_tree` 确认状态,继续往下走。
|
|
163
|
+
4. **浏览器侧没有任何"交接"接口,也不需要。** 交接是一个对话协议,不是一个 API。
|
|
164
|
+
脚本版的等价实现见 `with-human.mjs`:它只是 `readline` 打印一句提示 + 等回车,
|
|
165
|
+
浏览器对此毫不知情。
|
|
166
|
+
5. 登录态存在 `persist:` partition 里,重启浏览器也还在,通常只需人工介入一次。
|
package/llms-install.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# llms-install.md — agent-oriented install guide for AI Browser
|
|
2
|
+
|
|
3
|
+
This file is written for AI coding agents installing or configuring **AI Browser**
|
|
4
|
+
on a user's machine. The human-facing guide is [README.md](README.md); this file is
|
|
5
|
+
the short, exact path. Do not invent flags or tool names not listed here.
|
|
6
|
+
|
|
7
|
+
## What it is
|
|
8
|
+
|
|
9
|
+
A real, visible Electron browser driven by an agent. Pages are returned as a
|
|
10
|
+
semantic tree (roles / labels / values / actions, stable `data-ai-id` per element),
|
|
11
|
+
not screenshots. Input is trusted CDP keyboard/mouse events. A human can use the
|
|
12
|
+
window's mouse and keyboard at any time (login, QR scan, CAPTCHA, payment).
|
|
13
|
+
|
|
14
|
+
Two equivalent interfaces: **MCP stdio** (17 `browse_*` tools) and a raw WebSocket
|
|
15
|
+
JSON-RPC API at `ws://localhost:9223` (`ui.*` methods).
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js **≥ 18**
|
|
20
|
+
- A desktop session (GUI app): macOS or Linux. Windows is experimental/untested.
|
|
21
|
+
- ~100 MB for the Electron binary (prepared on first launch / `npm install`, from GitHub releases).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
Preferred — npm package [`@glittering0717/ai-browser`](https://www.npmjs.com/package/@glittering0717/ai-browser),
|
|
26
|
+
no clone needed:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx -y @glittering0717/ai-browser start
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The first launch prepares the Electron engine once (~100 MB). npm ≥ 11 skips dependency install
|
|
33
|
+
scripts by default; the launcher detects the missing engine and runs Electron's downloader itself.
|
|
34
|
+
If the download is slow/fails (common on mainland-China networks), set a mirror in the environment
|
|
35
|
+
of the launch command:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npx -y @glittering0717/ai-browser start
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Alternative — from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/Glittering/ai-browser.git
|
|
45
|
+
cd ai-browser
|
|
46
|
+
npm install # prefix ELECTRON_MIRROR=... if the Electron download is slow
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Verify the binary exists before anything else:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ls node_modules/electron/dist # must contain Electron.app (macOS)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Optional end-to-end check: `npx -y @glittering0717/ai-browser start` (or `npm start` from source) opens
|
|
56
|
+
a 1280×800 window and logs `AI Browser WS server listening on ws://localhost:9223`.
|
|
57
|
+
|
|
58
|
+
## Register as an MCP server
|
|
59
|
+
|
|
60
|
+
Add this to the client config (Claude Desktop config, `~/.cursor/mcp.json`, project `.mcp.json`,
|
|
61
|
+
Cline, etc.):
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"ai-browser": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "@glittering0717/ai-browser", "mcp"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
From-source alternative: `"command": "node"` with the absolute path to
|
|
75
|
+
`src/main/mcp_server.js`.
|
|
76
|
+
|
|
77
|
+
Do **not** launch the browser yourself first: on the first `browse_*` call the MCP server probes
|
|
78
|
+
port 9223, spawns Electron detached if nothing is listening, and waits up to 30 s (the very first
|
|
79
|
+
call can take longer if the engine download hasn't happened yet — the progress goes to stderr).
|
|
80
|
+
A long-lived shared window is also fine — run the `start` command manually; a second launch only
|
|
81
|
+
focuses the existing window (single instance lock).
|
|
82
|
+
|
|
83
|
+
## The 17 tools
|
|
84
|
+
|
|
85
|
+
- `browse_navigate` — load a URL in the active or given tab.
|
|
86
|
+
- `browse_get_tree` — semantic tree + context (modals, toasts, required-field
|
|
87
|
+
hints, login state, stats). Params: `subset: "interactive"` (token saver, use by
|
|
88
|
+
default), `ax: true` (links carry urls), `mode: "diff"` (only new/hidden nodes).
|
|
89
|
+
- `browse_act` — act on a `data-ai-id`: `click`, `type` (append at caret),
|
|
90
|
+
`setContent` (replace all), `clear`, `focus`, `hover`, `scroll_to`, `upload`,
|
|
91
|
+
`get_value` (read-only full value; tree truncates at 200 chars), `drag`,
|
|
92
|
+
`press`, `wheel`.
|
|
93
|
+
- `browse_diff` — `snapshot` a baseline, then `diff`; returns an explicit
|
|
94
|
+
`verdict: "changed" | "unchanged"` with before/after JSON paths. Wrap every
|
|
95
|
+
write with this — a success return is not evidence the write worked.
|
|
96
|
+
- `browse_capabilities` — probe a page and recommend a route:
|
|
97
|
+
L1 structured data / L2 semantic tree / L3 pointer gestures.
|
|
98
|
+
- `browse_evaluate` — JS **expression** in page context; 5000-char cap;
|
|
99
|
+
`process.` / `require` / `child_process` are rejected.
|
|
100
|
+
- `browse_scroll` — document scrolling only; use `act {action:"wheel"}` on
|
|
101
|
+
infinite-canvas apps.
|
|
102
|
+
- `browse_wait` — poll until `button_enabled` / `modal_appeared` /
|
|
103
|
+
`text_contains` / `url_contains`.
|
|
104
|
+
- `browse_network` — captured requests (`list` / `get` / `clear` / `configure`),
|
|
105
|
+
including POST bodies; auth/cookie headers redacted by default.
|
|
106
|
+
- `browse_canvas` — `list` / `read` (2D draw calls, text from `fillText`, no OCR) /
|
|
107
|
+
`configure` / `capture` (PNG fallback for opaque WebGL only).
|
|
108
|
+
- `browse_repaint` — force compositor redraw when the window shows a stale page.
|
|
109
|
+
- `browse_downloads` — list downloads with on-disk paths (poll after clicking
|
|
110
|
+
export/download; downloads are asynchronous).
|
|
111
|
+
- `browse_list_tabs`, `browse_new_tab`, `browse_close_tab`,
|
|
112
|
+
`browse_set_active_tab` — tab management.
|
|
113
|
+
- `browse_quit` — shut the whole browser down and release the process.
|
|
114
|
+
|
|
115
|
+
There is no `browse_subscribe` and no `browse_network_body`: real-time events are
|
|
116
|
+
raw-WebSocket only (`ui.subscribe`), because MCP stdio drops server→client
|
|
117
|
+
notifications. Use `browse_network` for pull-based inspection.
|
|
118
|
+
|
|
119
|
+
## Standard agent loop
|
|
120
|
+
|
|
121
|
+
1. `browse_navigate { url }`
|
|
122
|
+
2. `browse_get_tree { subset: "interactive" }` → find the target's `id`
|
|
123
|
+
3. `browse_act { action, target }`
|
|
124
|
+
4. `browse_diff { operation: "diff", snapshot }` → confirm `verdict: "changed"`
|
|
125
|
+
5. `browse_wait` when the page must transition
|
|
126
|
+
6. `browse_quit` only when the whole task is finished — never while a human is
|
|
127
|
+
working in the window (it tears down the window and loses half-done state).
|
|
128
|
+
|
|
129
|
+
## Human hand-off
|
|
130
|
+
|
|
131
|
+
When blocked by login, SSO, QR code, slider/image CAPTCHA, SMS/email code, or
|
|
132
|
+
payment confirmation: stop, ask the user in chat to do it in the visible browser
|
|
133
|
+
window and reply "done", wait for that reply, then re-read with
|
|
134
|
+
`browse_get_tree` and continue. Never fabricate credentials or retry clicks that
|
|
135
|
+
cannot succeed. Logins persist in the `persist:ai-browser` session partition
|
|
136
|
+
(on disk under `~/.ai-browser`), so intervention is usually needed once per site.
|
|
137
|
+
|
|
138
|
+
## Troubleshooting (common cases)
|
|
139
|
+
|
|
140
|
+
- **Port 9223 busy:** quit the stale instance, or set `AI_BROWSER_PORT=9224`
|
|
141
|
+
consistently for the app and every client.
|
|
142
|
+
- **`npm start` fails immediately:** the Electron binary did not download —
|
|
143
|
+
check `ls node_modules/electron/dist`, reinstall with `ELECTRON_MIRROR`.
|
|
144
|
+
- **Action reported success but nothing changed:** use `browse_diff`; an overlay
|
|
145
|
+
may have eaten the click or the app reverted it. Raw WS nests action args
|
|
146
|
+
under `params.params`.
|
|
147
|
+
- **Window shows a stale page:** call `browse_repaint`.
|
|
148
|
+
- **Slow mainland-China network / reboot loops:** watchdog targets are
|
|
149
|
+
configurable; `AI_BROWSER_WATCHDOG=0` disables it. See README for details.
|
|
150
|
+
|
|
151
|
+
## Raw WebSocket alternative (no MCP)
|
|
152
|
+
|
|
153
|
+
`ws://localhost:9223`, JSON-RPC 2.0. Methods `ui.navigate`, `ui.get_tree`,
|
|
154
|
+
`ui.act`, `ui.snapshot` / `ui.diff`, `ui.capabilities`, `ui.evaluate`,
|
|
155
|
+
`ui.scroll`, `ui.wait`, `ui.new_tab` / `ui.close_tab` / `ui.list_tabs` /
|
|
156
|
+
`ui.set_active_tab`, `ui.network_*`, `ui.canvas_*`, `ui.repaint`,
|
|
157
|
+
`ui.downloads`, `ui.subscribe`, `ui.quit`. Minimal client:
|
|
158
|
+
[`examples/raw-ws.mjs`](examples/raw-ws.mjs).
|
package/package.json
CHANGED
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glittering0717/ai-browser",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "AI-native browser — renders pixels for humans, structured semantic-tree API for agents. No screenshots, no OCR, no coordinate guessing.",
|
|
5
|
+
"main": "src/main/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-browser": "bin/ai-browser.cjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"scripts",
|
|
12
|
+
"src",
|
|
13
|
+
"examples/AGENT_INSTRUCTIONS.md",
|
|
14
|
+
"examples/mcp-config.json",
|
|
15
|
+
"llms-install.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Glittering",
|
|
22
|
+
"url": "https://github.com/Glittering"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/Glittering/ai-browser#readme",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/Glittering/ai-browser.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/Glittering/ai-browser/issues"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"ai",
|
|
34
|
+
"ai-agent",
|
|
35
|
+
"browser",
|
|
36
|
+
"mcp",
|
|
37
|
+
"model-context-protocol",
|
|
38
|
+
"agent",
|
|
39
|
+
"automation",
|
|
40
|
+
"electron",
|
|
41
|
+
"semantic",
|
|
42
|
+
"web-scraping"
|
|
43
|
+
],
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"start": "node scripts/start.cjs",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"smoke": "node e2e/smoke_ws.cjs",
|
|
55
|
+
"capabilities": "node e2e/capabilities_ws.cjs",
|
|
56
|
+
"richtext": "node e2e/richtext_ws.cjs",
|
|
57
|
+
"network": "node e2e/network_ws.cjs",
|
|
58
|
+
"value": "node e2e/value_ws.cjs",
|
|
59
|
+
"canvas": "node e2e/canvas_ws.cjs",
|
|
60
|
+
"focus": "node e2e/focus_ws.cjs",
|
|
61
|
+
"interact": "node e2e/interact_ws.cjs",
|
|
62
|
+
"view": "node e2e/view_ws.cjs",
|
|
63
|
+
"realsites": "node e2e/e2e_realsites.cjs",
|
|
64
|
+
"realsites:meta": "vitest run tests/test_realsites_sites.js",
|
|
65
|
+
"mcp": "node src/main/mcp_server.js",
|
|
66
|
+
"modeldiff": "node e2e/modeldiff_ws.cjs",
|
|
67
|
+
"audit": "node e2e/audit_ws.cjs"
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
71
|
+
"electron": "^33.4.11",
|
|
72
|
+
"jsdom": "^29.1.1",
|
|
73
|
+
"ws": "^8.0.0"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@playwright/test": "^1.45.0",
|
|
77
|
+
"playwright": "^1.45.0",
|
|
78
|
+
"vitest": "^2.0.0"
|
|
79
|
+
}
|
|
80
|
+
}
|