@angelky7/dsh-browser-control 1.5.0 → 1.6.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.en.md +5 -4
- package/README.md +5 -4
- package/lib/index.js +133 -1
- package/lib/types/errors.js +4 -0
- package/lib/types/index.js +88 -0
- package/lib/types/policy.js +2 -0
- package/llms.txt +1 -1
- package/package.json +2 -2
package/README.en.md
CHANGED
|
@@ -64,8 +64,8 @@ In one line: **for "use MY browser" tasks (logged-in Bilibili, Zhihu, admin pane
|
|
|
64
64
|
|
|
65
65
|
| File | Purpose |
|
|
66
66
|
|---|---|
|
|
67
|
-
| [DSH-Browser-Control-v1.
|
|
68
|
-
| [dsh-browser-control-plugin-v1.
|
|
67
|
+
| [DSH-Browser-Control-v1.6.0.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.6.0/DSH-Browser-Control-v1.6.0.zip) | Chrome extension (unzip and load; this release adds the windows commands — reinstall required) |
|
|
68
|
+
| [dsh-browser-control-plugin-v1.6.0.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.6.0/dsh-browser-control-plugin-v1.6.0.zip) | dsh plugin (offline fallback; online installs use Option A/B) |
|
|
69
69
|
|
|
70
70
|
## Install the Chrome extension (30 seconds)
|
|
71
71
|
|
|
@@ -104,7 +104,7 @@ npm install @angelky7/dsh-browser-control
|
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
# Straight from GitHub
|
|
107
|
-
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.
|
|
107
|
+
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.6.0"
|
|
108
108
|
|
|
109
109
|
# Local checkout for debugging (note: the explicit file: prefix is required)
|
|
110
110
|
dsh plugin --profile web add "file:D:\path\to\dsh-browser-control"
|
|
@@ -264,11 +264,12 @@ curl -X POST http://127.0.0.1:9777/api/command \
|
|
|
264
264
|
| `browser_network_clear` | Clear the network log buffer on its own (v1.0.7+) |
|
|
265
265
|
| `browser_pdf` | Export the current page as PDF (v1.0.7+) |
|
|
266
266
|
| `browser_archive` | Save the whole page as a single-file MHTML (text/images/styles inline; a greppable offline evidence snapshot — embedded scripts do not run, v1.5.0+) |
|
|
267
|
+
| `browser_windows` | Window control: list / open / update state & geometry / close a whole window / move tabs across windows (the only tool that may touch window geometry, v1.6.0+) |
|
|
267
268
|
| `browser_emulate` | Switch to a device viewport (mobile / desktop / custom, v1.0.7+) |
|
|
268
269
|
| `browser_cleanup` | Clean up temp files |
|
|
269
270
|
| `browser_info` | Which browser and extension build is attached (run it first when debugging, v1.0.9+) |
|
|
270
271
|
|
|
271
|
-
|
|
272
|
+
24 tools in total. `browser_wait`, `browser_find`, `browser_dialog` and `browser_info` existed inside the extension up to v1.0.8 but were never registered as tools, so the agent could not reach them; `browser_network_clear` existed in code and was simply missing from this table. `verify/verify_surface.mjs` now reconciles the extension command table, the commands the plugin calls, this table and the built bundle, so any drift fails `npm run verify`.
|
|
272
273
|
|
|
273
274
|
## Architecture
|
|
274
275
|
|
package/README.md
CHANGED
|
@@ -64,8 +64,8 @@ Chrome 扩展(CDP 驱动)
|
|
|
64
64
|
|
|
65
65
|
| 文件 | 说明 |
|
|
66
66
|
|---|---|
|
|
67
|
-
| [DSH-Browser-Control-v1.
|
|
68
|
-
| [dsh-browser-control-plugin-v1.
|
|
67
|
+
| [DSH-Browser-Control-v1.6.0.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.6.0/DSH-Browser-Control-v1.6.0.zip) | Chrome 扩展(解压后加载;这一版扩展新增 windows 窗口控制命令,**必须重装**) |
|
|
68
|
+
| [dsh-browser-control-plugin-v1.6.0.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.6.0/dsh-browser-control-plugin-v1.6.0.zip) | dsh 插件(离线兜底,在线装直接用方式 A/B) |
|
|
69
69
|
|
|
70
70
|
## 安装 Chrome 扩展(30 秒)
|
|
71
71
|
|
|
@@ -104,7 +104,7 @@ npm install @angelky7/dsh-browser-control
|
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
# 直接从 GitHub 安装
|
|
107
|
-
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.
|
|
107
|
+
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.6.0"
|
|
108
108
|
|
|
109
109
|
# 本地目录调试(注意:必须显式 file: 前缀)
|
|
110
110
|
dsh plugin --profile web add "file:D:\path\to\dsh-browser-control"
|
|
@@ -259,10 +259,11 @@ curl -X POST http://127.0.0.1:9777/api/command \
|
|
|
259
259
|
| `browser_pdf` | 当前页导出 PDF(v1.0.7+) |
|
|
260
260
|
| `browser_archive` | 整页存成单文件 MHTML(正文/图片/样式全内联,可当文本 grep 的离线证据;是快照,内嵌脚本不会跑,v1.5.0+) |
|
|
261
261
|
| `browser_emulate` | 切设备视口(移动 / 桌面 / 自定义,v1.0.7+) |
|
|
262
|
+
| `browser_windows` | 窗口控制:清单/开窗/改状态与几何/关整窗/跨窗挪标签(唯一能碰窗口几何的工具,v1.6.0+) |
|
|
262
263
|
| `browser_cleanup` | 清理临时文件 |
|
|
263
264
|
| `browser_info` | 报告实际连上的浏览器与扩展版本(排障先跑它,v1.0.9+) |
|
|
264
265
|
|
|
265
|
-
共
|
|
266
|
+
共 24 个工具。`browser_wait` / `browser_find` / `browser_dialog` / `browser_info` 在 v1.0.8 及以前是「扩展里已实现、但没注册成工具」,agent 调不到;`browser_network_clear` 则是代码里有、这张表漏了。这些现在由 `verify/verify_surface.mjs` 做三方对账(扩展命令表 ↔ 插件调用 ↔ 文档表 ↔ 构建产物),漂移会直接让 `npm run verify` 失败。
|
|
266
267
|
|
|
267
268
|
## 架构
|
|
268
269
|
|
package/lib/index.js
CHANGED
|
@@ -118,7 +118,8 @@ const WRITE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
118
118
|
"tabs.activate",
|
|
119
119
|
"dialog",
|
|
120
120
|
"cleanup",
|
|
121
|
-
"hold"
|
|
121
|
+
"hold",
|
|
122
|
+
"windows.open"
|
|
122
123
|
]);
|
|
123
124
|
/** 该命令是否可能留下副作用。 */
|
|
124
125
|
function isWriteCommand(command) {
|
|
@@ -874,6 +875,7 @@ const READ_ONLY_ALLOWED = /* @__PURE__ */ new Set([
|
|
|
874
875
|
"tabs.list",
|
|
875
876
|
"console.log",
|
|
876
877
|
"network.log",
|
|
878
|
+
"windows.list",
|
|
877
879
|
"cookies.get"
|
|
878
880
|
]);
|
|
879
881
|
/**
|
|
@@ -1183,6 +1185,10 @@ function requireTabId(args, action) {
|
|
|
1183
1185
|
if (typeof args.tabId !== "number") throw new Error(`${action} requires tabId`);
|
|
1184
1186
|
return args.tabId;
|
|
1185
1187
|
}
|
|
1188
|
+
function requireWindowId(args, action) {
|
|
1189
|
+
if (typeof args.windowId !== "number") throw new Error(`${action} requires windowId`);
|
|
1190
|
+
return args.windowId;
|
|
1191
|
+
}
|
|
1186
1192
|
/**
|
|
1187
1193
|
* Every tab the caller asked to close. `tabId` and `tabIds` are both accepted
|
|
1188
1194
|
* (a batch call that repeats one id, or passes both, must not close it twice),
|
|
@@ -2125,6 +2131,132 @@ function applyBrowserTools(ctx, controller) {
|
|
|
2125
2131
|
}
|
|
2126
2132
|
}
|
|
2127
2133
|
}));
|
|
2134
|
+
ctx.tools.register(defineTool({
|
|
2135
|
+
name: "browser_windows",
|
|
2136
|
+
description: "Inspect and control browser windows: list windows with geometry and state, open a new window (optionally positioned/sized), update state (minimize / maximize / fullscreen / focus) or geometry, close a whole window (all its tabs go too), or move tabs between windows. Geometry and state changes happen only through this tool. open is the only non-idempotent action: if it times out, run action:\"list\" first — the window may already exist.",
|
|
2137
|
+
parameters: {
|
|
2138
|
+
action: {
|
|
2139
|
+
type: "string",
|
|
2140
|
+
required: true,
|
|
2141
|
+
description: "One of: list, open, update, close, moveTab."
|
|
2142
|
+
},
|
|
2143
|
+
windowId: {
|
|
2144
|
+
type: "number",
|
|
2145
|
+
description: "Target window for update/close/moveTab."
|
|
2146
|
+
},
|
|
2147
|
+
url: {
|
|
2148
|
+
type: "string",
|
|
2149
|
+
description: "For open: a URL or an array of URLs (one tab each); defaults to about:blank."
|
|
2150
|
+
},
|
|
2151
|
+
state: {
|
|
2152
|
+
type: "string",
|
|
2153
|
+
description: "For open/update: normal | minimized | maximized | fullscreen."
|
|
2154
|
+
},
|
|
2155
|
+
left: {
|
|
2156
|
+
type: "number",
|
|
2157
|
+
description: "For open/update: window left edge in screen pixels."
|
|
2158
|
+
},
|
|
2159
|
+
top: {
|
|
2160
|
+
type: "number",
|
|
2161
|
+
description: "For open/update: window top edge in screen pixels."
|
|
2162
|
+
},
|
|
2163
|
+
width: {
|
|
2164
|
+
type: "number",
|
|
2165
|
+
description: "For open/update: window width in screen pixels."
|
|
2166
|
+
},
|
|
2167
|
+
height: {
|
|
2168
|
+
type: "number",
|
|
2169
|
+
description: "For open/update: window height in screen pixels."
|
|
2170
|
+
},
|
|
2171
|
+
focused: {
|
|
2172
|
+
type: "boolean",
|
|
2173
|
+
description: "For open/update: focus the window. focused:true on a minimized window restores it WITHOUT un-maximizing (state:\"normal\" would un-maximize)."
|
|
2174
|
+
},
|
|
2175
|
+
alwaysOnTop: {
|
|
2176
|
+
type: "boolean",
|
|
2177
|
+
description: "For update: keep the window above others."
|
|
2178
|
+
},
|
|
2179
|
+
drawAttention: {
|
|
2180
|
+
type: "boolean",
|
|
2181
|
+
description: "For update: flash the taskbar entry to get the user's attention without stealing focus."
|
|
2182
|
+
},
|
|
2183
|
+
incognito: {
|
|
2184
|
+
type: "boolean",
|
|
2185
|
+
description: "For open: open in incognito. Fails honestly when the extension is not allowed in incognito."
|
|
2186
|
+
},
|
|
2187
|
+
tabId: {
|
|
2188
|
+
type: "number",
|
|
2189
|
+
description: "For moveTab: one tab to move."
|
|
2190
|
+
},
|
|
2191
|
+
tabIds: {
|
|
2192
|
+
type: "array",
|
|
2193
|
+
items: { type: "number" },
|
|
2194
|
+
description: "For moveTab: several tabs to move in order; each is reported individually."
|
|
2195
|
+
},
|
|
2196
|
+
index: {
|
|
2197
|
+
type: "number",
|
|
2198
|
+
description: "For moveTab: target position in the destination window; omit for the end."
|
|
2199
|
+
}
|
|
2200
|
+
},
|
|
2201
|
+
output: {
|
|
2202
|
+
schema: {
|
|
2203
|
+
type: "object",
|
|
2204
|
+
additionalProperties: true
|
|
2205
|
+
},
|
|
2206
|
+
render: (_args, value) => [{
|
|
2207
|
+
type: "text",
|
|
2208
|
+
text: clampRendered(JSON.stringify(value))
|
|
2209
|
+
}]
|
|
2210
|
+
},
|
|
2211
|
+
presentCall: (args) => ({
|
|
2212
|
+
card: "generic",
|
|
2213
|
+
title: `Browser windows: ${args.action}`,
|
|
2214
|
+
kind: "other"
|
|
2215
|
+
}),
|
|
2216
|
+
async execute(args, exec) {
|
|
2217
|
+
switch (args.action) {
|
|
2218
|
+
case "list": return await controller.execute("windows.list", {}, exec.signal);
|
|
2219
|
+
case "open": {
|
|
2220
|
+
const params = {};
|
|
2221
|
+
if (args.url !== void 0) params.url = args.url;
|
|
2222
|
+
if (args.state !== void 0) params.state = args.state;
|
|
2223
|
+
if (args.incognito === true) params.incognito = true;
|
|
2224
|
+
for (const k of [
|
|
2225
|
+
"left",
|
|
2226
|
+
"top",
|
|
2227
|
+
"width",
|
|
2228
|
+
"height"
|
|
2229
|
+
]) if (typeof args[k] === "number") params[k] = args[k];
|
|
2230
|
+
if (args.focused !== void 0) params.focused = args.focused;
|
|
2231
|
+
return await controller.execute("windows.open", params, exec.signal);
|
|
2232
|
+
}
|
|
2233
|
+
case "update": {
|
|
2234
|
+
const params = { windowId: requireWindowId(args, "update") };
|
|
2235
|
+
if (args.state !== void 0) params.state = args.state;
|
|
2236
|
+
for (const k of [
|
|
2237
|
+
"left",
|
|
2238
|
+
"top",
|
|
2239
|
+
"width",
|
|
2240
|
+
"height"
|
|
2241
|
+
]) if (typeof args[k] === "number") params[k] = args[k];
|
|
2242
|
+
if (args.focused !== void 0) params.focused = args.focused;
|
|
2243
|
+
if (args.alwaysOnTop !== void 0) params.alwaysOnTop = args.alwaysOnTop;
|
|
2244
|
+
if (args.drawAttention === true) params.drawAttention = true;
|
|
2245
|
+
return await controller.execute("windows.update", params, exec.signal);
|
|
2246
|
+
}
|
|
2247
|
+
case "close": return await controller.execute("windows.close", { windowId: requireWindowId(args, "close") }, exec.signal);
|
|
2248
|
+
case "moveTab": {
|
|
2249
|
+
if (args.tabId === void 0 && args.tabIds === void 0) throw new Error("moveTab requires tabId or tabIds");
|
|
2250
|
+
const params = { windowId: requireWindowId(args, "moveTab") };
|
|
2251
|
+
if (args.tabId !== void 0) params.tabId = args.tabId;
|
|
2252
|
+
if (args.tabIds !== void 0) params.tabIds = args.tabIds;
|
|
2253
|
+
if (args.index !== void 0) params.index = args.index;
|
|
2254
|
+
return await controller.execute("windows.moveTab", params, exec.signal);
|
|
2255
|
+
}
|
|
2256
|
+
default: throw new Error(`unknown windows action: ${String(args.action)} (use list|open|update|close|moveTab)`);
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
}));
|
|
2128
2260
|
/** Page-side ceiling of the eval race in the extension; the bridge budget adds slack on top. */
|
|
2129
2261
|
const EVAL_MAX_MS = 12e4;
|
|
2130
2262
|
ctx.tools.register(defineTool({
|
package/lib/types/errors.js
CHANGED
|
@@ -48,6 +48,10 @@ const WRITE_COMMANDS = new Set([
|
|
|
48
48
|
// 往页面里插一个宿主元素 = 改页面 DOM。超时/掉线之后重试,可能多留一层浮层
|
|
49
49
|
//(旧的那层还挂着),所以报 unknown 而不是「没动过页面」。
|
|
50
50
|
'hold',
|
|
51
|
+
// 开窗不是幂等的:超时/掉线后重试可能真的开出第二个窗口——必须报 unknown。
|
|
52
|
+
// update/close/moveTab 是幂等的(重设同一几何、关已关的窗、挪到同一位置),
|
|
53
|
+
// 按判据算读,登记在 verify_errors 的 classifiedRead 里。
|
|
54
|
+
'windows.open',
|
|
51
55
|
]);
|
|
52
56
|
/** 该命令是否可能留下副作用。 */
|
|
53
57
|
export function isWriteCommand(command) {
|
package/lib/types/index.js
CHANGED
|
@@ -289,6 +289,11 @@ function requireTabId(args, action) {
|
|
|
289
289
|
throw new Error(`${action} requires tabId`);
|
|
290
290
|
return args.tabId;
|
|
291
291
|
}
|
|
292
|
+
function requireWindowId(args, action) {
|
|
293
|
+
if (typeof args.windowId !== 'number')
|
|
294
|
+
throw new Error(`${action} requires windowId`);
|
|
295
|
+
return args.windowId;
|
|
296
|
+
}
|
|
292
297
|
/**
|
|
293
298
|
* Every tab the caller asked to close. `tabId` and `tabIds` are both accepted
|
|
294
299
|
* (a batch call that repeats one id, or passes both, must not close it twice),
|
|
@@ -942,6 +947,89 @@ function applyBrowserTools(ctx, controller) {
|
|
|
942
947
|
}
|
|
943
948
|
},
|
|
944
949
|
}));
|
|
950
|
+
// Window control is an explicit, opt-in surface: no other tool ever touches
|
|
951
|
+
// window geometry or state (the input path only focuses), so everything the
|
|
952
|
+
// model does to the user's windows is visible as a browser_windows call.
|
|
953
|
+
ctx.tools.register(defineTool({
|
|
954
|
+
name: 'browser_windows',
|
|
955
|
+
description: 'Inspect and control browser windows: list windows with geometry and state, open a new window (optionally positioned/sized), update state (minimize / maximize / fullscreen / focus) or geometry, close a whole window (all its tabs go too), or move tabs between windows. Geometry and state changes happen only through this tool. open is the only non-idempotent action: if it times out, run action:"list" first — the window may already exist.',
|
|
956
|
+
parameters: {
|
|
957
|
+
action: { type: 'string', required: true, description: 'One of: list, open, update, close, moveTab.' },
|
|
958
|
+
windowId: { type: 'number', description: 'Target window for update/close/moveTab.' },
|
|
959
|
+
url: { type: 'string', description: 'For open: a URL or an array of URLs (one tab each); defaults to about:blank.' },
|
|
960
|
+
state: { type: 'string', description: 'For open/update: normal | minimized | maximized | fullscreen.' },
|
|
961
|
+
left: { type: 'number', description: 'For open/update: window left edge in screen pixels.' },
|
|
962
|
+
top: { type: 'number', description: 'For open/update: window top edge in screen pixels.' },
|
|
963
|
+
width: { type: 'number', description: 'For open/update: window width in screen pixels.' },
|
|
964
|
+
height: { type: 'number', description: 'For open/update: window height in screen pixels.' },
|
|
965
|
+
focused: { type: 'boolean', description: 'For open/update: focus the window. focused:true on a minimized window restores it WITHOUT un-maximizing (state:"normal" would un-maximize).' },
|
|
966
|
+
alwaysOnTop: { type: 'boolean', description: 'For update: keep the window above others.' },
|
|
967
|
+
drawAttention: { type: 'boolean', description: 'For update: flash the taskbar entry to get the user\'s attention without stealing focus.' },
|
|
968
|
+
incognito: { type: 'boolean', description: 'For open: open in incognito. Fails honestly when the extension is not allowed in incognito.' },
|
|
969
|
+
tabId: { type: 'number', description: 'For moveTab: one tab to move.' },
|
|
970
|
+
tabIds: { type: 'array', items: { type: 'number' }, description: 'For moveTab: several tabs to move in order; each is reported individually.' },
|
|
971
|
+
index: { type: 'number', description: 'For moveTab: target position in the destination window; omit for the end.' },
|
|
972
|
+
},
|
|
973
|
+
output: {
|
|
974
|
+
schema: { type: 'object', additionalProperties: true },
|
|
975
|
+
render: (_args, value) => [{ type: 'text', text: clampRendered(JSON.stringify(value)) }],
|
|
976
|
+
},
|
|
977
|
+
presentCall: args => ({ card: 'generic', title: `Browser windows: ${args.action}`, kind: 'other' }),
|
|
978
|
+
async execute(args, exec) {
|
|
979
|
+
switch (args.action) {
|
|
980
|
+
case 'list':
|
|
981
|
+
return await controller.execute('windows.list', {}, exec.signal);
|
|
982
|
+
case 'open': {
|
|
983
|
+
const params = {};
|
|
984
|
+
if (args.url !== undefined)
|
|
985
|
+
params.url = args.url;
|
|
986
|
+
if (args.state !== undefined)
|
|
987
|
+
params.state = args.state;
|
|
988
|
+
if (args.incognito === true)
|
|
989
|
+
params.incognito = true;
|
|
990
|
+
for (const k of ['left', 'top', 'width', 'height']) {
|
|
991
|
+
if (typeof args[k] === 'number')
|
|
992
|
+
params[k] = args[k];
|
|
993
|
+
}
|
|
994
|
+
if (args.focused !== undefined)
|
|
995
|
+
params.focused = args.focused;
|
|
996
|
+
return await controller.execute('windows.open', params, exec.signal);
|
|
997
|
+
}
|
|
998
|
+
case 'update': {
|
|
999
|
+
const params = { windowId: requireWindowId(args, 'update') };
|
|
1000
|
+
if (args.state !== undefined)
|
|
1001
|
+
params.state = args.state;
|
|
1002
|
+
for (const k of ['left', 'top', 'width', 'height']) {
|
|
1003
|
+
if (typeof args[k] === 'number')
|
|
1004
|
+
params[k] = args[k];
|
|
1005
|
+
}
|
|
1006
|
+
if (args.focused !== undefined)
|
|
1007
|
+
params.focused = args.focused;
|
|
1008
|
+
if (args.alwaysOnTop !== undefined)
|
|
1009
|
+
params.alwaysOnTop = args.alwaysOnTop;
|
|
1010
|
+
if (args.drawAttention === true)
|
|
1011
|
+
params.drawAttention = true;
|
|
1012
|
+
return await controller.execute('windows.update', params, exec.signal);
|
|
1013
|
+
}
|
|
1014
|
+
case 'close':
|
|
1015
|
+
return await controller.execute('windows.close', { windowId: requireWindowId(args, 'close') }, exec.signal);
|
|
1016
|
+
case 'moveTab': {
|
|
1017
|
+
if (args.tabId === undefined && args.tabIds === undefined)
|
|
1018
|
+
throw new Error('moveTab requires tabId or tabIds');
|
|
1019
|
+
const params = { windowId: requireWindowId(args, 'moveTab') };
|
|
1020
|
+
if (args.tabId !== undefined)
|
|
1021
|
+
params.tabId = args.tabId;
|
|
1022
|
+
if (args.tabIds !== undefined)
|
|
1023
|
+
params.tabIds = args.tabIds;
|
|
1024
|
+
if (args.index !== undefined)
|
|
1025
|
+
params.index = args.index;
|
|
1026
|
+
return await controller.execute('windows.moveTab', params, exec.signal);
|
|
1027
|
+
}
|
|
1028
|
+
default:
|
|
1029
|
+
throw new Error(`unknown windows action: ${String(args.action)} (use list|open|update|close|moveTab)`);
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
1032
|
+
}));
|
|
945
1033
|
/** Page-side ceiling of the eval race in the extension; the bridge budget adds slack on top. */
|
|
946
1034
|
const EVAL_MAX_MS = 120_000;
|
|
947
1035
|
ctx.tools.register(defineTool({
|
package/lib/types/policy.js
CHANGED
|
@@ -24,6 +24,8 @@ export const READ_ONLY_ALLOWED = new Set([
|
|
|
24
24
|
'content', 'find', 'snapshot', 'screenshot', 'pdf', 'wait',
|
|
25
25
|
// 读扩展采集到的缓冲
|
|
26
26
|
'tabs.list', 'console.log', 'network.log',
|
|
27
|
+
// 读窗口清单(几何/状态/tabIds);open/update/close/moveTab 改窗口,不在此列
|
|
28
|
+
'windows.list',
|
|
27
29
|
// 读浏览器自己的状态(cookie 是凭据,但 get 不改任何东西;set/remove 不在此列)
|
|
28
30
|
'cookies.get',
|
|
29
31
|
]);
|
package/llms.txt
CHANGED
|
@@ -35,7 +35,7 @@ Chrome 浏览器扩展 + DeepSeek Harness 插件,让 AI Agent 通过 CDP 驱
|
|
|
35
35
|
|
|
36
36
|
## 关键文件
|
|
37
37
|
|
|
38
|
-
- `src/index.ts` -
|
|
38
|
+
- `src/index.ts` - 24 个 `browser_*` 工具注册 + settings 接线的版本兼容分支
|
|
39
39
|
- `src/policy.ts` - 三张互不相同的表:只读白名单、脚本命令、插件本地破坏性命令(`browser_cleanup` 不经过 `execute()`,必须自己站闸门)
|
|
40
40
|
- `src/server.ts` - WebSocket 服务 + HTTP 面(鉴权、来源校验、状态页)
|
|
41
41
|
- `src/ws.ts` - RFC 6455 帧编解码
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angelky7/dsh-browser-control",
|
|
3
3
|
"description": "Local WebSocket bridge between dsh and the DSH Browser Control browser extension, with model-facing browser_* tools and a Settings-managed on/off switch",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"build": "tsc -p tsconfig.json && tsdown",
|
|
38
38
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
39
|
"check": "npm run typecheck && npm run build && publint",
|
|
40
|
-
"verify": "node verify/verify_config_patch.mjs && node verify/verify_peers.mjs && node verify/verify_surface.mjs && node verify/verify_tab_groups.mjs && node verify/verify_tabs_close_many.mjs && node verify/verify_cdp_retry.mjs && node verify/verify_snapshot_ax.mjs && node verify/verify_hard_lock.mjs && node verify/verify_policy.mjs && node verify/verify_cookies.mjs && node verify/verify_extension_e2e.mjs && node verify/verify_plugin.mjs legacy && node verify/verify_plugin.mjs conflict && node verify/verify_plugin.mjs new && node verify/verify_http.mjs && node verify/verify_errors.mjs && node verify/verify_hold.mjs && node verify/verify_protocol.mjs && node verify/verify_bundle_http.mjs && node verify/verify_nav_report.mjs && node verify/verify_nav_landing.mjs && node verify/verify_render_contract.mjs && node verify/verify_press_events.mjs && node verify/verify_nav_timeout.mjs && node verify/verify_nav_waituntil.mjs && node verify/verify_archive.mjs"
|
|
40
|
+
"verify": "node verify/verify_config_patch.mjs && node verify/verify_peers.mjs && node verify/verify_surface.mjs && node verify/verify_tab_groups.mjs && node verify/verify_tabs_close_many.mjs && node verify/verify_cdp_retry.mjs && node verify/verify_snapshot_ax.mjs && node verify/verify_hard_lock.mjs && node verify/verify_policy.mjs && node verify/verify_cookies.mjs && node verify/verify_extension_e2e.mjs && node verify/verify_plugin.mjs legacy && node verify/verify_plugin.mjs conflict && node verify/verify_plugin.mjs new && node verify/verify_http.mjs && node verify/verify_errors.mjs && node verify/verify_hold.mjs && node verify/verify_protocol.mjs && node verify/verify_bundle_http.mjs && node verify/verify_nav_report.mjs && node verify/verify_nav_landing.mjs && node verify/verify_render_contract.mjs && node verify/verify_press_events.mjs && node verify/verify_nav_timeout.mjs && node verify/verify_nav_waituntil.mjs && node verify/verify_archive.mjs && node verify/verify_windows.mjs"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public",
|