@sleepinsummer/agent-browser-cli 0.3.1 → 0.3.3
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/CHANGELOG.md +19 -0
- package/README.md +58 -27
- package/README_EN.md +55 -26
- package/assets/tmwd_cdp_bridge/background.js +111 -1
- package/assets/tmwd_cdp_bridge/disable_dialogs.js +3 -24
- package/assets/tmwd_cdp_bridge/manifest.json +0 -11
- package/assets/tmwd_cdp_bridge/popup.html +12 -0
- package/assets/tmwd_cdp_bridge/popup.js +44 -0
- package/package.json +6 -6
- package/skills/agent-browser-cli/SKILL.md +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
+
## 未发布
|
|
2
|
+
|
|
3
|
+
## v0.3.3 - 2026-05-18
|
|
4
|
+
|
|
5
|
+
- `tabtree` 默认改为 compact 输出,截断长 URL 并省略 `session_key` 以降低 token 消耗;新增 `tabtree --full` 输出完整 URL 和 `session_key`。
|
|
6
|
+
- 优化 `tabtree` 实现,daemon 锁内只复制必要会话字段,锁外完成排序和 JSON 组装。
|
|
7
|
+
- Chrome 扩展 popup 增加 label 唯一性提示:推荐用 CLI 设置 label 以校验当前 daemon 内跨 Profile 唯一性。
|
|
8
|
+
- 平台 npm 包打包时默认对复制后的原生二进制执行 `strip`,减小发布体积;本地 `target/release` 二进制保持不变。
|
|
9
|
+
- 新增 `tabtree` 树形查询命令,支持按 `tab_id`、`profile_id/profile_label`、`browser_id` 过滤,并保留 browser → profile → tab 父子节点;原先临时增加的 `profiles` / `browsers` 摘要命令已移除,统一使用 `tabtree` / `lookup`。
|
|
10
|
+
- 新增 `lookup tab|browser|profile` 反查命令,可由 `tab_id` 反查 `browser_id` / `profile_id` / `profile_label`,或由 `browser_id` 反查所属 profile。
|
|
11
|
+
- 移除扩展默认注入的全局 `alert` / `confirm` / `prompt` 重写,改为 CLI 页面执行期间临时抑制弹窗并在命令结束后恢复原生函数。
|
|
12
|
+
- 新增 `profile-label set|clear`,并在 Chrome 扩展 popup 中支持设置 Profile Label;label 冲突时 CLI 按歧义处理,不参与内部路由主键。
|
|
13
|
+
|
|
1
14
|
# 更新日志
|
|
2
15
|
|
|
3
16
|
所有重要变更都会记录在这里。日期使用北京时间自然日。
|
|
4
17
|
|
|
18
|
+
## v0.3.2 - 2026-05-17
|
|
19
|
+
|
|
20
|
+
### 修复
|
|
21
|
+
|
|
22
|
+
- 修复长 URL 页面中 `snapshot` 后立即使用 `@e` 可能误报 `ref expired` 的问题。
|
|
23
|
+
|
|
5
24
|
## v0.3.1 - 2026-05-17
|
|
6
25
|
|
|
7
26
|
### 新增
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<a href="https://github.com/sleepinginsummer/agent-browser-cli"><img src="https://img.shields.io/badge/CLI-agentbrowsercli-2ea44f" alt="CLI agentbrowsercli"></a>
|
|
11
11
|
<a href="https://github.com/sleepinginsummer/agent-browser-cli/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License MIT"></a>
|
|
12
12
|
<a href="https://github.com/sleepinginsummer/agent-browser-cli"><img src="https://img.shields.io/badge/sys-win%2Fmac%2Flinux-0078D6?labelColor=0078D6&color=C0C0C0" alt="sys win/mac/linux"></a>
|
|
13
|
-
<a href="https://github.com/sleepinginsummer/agent-browser-cli/releases"><img src="https://img.shields.io/badge/release-v0.3.
|
|
13
|
+
<a href="https://github.com/sleepinginsummer/agent-browser-cli/releases"><img src="https://img.shields.io/badge/release-v0.3.3-orange" alt="release v0.3.3"></a>
|
|
14
14
|
<a href="https://github.com/sleepinginsummer/agent-browser-cli/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen" alt="PRs welcome"></a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
@@ -53,32 +53,36 @@
|
|
|
53
53
|
- 若干优化,缩短命令执行时间
|
|
54
54
|
- rust实现cli端
|
|
55
55
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
56
|
+
## 他能做的事情
|
|
57
|
+
|
|
58
|
+
1. 自动化测试
|
|
59
|
+
可以复用真实浏览器环境做页面流程验证、表单提交、按钮点击、跳转检查、登录态页面测试。
|
|
60
|
+
2. 前端页面 Debug
|
|
61
|
+
可以读取 DOM、执行 JS、查看页面状态、截图确认效果,辅助定位前端交互、渲染和数据问题,对接后端接口。
|
|
62
|
+
3. 页面样式调试
|
|
63
|
+
可以在真实页面里执行 JS 修改 DOM / CSS,临时验证样式、布局和交互效果,但更偏辅助调试,不是完整设计工具。
|
|
64
|
+
4. 网页数据采集
|
|
65
|
+
可以读取页面内容、表格、列表、Cookie 和接口相关状态,适合处理需要登录态的页面数据提取。
|
|
66
|
+
5. 浏览器操作脚本化
|
|
67
|
+
可以把打开页面、切换标签页、执行 JS、截图、上传文件等操作串成脚本,做重复性网页任务。
|
|
68
|
+
6. Agent 辅助操作网页后台
|
|
69
|
+
适合让 AI Agent 操作管理后台、配置页面、低代码平台、表单系统等已有网页工具。
|
|
70
|
+
7. 页面结构分析
|
|
71
|
+
可以简化 HTML、识别主要内容区和列表结构,帮助 Agent 更快理解复杂页面。
|
|
72
|
+
8. 安全研究和逆向辅助
|
|
73
|
+
可以在真实浏览器会话里观察页面行为、执行调试脚本、读取前端状态,辅助分析前端逻辑和接口调用。
|
|
74
|
+
|
|
75
|
+
## 他的能力
|
|
76
|
+
|
|
77
|
+
1. 扫描当前 Chrome 标签页,获取页面标题、URL 和标签页 ID。
|
|
78
|
+
2. 切换到指定标签页,复用已有页面和登录态。
|
|
79
|
+
3. 打开新标签页,支持直接访问目标 URL。
|
|
80
|
+
4. 在页面中执行 JavaScript,读取 DOM、表单、状态和页面数据。
|
|
81
|
+
5. 读取当前页面 Cookie,方便处理登录态相关任务。
|
|
82
|
+
6. 调用 Chrome CDP 能力,执行更底层的页面控制。
|
|
83
|
+
7. 截取页面截图,用于视觉检查和页面确认。
|
|
84
|
+
8. 上传本地文件到网页文件选择框。
|
|
85
|
+
9. 操作下拉框、按钮、表单等常见页面元素。
|
|
82
86
|
|
|
83
87
|
## 目录结构
|
|
84
88
|
|
|
@@ -156,7 +160,25 @@ agent-browser-cli set-extension-port 18766
|
|
|
156
160
|
|
|
157
161
|
Chrome 插件 popup 中也可以修改插件端口并立即重连。插件端口必须和 CLI 配置中的 `extension_port` 一致。
|
|
158
162
|
|
|
163
|
+
### Profile Label
|
|
164
|
+
|
|
165
|
+
多 Chrome Profile / 多浏览器实例下,`profile_id` 和 `browser_id` 较长。可以给每个 Chrome Profile 设置短 label,之后用 `--profile <label>` 操作。
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
agent-browser-cli lookup tab <tabId>
|
|
169
|
+
agent-browser-cli lookup browser <browser_id>
|
|
170
|
+
agent-browser-cli profile-label set work --profile <profile_id>
|
|
171
|
+
agent-browser-cli tabs --profile work
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
也可以在对应 Chrome Profile 的扩展 popup 中设置 Profile Label。label 只作为别名,内部路由仍使用 `browser_id:profile_id:tab_id`;如果当前 daemon 内 label 匹配到多个 profile,CLI 会报歧义。推荐用 CLI 设置 label,因为 CLI 会校验当前 daemon 内跨 Profile 唯一性;popup 是本地便捷入口,不保证跨 Profile 唯一。`tabtree` 默认截断 URL 并省略 `session_key` 以减少 token,需完整字段时加 `--full`。
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
### 弹窗抑制
|
|
159
180
|
|
|
181
|
+
扩展不再默认重写页面的 `alert` / `confirm` / `prompt`。只有 CLI 执行页面脚本命令期间会临时抑制原生弹窗,命令结束后恢复,避免长期污染业务页面全局函数。
|
|
160
182
|
|
|
161
183
|
## 快速自检
|
|
162
184
|
|
|
@@ -185,6 +207,14 @@ README 只保留快速入口;完整命令和浏览器操作 SOP 见 [skills/ag
|
|
|
185
207
|
|
|
186
208
|
```bash
|
|
187
209
|
agent-browser-cli tabs
|
|
210
|
+
agent-browser-cli tabtree
|
|
211
|
+
agent-browser-cli tabtree --full
|
|
212
|
+
agent-browser-cli tabtree --profile <profile_label>
|
|
213
|
+
agent-browser-cli tabtree --tab <tabId>
|
|
214
|
+
agent-browser-cli lookup tab <tabId>
|
|
215
|
+
agent-browser-cli lookup browser <browser_id>
|
|
216
|
+
agent-browser-cli profile-label set work --profile <profile_id>
|
|
217
|
+
agent-browser-cli profile-label clear --profile <profile_id>
|
|
188
218
|
```
|
|
189
219
|
|
|
190
220
|
## 更新
|
|
@@ -210,6 +240,7 @@ agent-browser-cli stop
|
|
|
210
240
|
然后按需清理:
|
|
211
241
|
|
|
212
242
|
```bash
|
|
243
|
+
npm uninstall -g @sleepinsummer/agent-browser-cli
|
|
213
244
|
rm -f .agent-browser-cli.log .agent-browser-cli.lock
|
|
214
245
|
rm -rf ~/.agents/skills/agent-browser-cli
|
|
215
246
|
```
|
package/README_EN.md
CHANGED
|
@@ -53,32 +53,36 @@ Please read https://github.com/sleepinginsummer/agent-browser-cli/blob/main/AI_I
|
|
|
53
53
|
- Includes several optimizations to reduce command execution time.
|
|
54
54
|
- Rust implementation for the CLI side.
|
|
55
55
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
56
|
+
## What It Can Do
|
|
57
|
+
|
|
58
|
+
1. Automated testing
|
|
59
|
+
It can reuse a real browser environment for page-flow validation, form submission, button clicks, navigation checks, and login-state page testing.
|
|
60
|
+
2. Frontend page debugging
|
|
61
|
+
It can read the DOM, execute JS, inspect page state, and capture screenshots to help locate frontend interaction, rendering, and data issues, including backend API integration problems.
|
|
62
|
+
3. Page style debugging
|
|
63
|
+
It can execute JS on real pages to modify DOM / CSS and temporarily validate styles, layout, and interaction effects. It is more of a debugging assistant than a complete design tool.
|
|
64
|
+
4. Web data extraction
|
|
65
|
+
It can read page content, tables, lists, cookies, and API-related state, making it suitable for extracting data from pages that require login state.
|
|
66
|
+
5. Browser operation scripting
|
|
67
|
+
It can chain operations such as opening pages, switching tabs, executing JS, taking screenshots, and uploading files into scripts for repetitive web tasks.
|
|
68
|
+
6. Agent-assisted web admin operation
|
|
69
|
+
It is suitable for letting AI Agents operate existing web tools such as admin consoles, configuration pages, low-code platforms, and form systems.
|
|
70
|
+
7. Page structure analysis
|
|
71
|
+
It can simplify HTML and identify main content areas and list structures, helping Agents understand complex pages faster.
|
|
72
|
+
8. Security research and reverse-engineering assistance
|
|
73
|
+
It can observe page behavior in a real browser session, execute debugging scripts, and read frontend state to assist analysis of frontend logic and API calls.
|
|
74
|
+
|
|
75
|
+
## Its Capabilities
|
|
76
|
+
|
|
77
|
+
1. Scan current Chrome tabs and get page titles, URLs, and tab IDs.
|
|
78
|
+
2. Switch to a specified tab and reuse existing pages and login state.
|
|
79
|
+
3. Open new tabs and directly visit target URLs.
|
|
80
|
+
4. Execute JavaScript in pages and read DOM, forms, state, and page data.
|
|
81
|
+
5. Read cookies from the current page for login-state related tasks.
|
|
82
|
+
6. Call Chrome CDP capabilities for lower-level page control.
|
|
83
|
+
7. Capture page screenshots for visual inspection and page confirmation.
|
|
84
|
+
8. Upload local files to web file picker inputs.
|
|
85
|
+
9. Operate common page elements such as dropdowns, buttons, and forms.
|
|
82
86
|
|
|
83
87
|
## Layout
|
|
84
88
|
|
|
@@ -156,6 +160,10 @@ After manually editing the config file, run `agent-browser-cli restart` so the d
|
|
|
156
160
|
|
|
157
161
|
The Chrome extension popup can also update the extension port and reconnect immediately. The popup port must match the CLI `extension_port` config.
|
|
158
162
|
|
|
163
|
+
### Dialog Suppression
|
|
164
|
+
|
|
165
|
+
The extension no longer rewrites page `alert` / `confirm` / `prompt` globally. Native dialogs are suppressed only while a CLI page script command is running, then restored after the command finishes.
|
|
166
|
+
|
|
159
167
|
## Quick Check
|
|
160
168
|
|
|
161
169
|
```bash
|
|
@@ -177,12 +185,33 @@ On success, it returns:
|
|
|
177
185
|
}
|
|
178
186
|
```
|
|
179
187
|
|
|
188
|
+
### Profile Label
|
|
189
|
+
|
|
190
|
+
When multiple Chrome Profiles or browser instances are connected, `profile_id` and `browser_id` are long. You can set a short label for each Chrome Profile and then use `--profile <label>`.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
agent-browser-cli lookup tab <tabId>
|
|
194
|
+
agent-browser-cli lookup browser <browser_id>
|
|
195
|
+
agent-browser-cli profile-label set work --profile <profile_id>
|
|
196
|
+
agent-browser-cli tabs --profile work
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
You can also set Profile Label from the extension popup in the corresponding Chrome Profile. The label is only an alias; internal routing still uses `browser_id:profile_id:tab_id`. If a label matches multiple profiles in the current daemon, the CLI reports ambiguity. Prefer setting labels via CLI because it checks uniqueness across currently connected profiles; the popup is a local convenience entry and cannot guarantee cross-profile uniqueness. `tabtree` uses compact output by default; pass `--full` for full URLs and session keys.
|
|
200
|
+
|
|
180
201
|
## Common Commands
|
|
181
202
|
|
|
182
203
|
The README only keeps the quick entry point. For the full command list and browser operation SOP, see [skills/agent-browser-cli/SKILL.md](./skills/agent-browser-cli/SKILL.md).
|
|
183
204
|
|
|
184
205
|
```bash
|
|
185
206
|
agent-browser-cli tabs
|
|
207
|
+
agent-browser-cli tabtree
|
|
208
|
+
agent-browser-cli tabtree --full
|
|
209
|
+
agent-browser-cli tabtree --profile <profile_label>
|
|
210
|
+
agent-browser-cli tabtree --tab <tabId>
|
|
211
|
+
agent-browser-cli lookup tab <tabId>
|
|
212
|
+
agent-browser-cli lookup browser <browser_id>
|
|
213
|
+
agent-browser-cli profile-label set work --profile <profile_id>
|
|
214
|
+
agent-browser-cli profile-label clear --profile <profile_id>
|
|
186
215
|
```
|
|
187
216
|
|
|
188
217
|
## Update
|
|
@@ -42,6 +42,7 @@ function withClientIdentity(payload) {
|
|
|
42
42
|
async function handleExtMessage(msg, sender) {
|
|
43
43
|
if (msg.cmd === 'status') return handleStatus();
|
|
44
44
|
if (msg.cmd === 'setPort') return await handleSetPort(msg);
|
|
45
|
+
if (msg.cmd === 'setProfileLabel') return await handleSetProfileLabel(msg);
|
|
45
46
|
lastCommandAt = Date.now();
|
|
46
47
|
if (msg.cmd === 'cookies') return await handleCookies(msg, sender);
|
|
47
48
|
if (msg.cmd === 'cdp') return await handleCDP(msg, sender);
|
|
@@ -137,6 +138,29 @@ async function handleSetPort(msg) {
|
|
|
137
138
|
};
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
function normalizeProfileLabel(label) {
|
|
142
|
+
const raw = String(label || '').trim();
|
|
143
|
+
if (!raw) return null;
|
|
144
|
+
if (raw.length > 40) throw new Error('Profile Label 长度不能超过 40 个字符');
|
|
145
|
+
if (!/^[A-Za-z0-9_.-]+$/.test(raw)) throw new Error('Profile Label 只能包含英文、数字、-、_、.');
|
|
146
|
+
return raw;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function handleSetProfileLabel(msg) {
|
|
150
|
+
const label = normalizeProfileLabel(msg.label);
|
|
151
|
+
profileLabel = label;
|
|
152
|
+
await chrome.storage.local.set({ profileLabel });
|
|
153
|
+
await sendTabsUpdate();
|
|
154
|
+
return {
|
|
155
|
+
ok: true,
|
|
156
|
+
data: {
|
|
157
|
+
browserId,
|
|
158
|
+
profileId,
|
|
159
|
+
profileLabel
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
140
164
|
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|
141
165
|
handleExtMessage(msg, sender).then(sendResponse);
|
|
142
166
|
return true;
|
|
@@ -618,6 +642,87 @@ async function injectContentScriptsIntoExistingTabs() {
|
|
|
618
642
|
}
|
|
619
643
|
}
|
|
620
644
|
|
|
645
|
+
function buildDialogSuppressionScript(enabled) {
|
|
646
|
+
if (!enabled) {
|
|
647
|
+
return `(() => {
|
|
648
|
+
const state = window.__TMWD_DIALOG_SUPPRESSION__;
|
|
649
|
+
if (!state) return;
|
|
650
|
+
state.count = Math.max(0, Number(state.count || 1) - 1);
|
|
651
|
+
if (state.count > 0) return;
|
|
652
|
+
try {
|
|
653
|
+
window.alert = state.alert;
|
|
654
|
+
window.confirm = state.confirm;
|
|
655
|
+
window.prompt = state.prompt;
|
|
656
|
+
} finally {
|
|
657
|
+
delete window.__TMWD_DIALOG_SUPPRESSION__;
|
|
658
|
+
}
|
|
659
|
+
})()`;
|
|
660
|
+
}
|
|
661
|
+
return `(() => {
|
|
662
|
+
const existing = window.__TMWD_DIALOG_SUPPRESSION__;
|
|
663
|
+
if (existing) {
|
|
664
|
+
existing.count = Number(existing.count || 1) + 1;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const state = {
|
|
668
|
+
count: 1,
|
|
669
|
+
alert: window.alert,
|
|
670
|
+
confirm: window.confirm,
|
|
671
|
+
prompt: window.prompt
|
|
672
|
+
};
|
|
673
|
+
window.__TMWD_DIALOG_SUPPRESSION__ = state;
|
|
674
|
+
const toast = (type, msg) => {
|
|
675
|
+
try { console.log('[TMWD] ' + type + ' suppressed during CLI command:', msg); } catch (_) {}
|
|
676
|
+
try {
|
|
677
|
+
const d = document.createElement('div');
|
|
678
|
+
d.textContent = '[' + type + '] ' + msg;
|
|
679
|
+
Object.assign(d.style, {
|
|
680
|
+
position:'fixed', top:'12px', right:'12px', zIndex:'2147483647',
|
|
681
|
+
background:'#222', color:'#fff', padding:'10px 18px', borderRadius:'8px',
|
|
682
|
+
fontSize:'14px', maxWidth:'420px', wordBreak:'break-all',
|
|
683
|
+
boxShadow:'0 4px 16px rgba(0,0,0,.3)', opacity:'1',
|
|
684
|
+
transition:'opacity .5s', pointerEvents:'none'
|
|
685
|
+
});
|
|
686
|
+
(document.body || document.documentElement).appendChild(d);
|
|
687
|
+
setTimeout(() => { d.style.opacity = '0'; }, 3000);
|
|
688
|
+
setTimeout(() => { d.remove(); }, 3600);
|
|
689
|
+
} catch (_) {}
|
|
690
|
+
};
|
|
691
|
+
window.alert = function(msg) { toast('alert', msg); };
|
|
692
|
+
window.confirm = function(msg) { toast('confirm', msg); return true; };
|
|
693
|
+
window.prompt = function(msg, def) { toast('prompt', msg); return def || null; };
|
|
694
|
+
})()`;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
async function setDialogSuppressionByScripting(tabId, enabled) {
|
|
698
|
+
try {
|
|
699
|
+
await chrome.scripting.executeScript({
|
|
700
|
+
target: { tabId },
|
|
701
|
+
world: 'MAIN',
|
|
702
|
+
func: async (script) => await eval(script),
|
|
703
|
+
args: [buildDialogSuppressionScript(enabled)]
|
|
704
|
+
});
|
|
705
|
+
return true;
|
|
706
|
+
} catch (e) {
|
|
707
|
+
console.log('[TMWD-WS] dialog suppression scripting failed:', e.message);
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
async function setDialogSuppressionByCdp(tabId, enabled) {
|
|
713
|
+
try {
|
|
714
|
+
await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {
|
|
715
|
+
expression: buildDialogSuppressionScript(enabled),
|
|
716
|
+
awaitPromise: true,
|
|
717
|
+
returnByValue: true
|
|
718
|
+
});
|
|
719
|
+
return true;
|
|
720
|
+
} catch (e) {
|
|
721
|
+
console.log('[TMWD-WS] dialog suppression CDP failed:', e.message);
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
621
726
|
// --- Shared page/CDP script builder core ---
|
|
622
727
|
function buildExecScript(code, errorHandler) {
|
|
623
728
|
return `(async () => {
|
|
@@ -764,6 +869,7 @@ async function handleWsExec(data) {
|
|
|
764
869
|
const newTabIds = new Set();
|
|
765
870
|
const onCreated = (tab) => { newTabIds.add(tab.id); };
|
|
766
871
|
chrome.tabs.onCreated.addListener(onCreated);
|
|
872
|
+
await setDialogSuppressionByScripting(tabId, true);
|
|
767
873
|
try {
|
|
768
874
|
let res;
|
|
769
875
|
try {
|
|
@@ -788,9 +894,11 @@ async function handleWsExec(data) {
|
|
|
788
894
|
const wrappedCode = buildCdpScript(data.code);
|
|
789
895
|
try {
|
|
790
896
|
await chrome.debugger.attach({ tabId }, '1.3');
|
|
897
|
+
await setDialogSuppressionByCdp(tabId, true);
|
|
791
898
|
const cdpRes = await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {
|
|
792
899
|
expression: wrappedCode, awaitPromise: true, returnByValue: true
|
|
793
900
|
});
|
|
901
|
+
await setDialogSuppressionByCdp(tabId, false);
|
|
794
902
|
await chrome.debugger.detach({ tabId });
|
|
795
903
|
if (cdpRes.exceptionDetails) {
|
|
796
904
|
const desc = cdpRes.exceptionDetails.exception?.description || 'CDP Error';
|
|
@@ -799,6 +907,7 @@ async function handleWsExec(data) {
|
|
|
799
907
|
res = cdpRes.result.value;
|
|
800
908
|
}
|
|
801
909
|
} catch (cdpErr) {
|
|
910
|
+
try { await setDialogSuppressionByCdp(tabId, false); } catch (_) {}
|
|
802
911
|
try { await chrome.debugger.detach({ tabId }); } catch (_) {}
|
|
803
912
|
res = { ok: false, error: { name: 'Error', message: 'CDP fallback failed: ' + cdpErr.message, stack: '' } };
|
|
804
913
|
}
|
|
@@ -812,7 +921,7 @@ async function handleWsExec(data) {
|
|
|
812
921
|
try { const t = await chrome.tabs.get(id); newTabs.push({id: t.id, url: t.url, title: t.title}); } catch (_) {}
|
|
813
922
|
}
|
|
814
923
|
if (res?.ok) {
|
|
815
|
-
ws.send(JSON.stringify({ type: 'result', id: data.id, result: res.data, newTabs }));
|
|
924
|
+
ws.send(JSON.stringify({ type: 'result', id: data.id, result: res.data ?? null, newTabs }));
|
|
816
925
|
} else {
|
|
817
926
|
console.log(res);
|
|
818
927
|
ws.send(JSON.stringify({ type: 'error', id: data.id, error: res?.error || 'Unknown error', newTabs }));
|
|
@@ -820,6 +929,7 @@ async function handleWsExec(data) {
|
|
|
820
929
|
} catch (e) {
|
|
821
930
|
ws.send(JSON.stringify({ type: 'error', id: data.id, error: { name: e.name || 'Error', message: e.message || String(e), stack: e.stack || '' } }));
|
|
822
931
|
} finally {
|
|
932
|
+
await setDialogSuppressionByScripting(tabId, false);
|
|
823
933
|
chrome.tabs.onCreated.removeListener(onCreated);
|
|
824
934
|
}
|
|
825
935
|
}
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function toast(type, msg) {
|
|
5
|
-
_log('[TMWD] ' + type + ' suppressed:', msg);
|
|
6
|
-
try {
|
|
7
|
-
const d = document.createElement('div');
|
|
8
|
-
d.textContent = '[' + type + '] ' + msg;
|
|
9
|
-
Object.assign(d.style, {
|
|
10
|
-
position:'fixed', top:'12px', right:'12px', zIndex:'2147483647',
|
|
11
|
-
background:'#222', color:'#fff', padding:'10px 18px', borderRadius:'8px',
|
|
12
|
-
fontSize:'14px', maxWidth:'420px', wordBreak:'break-all',
|
|
13
|
-
boxShadow:'0 4px 16px rgba(0,0,0,.3)', opacity:'1',
|
|
14
|
-
transition:'opacity .5s', pointerEvents:'none'
|
|
15
|
-
});
|
|
16
|
-
(document.body || document.documentElement).appendChild(d);
|
|
17
|
-
setTimeout(() => { d.style.opacity = '0'; }, 3000);
|
|
18
|
-
setTimeout(() => { d.remove(); }, 3600);
|
|
19
|
-
} catch(e) {}
|
|
20
|
-
}
|
|
21
|
-
window.alert = function(msg) { toast('alert', msg); };
|
|
22
|
-
window.confirm = function(msg) { toast('confirm', msg); return true; };
|
|
23
|
-
window.prompt = function(msg, def) { toast('prompt', msg); return def || null; };
|
|
24
|
-
})();
|
|
1
|
+
// Deprecated: dialog suppression is no longer injected globally.
|
|
2
|
+
// background.js now enables alert/confirm/prompt suppression only during CLI page execution
|
|
3
|
+
// and restores the native functions after the command finishes.
|
|
@@ -23,17 +23,6 @@
|
|
|
23
23
|
"service_worker": "background.js"
|
|
24
24
|
},
|
|
25
25
|
"content_scripts": [
|
|
26
|
-
{
|
|
27
|
-
"matches": [
|
|
28
|
-
"<all_urls>"
|
|
29
|
-
],
|
|
30
|
-
"js": [
|
|
31
|
-
"disable_dialogs.js"
|
|
32
|
-
],
|
|
33
|
-
"run_at": "document_start",
|
|
34
|
-
"all_frames": true,
|
|
35
|
-
"world": "MAIN"
|
|
36
|
-
},
|
|
37
26
|
{
|
|
38
27
|
"matches": [
|
|
39
28
|
"<all_urls>"
|
|
@@ -9,6 +9,7 @@ button{background:#264f78;color:#fff;border:none;padding:4px 12px;cursor:pointer
|
|
|
9
9
|
button:hover{background:#37699e}
|
|
10
10
|
input{width:86px;background:#252526;color:#d4d4d4;border:1px solid #3c3c3c;border-radius:3px;padding:4px;font:12px monospace}
|
|
11
11
|
label{display:inline-flex;align-items:center;gap:6px;margin-right:6px}
|
|
12
|
+
#profileLabel{width:150px}
|
|
12
13
|
.panel{margin-bottom:8px;padding:6px;background:#252526;border-radius:3px}
|
|
13
14
|
.row{display:flex;align-items:center;gap:6px;margin-top:6px}
|
|
14
15
|
.status{color:#9cdcfe}
|
|
@@ -26,6 +27,17 @@ pre{white-space:pre-wrap;word-break:break-all;margin:0;padding:6px;background:#2
|
|
|
26
27
|
</div>
|
|
27
28
|
<div id="portMsg"></div>
|
|
28
29
|
</div>
|
|
30
|
+
<h3>👤 Profile</h3>
|
|
31
|
+
<div class="panel">
|
|
32
|
+
<div id="profileStatus" class="status">读取 Profile...</div>
|
|
33
|
+
<div class="row">
|
|
34
|
+
<label>Label <input id="profileLabel" type="text" maxlength="40" placeholder="work"></label>
|
|
35
|
+
<button id="saveProfileLabel">保存</button>
|
|
36
|
+
<button id="clearProfileLabel">清空</button>
|
|
37
|
+
</div>
|
|
38
|
+
<div id="profileMsg"></div>
|
|
39
|
+
<div class="status">建议用 CLI 设置 label,可校验跨 Profile 唯一性。</div>
|
|
40
|
+
</div>
|
|
29
41
|
<h3>🍪 Cookies</h3>
|
|
30
42
|
<button id="refresh">刷新</button>
|
|
31
43
|
<pre id="out">点击刷新获取 cookies...</pre>
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
document.addEventListener('DOMContentLoaded', () => {
|
|
2
2
|
const btn = document.getElementById('refresh');
|
|
3
3
|
const savePortBtn = document.getElementById('savePort');
|
|
4
|
+
const saveProfileLabelBtn = document.getElementById('saveProfileLabel');
|
|
5
|
+
const clearProfileLabelBtn = document.getElementById('clearProfileLabel');
|
|
4
6
|
btn.addEventListener('click', fetchCookies);
|
|
5
7
|
savePortBtn.addEventListener('click', savePort);
|
|
8
|
+
saveProfileLabelBtn.addEventListener('click', saveProfileLabel);
|
|
9
|
+
clearProfileLabelBtn.addEventListener('click', clearProfileLabel);
|
|
6
10
|
refreshBridgeStatus();
|
|
7
11
|
fetchCookies();
|
|
8
12
|
});
|
|
@@ -16,9 +20,49 @@ async function refreshBridgeStatus() {
|
|
|
16
20
|
const data = resp.data || {};
|
|
17
21
|
portInput.value = data.wsPort || 18765;
|
|
18
22
|
status.textContent = `状态: ${data.wsConnected ? '已连接' : '未连接'} ${data.wsUrl || ''}`;
|
|
23
|
+
renderProfileStatus(data);
|
|
19
24
|
} catch (e) {
|
|
20
25
|
status.textContent = '状态读取失败: ' + e.message;
|
|
21
26
|
status.className = 'error';
|
|
27
|
+
const profileStatus = document.getElementById('profileStatus');
|
|
28
|
+
profileStatus.textContent = 'Profile 读取失败: ' + e.message;
|
|
29
|
+
profileStatus.className = 'error';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function renderProfileStatus(data) {
|
|
34
|
+
const profileStatus = document.getElementById('profileStatus');
|
|
35
|
+
const profileLabelInput = document.getElementById('profileLabel');
|
|
36
|
+
const profileId = data.profileId || '-';
|
|
37
|
+
const browserId = data.browserId || '-';
|
|
38
|
+
const label = data.profileLabel || '';
|
|
39
|
+
profileLabelInput.value = label;
|
|
40
|
+
profileStatus.textContent = `Profile: ${label || '(未设置)'} / ${profileId} / ${browserId}`;
|
|
41
|
+
profileStatus.className = 'status';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function saveProfileLabel() {
|
|
45
|
+
const input = document.getElementById('profileLabel');
|
|
46
|
+
await setProfileLabel(input.value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function clearProfileLabel() {
|
|
50
|
+
const input = document.getElementById('profileLabel');
|
|
51
|
+
input.value = '';
|
|
52
|
+
await setProfileLabel(null);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function setProfileLabel(label) {
|
|
56
|
+
const profileMsg = document.getElementById('profileMsg');
|
|
57
|
+
try {
|
|
58
|
+
const resp = await chrome.runtime.sendMessage({ cmd: 'setProfileLabel', label });
|
|
59
|
+
if (!resp?.ok) throw new Error(resp?.error || 'unknown');
|
|
60
|
+
profileMsg.textContent = `Success: Profile Label ${resp.data?.profileLabel || '已清空'}`;
|
|
61
|
+
profileMsg.className = 'status';
|
|
62
|
+
await refreshBridgeStatus();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
profileMsg.textContent = '保存失败: ' + e.message;
|
|
65
|
+
profileMsg.className = 'error';
|
|
22
66
|
}
|
|
23
67
|
}
|
|
24
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleepinsummer/agent-browser-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Agent-oriented browser sensing and control CLI backed by a native Rust daemon.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"postinstall": "node npm/postinstall.js"
|
|
25
25
|
},
|
|
26
26
|
"optionalDependencies": {
|
|
27
|
-
"@sleepinsummer/agent-browser-cli-darwin-arm64": "0.3.
|
|
28
|
-
"@sleepinsummer/agent-browser-cli-darwin-x64": "0.3.
|
|
29
|
-
"@sleepinsummer/agent-browser-cli-linux-x64": "0.3.
|
|
30
|
-
"@sleepinsummer/agent-browser-cli-linux-arm64": "0.3.
|
|
31
|
-
"@sleepinsummer/agent-browser-cli-win32-x64": "0.3.
|
|
27
|
+
"@sleepinsummer/agent-browser-cli-darwin-arm64": "0.3.3",
|
|
28
|
+
"@sleepinsummer/agent-browser-cli-darwin-x64": "0.3.3",
|
|
29
|
+
"@sleepinsummer/agent-browser-cli-linux-x64": "0.3.3",
|
|
30
|
+
"@sleepinsummer/agent-browser-cli-linux-arm64": "0.3.3",
|
|
31
|
+
"@sleepinsummer/agent-browser-cli-win32-x64": "0.3.3"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
34
|
"node": ">=18"
|
|
@@ -43,7 +43,15 @@ agent-browser-cli status
|
|
|
43
43
|
agent-browser-cli doctor
|
|
44
44
|
agent-browser-cli logs --tail 100
|
|
45
45
|
agent-browser-cli tabs
|
|
46
|
+
agent-browser-cli tabtree
|
|
47
|
+
agent-browser-cli tabtree --full
|
|
48
|
+
agent-browser-cli tabtree --profile work
|
|
49
|
+
agent-browser-cli tabtree --tab <tabId>
|
|
50
|
+
agent-browser-cli lookup tab <tabId>
|
|
51
|
+
agent-browser-cli lookup browser <browser_id>
|
|
52
|
+
agent-browser-cli lookup profile work
|
|
46
53
|
agent-browser-cli tabs --profile work
|
|
54
|
+
agent-browser-cli profile-label set work --profile <profile_id>
|
|
47
55
|
agent-browser-cli scan --tabs-only
|
|
48
56
|
agent-browser-cli scan --profile work --tab <tabId> --text-only
|
|
49
57
|
agent-browser-cli open --profile work https://example.com
|
|
@@ -81,7 +89,7 @@ agent-browser-cli send-keys --target '@e2' 'Enter'
|
|
|
81
89
|
agent-browser-cli mouse-click '@e3'
|
|
82
90
|
```
|
|
83
91
|
|
|
84
|
-
所有高层操作都支持 `--tab <tabId>`,多 Chrome Profile / 多浏览器实例时还支持 `--profile <profile_id-or-label>` 和 `--browser <browser_id>`。`tabs` 输出会包含 `browser_id`、`profile_id`、`profile_label`、`tab_id`、`session_key
|
|
92
|
+
所有高层操作都支持 `--tab <tabId>`,多 Chrome Profile / 多浏览器实例时还支持 `--profile <profile_id-or-label>` 和 `--browser <browser_id>`。`tabs` 输出会包含 `browser_id`、`profile_id`、`profile_label`、`tab_id`、`session_key`。`tabtree` 用树形结构列出 browser → profile → tabs,支持 `--tab <tabId>`、`--profile <profile_id-or-label>`、`--browser <browser_id>` 过滤,过滤结果仍保留父子节点;默认 compact 输出会截断 URL 并省略 `session_key`,需要完整字段时用 `tabtree --full`;`lookup tab <tabId>` 可由 tab 反查 `browser_id` / `profile_id` / `profile_label`,`lookup browser <browser_id>` 可反查所属 profile。`profile-label set <label> --profile <profile_id>` 可设置当前 Chrome Profile 的 label 并校验当前 daemon 内唯一性,`profile-label clear --profile <profile_id>` 可清空;popup 设置是本地便捷入口,不保证跨 Profile 唯一;label 只允许英文、数字、`-`、`_`、`.`,当前 daemon 内匹配到多个 profile 时会报歧义,不能猜。只传 `--tab` 且存在歧义时,必须补 `--profile` 或 `--browser`。`@e` 只在当前 daemon、当前 session_key、最近一次 `snapshot` 内有效。`@e` 只接受 `@e1` 这种带 `@` 的格式。
|
|
85
93
|
|
|
86
94
|
慢页面要把等待和监控分开:
|
|
87
95
|
|
|
@@ -91,6 +99,8 @@ agent-browser-cli click '@e1' --wait-js 'return document.body.innerText.includes
|
|
|
91
99
|
|
|
92
100
|
`--wait-js` 负责等慢加载;`--monitor` 只负责操作前后页面 diff,默认关闭。
|
|
93
101
|
|
|
102
|
+
弹窗处理:扩展默认不改写业务页面的 `alert` / `confirm` / `prompt`。只有 CLI 页面执行命令期间临时抑制弹窗,结束后恢复;如果怀疑页面原生弹窗行为异常,先让用户重载扩展和页面。
|
|
103
|
+
|
|
94
104
|
## 端口和扩展
|
|
95
105
|
|
|
96
106
|
固定 API 端口:
|