@angelky7/dsh-browser-control 1.7.6 → 1.7.7
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 +1 -1
- package/lib/index.js +36 -4
- package/lib/types/index.js +37 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -240,7 +240,7 @@ curl -X POST http://127.0.0.1:9777/api/command \
|
|
|
240
240
|
|---|---|
|
|
241
241
|
| `browser_navigate` | 导航到 URL |
|
|
242
242
|
| `browser_read` | 读取页面文本/HTML,正文直接给模型(`offset`/`limit` 窗口,长文分页) |
|
|
243
|
-
| `browser_snapshot` | 页面快照 → 逐条列出 ref 的交互树(`mode:"ax"` 走 Chrome 无障碍树,拿 role +
|
|
243
|
+
| `browser_snapshot` | 页面快照 → 逐条列出 ref 的交互树(`mode:"ax"` 走 Chrome 无障碍树,拿 role + 可访问名);v1.7.7 起支持 `offset` **分页**:页头写明「第几到第几 / 页面总数 / 下一页 offset」,超出扩展 200 条窗口的部分如实说明用 find/read 补齐 |
|
|
244
244
|
| `browser_click` | 点击元素(by ref / selector) |
|
|
245
245
|
| `browser_type` | 在输入框填入文本 |
|
|
246
246
|
| `browser_press` | 模拟键盘按键(回车、Tab、方向键,以及 "Ctrl+A" / "Shift+ArrowRight" 这类组合键;一次按键只插入一次) |
|
package/lib/index.js
CHANGED
|
@@ -2032,9 +2032,13 @@ function applyBrowserTools(ctx, controller) {
|
|
|
2032
2032
|
type: "number",
|
|
2033
2033
|
description: "Target tab; defaults to the active tab."
|
|
2034
2034
|
},
|
|
2035
|
+
offset: {
|
|
2036
|
+
type: "number",
|
|
2037
|
+
description: "Skip this many items into the inventory (paging); default 0. Refs are valid for THIS call only — take the next page in the same breath if you need stable refs."
|
|
2038
|
+
},
|
|
2035
2039
|
limit: {
|
|
2036
2040
|
type: "number",
|
|
2037
|
-
description: "
|
|
2041
|
+
description: "Page size: max items returned by this call; defaults to 120. The extension supplies up to a 200-item window per call."
|
|
2038
2042
|
},
|
|
2039
2043
|
mode: {
|
|
2040
2044
|
type: "string",
|
|
@@ -2060,6 +2064,17 @@ function applyBrowserTools(ctx, controller) {
|
|
|
2060
2064
|
},
|
|
2061
2065
|
mode: { type: "string" },
|
|
2062
2066
|
axFallback: { type: "string" },
|
|
2067
|
+
offset: {
|
|
2068
|
+
type: "number",
|
|
2069
|
+
required: true
|
|
2070
|
+
},
|
|
2071
|
+
total: { type: "number" },
|
|
2072
|
+
returned: { type: "number" },
|
|
2073
|
+
nextOffset: { type: "number" },
|
|
2074
|
+
hasMore: {
|
|
2075
|
+
type: "boolean",
|
|
2076
|
+
required: true
|
|
2077
|
+
},
|
|
2063
2078
|
items: {
|
|
2064
2079
|
type: "array",
|
|
2065
2080
|
required: true,
|
|
@@ -2084,9 +2099,13 @@ function applyBrowserTools(ctx, controller) {
|
|
|
2084
2099
|
}
|
|
2085
2100
|
},
|
|
2086
2101
|
render: (_args, value) => {
|
|
2102
|
+
const shown = value.items.length;
|
|
2103
|
+
const start = value.offset + 1;
|
|
2104
|
+
const end = value.offset + shown;
|
|
2105
|
+
const totalText = value.total === void 0 ? `${value.returned ?? shown} available` : `${value.total} on the page`;
|
|
2087
2106
|
return [{
|
|
2088
2107
|
type: "text",
|
|
2089
|
-
text: clampRendered([`${value.items.
|
|
2108
|
+
text: clampRendered([`${shown === 0 ? `no items at offset ${value.offset}` : `items ${start}–${end} of ${totalText}`}${value.total !== void 0 && value.returned !== void 0 && value.total > value.returned ? ` (this extension returns at most ${value.returned}-item windows — use browser_find/browser_read to reach the rest)` : ""}${value.nextOffset === void 0 ? "" : `; next page: offset ${value.nextOffset}`} (mode ${value.mode ?? "dom"}); click or fill them by ref:` + (value.axFallback === void 0 ? "" : `\n(mode ax fell back to dom: ${value.axFallback})`), ...value.items.map((item) => {
|
|
2090
2109
|
const name = item.name === void 0 ? "" : ` "${flattenOneLine(item.name, 80)}"`;
|
|
2091
2110
|
const href = item.href === void 0 ? "" : ` -> ${flattenOneLine(item.href, 100)}`;
|
|
2092
2111
|
return `[${item.ref}] ${item.role ?? item.tag}${name}${href}`;
|
|
@@ -2100,20 +2119,33 @@ function applyBrowserTools(ctx, controller) {
|
|
|
2100
2119
|
kind: "other"
|
|
2101
2120
|
}),
|
|
2102
2121
|
async execute(args, exec) {
|
|
2103
|
-
const
|
|
2122
|
+
const offset = Math.max(0, Math.trunc(typeof args.offset === "number" && Number.isFinite(args.offset) ? args.offset : 0));
|
|
2123
|
+
const limit = Math.min(200, Math.max(1, typeof args.limit === "number" && Number.isFinite(args.limit) ? Math.trunc(args.limit) : 120));
|
|
2124
|
+
const params = { limit: 200 };
|
|
2104
2125
|
if (typeof args.tabId === "number") params.tabId = args.tabId;
|
|
2105
2126
|
if (typeof args.mode === "string") params.mode = args.mode;
|
|
2106
2127
|
const raw = await controller.execute("snapshot", params, exec.signal);
|
|
2107
2128
|
const requestedAx = args.mode === "ax";
|
|
2108
2129
|
const gotAx = raw.mode === "ax" || raw.ax !== void 0;
|
|
2109
2130
|
const axFallback = typeof raw.axFallback === "string" && raw.axFallback.length > 0 ? raw.axFallback : requestedAx && !gotAx ? "this extension ignored mode:\"ax\" and returned CSS-DOM matches (AX mode needs DSH Browser Control extension v1.0.11 or newer) — the names below are tag names, not accessible names" : void 0;
|
|
2131
|
+
const window = Array.isArray(raw.items) ? raw.items : [];
|
|
2132
|
+
const returned = window.length;
|
|
2133
|
+
const reportedTotal = typeof raw.total === "number" && Number.isFinite(raw.total) ? Math.trunc(raw.total) : void 0;
|
|
2134
|
+
const total = reportedTotal === void 0 ? void 0 : Math.max(returned, reportedTotal);
|
|
2135
|
+
const page = window.slice(offset, offset + limit);
|
|
2136
|
+
const hasMore = offset + page.length < returned;
|
|
2110
2137
|
return {
|
|
2111
2138
|
tabId: raw.tabId,
|
|
2112
2139
|
title: raw.title ?? "",
|
|
2113
2140
|
url: raw.url ?? "",
|
|
2114
2141
|
...raw.mode === void 0 ? {} : { mode: raw.mode },
|
|
2115
2142
|
...axFallback === void 0 ? {} : { axFallback },
|
|
2116
|
-
|
|
2143
|
+
offset,
|
|
2144
|
+
...total === void 0 ? {} : { total },
|
|
2145
|
+
returned,
|
|
2146
|
+
hasMore,
|
|
2147
|
+
...hasMore ? { nextOffset: offset + page.length } : {},
|
|
2148
|
+
items: page.map((item) => ({
|
|
2117
2149
|
ref: item.ref,
|
|
2118
2150
|
tag: item.tag,
|
|
2119
2151
|
...item.role === void 0 ? {} : { role: item.role },
|
package/lib/types/index.js
CHANGED
|
@@ -809,7 +809,8 @@ function applyBrowserTools(ctx, controller) {
|
|
|
809
809
|
description: 'Inventory the page\'s interactive elements with stable refs; pass a ref to browser_click/browser_type afterwards. mode:"ax" numbers them by Chrome\'s accessibility tree (role + accessible name), which survives a DOM redesign; the default mode matches CSS selectors.',
|
|
810
810
|
parameters: {
|
|
811
811
|
tabId: { type: 'number', description: 'Target tab; defaults to the active tab.' },
|
|
812
|
-
|
|
812
|
+
offset: { type: 'number', description: 'Skip this many items into the inventory (paging); default 0. Refs are valid for THIS call only — take the next page in the same breath if you need stable refs.' },
|
|
813
|
+
limit: { type: 'number', description: 'Page size: max items returned by this call; defaults to 120. The extension supplies up to a 200-item window per call.' },
|
|
813
814
|
mode: { type: 'string', description: '"ax" for the accessibility tree, "dom" (default) for the selector list. "ax" falls back to "dom" with axFallback set if the page gives no a11y tree.' },
|
|
814
815
|
},
|
|
815
816
|
output: {
|
|
@@ -822,6 +823,11 @@ function applyBrowserTools(ctx, controller) {
|
|
|
822
823
|
url: { type: 'string', required: true },
|
|
823
824
|
mode: { type: 'string' },
|
|
824
825
|
axFallback: { type: 'string' },
|
|
826
|
+
offset: { type: 'number', required: true },
|
|
827
|
+
total: { type: 'number' },
|
|
828
|
+
returned: { type: 'number' },
|
|
829
|
+
nextOffset: { type: 'number' },
|
|
830
|
+
hasMore: { type: 'boolean', required: true },
|
|
825
831
|
items: {
|
|
826
832
|
type: 'array',
|
|
827
833
|
required: true,
|
|
@@ -840,7 +846,16 @@ function applyBrowserTools(ctx, controller) {
|
|
|
840
846
|
},
|
|
841
847
|
},
|
|
842
848
|
render: (_args, value) => {
|
|
843
|
-
const
|
|
849
|
+
const shown = value.items.length;
|
|
850
|
+
const start = value.offset + 1;
|
|
851
|
+
const end = value.offset + shown;
|
|
852
|
+
const totalText = value.total === undefined ? `${value.returned ?? shown} available` : `${value.total} on the page`;
|
|
853
|
+
const range = shown === 0 ? `no items at offset ${value.offset}` : `items ${start}–${end} of ${totalText}`;
|
|
854
|
+
const capped = value.total !== undefined && value.returned !== undefined && value.total > value.returned
|
|
855
|
+
? ` (this extension returns at most ${value.returned}-item windows — use browser_find/browser_read to reach the rest)`
|
|
856
|
+
: '';
|
|
857
|
+
const next = value.nextOffset === undefined ? '' : `; next page: offset ${value.nextOffset}`;
|
|
858
|
+
const head = `${range}${capped}${next}`
|
|
844
859
|
+ ` (mode ${value.mode ?? 'dom'}); click or fill them by ref:`
|
|
845
860
|
+ (value.axFallback === undefined ? '' : `\n(mode ax fell back to dom: ${value.axFallback})`);
|
|
846
861
|
const lines = value.items.map(item => {
|
|
@@ -853,8 +868,12 @@ function applyBrowserTools(ctx, controller) {
|
|
|
853
868
|
},
|
|
854
869
|
presentCall: () => ({ card: 'generic', title: 'Snapshot browser page', kind: 'other' }),
|
|
855
870
|
async execute(args, exec) {
|
|
856
|
-
const
|
|
857
|
-
const
|
|
871
|
+
const offset = Math.max(0, Math.trunc(typeof args.offset === 'number' && Number.isFinite(args.offset) ? args.offset : 0));
|
|
872
|
+
const limit = Math.min(200, Math.max(1, typeof args.limit === 'number' && Number.isFinite(args.limit) ? Math.trunc(args.limit) : 120));
|
|
873
|
+
// Ask for the largest window the extension offers (200) and page in the
|
|
874
|
+
// plugin: older surfaces never learned an offset parameter, and they do
|
|
875
|
+
// report `total`, so paging stays plugin-side (v1.7.7).
|
|
876
|
+
const params = { limit: 200 };
|
|
858
877
|
if (typeof args.tabId === 'number')
|
|
859
878
|
params.tabId = args.tabId;
|
|
860
879
|
if (typeof args.mode === 'string')
|
|
@@ -872,13 +891,24 @@ function applyBrowserTools(ctx, controller) {
|
|
|
872
891
|
: (requestedAx && !gotAx
|
|
873
892
|
? 'this extension ignored mode:"ax" and returned CSS-DOM matches (AX mode needs DSH Browser Control extension v1.0.11 or newer) — the names below are tag names, not accessible names'
|
|
874
893
|
: undefined);
|
|
875
|
-
|
|
894
|
+
const window = Array.isArray(raw.items) ? raw.items : [];
|
|
895
|
+
const returned = window.length;
|
|
896
|
+
const reportedTotal = typeof raw.total === 'number' && Number.isFinite(raw.total) ? Math.trunc(raw.total) : undefined;
|
|
897
|
+
const total = reportedTotal === undefined ? undefined : Math.max(returned, reportedTotal);
|
|
898
|
+
const page = window.slice(offset, offset + limit);
|
|
899
|
+
const hasMore = offset + page.length < returned;
|
|
900
|
+
const receipt = {
|
|
876
901
|
tabId: raw.tabId,
|
|
877
902
|
title: raw.title ?? '',
|
|
878
903
|
url: raw.url ?? '',
|
|
879
904
|
...(raw.mode === undefined ? {} : { mode: raw.mode }),
|
|
880
905
|
...(axFallback === undefined ? {} : { axFallback }),
|
|
881
|
-
|
|
906
|
+
offset,
|
|
907
|
+
...(total === undefined ? {} : { total }),
|
|
908
|
+
returned,
|
|
909
|
+
hasMore,
|
|
910
|
+
...(hasMore ? { nextOffset: offset + page.length } : {}),
|
|
911
|
+
items: page.map(item => ({
|
|
882
912
|
ref: item.ref,
|
|
883
913
|
tag: item.tag,
|
|
884
914
|
...(item.role === undefined ? {} : { role: item.role }),
|
|
@@ -886,6 +916,7 @@ function applyBrowserTools(ctx, controller) {
|
|
|
886
916
|
...(item.href === undefined ? {} : { href: item.href }),
|
|
887
917
|
})),
|
|
888
918
|
};
|
|
919
|
+
return receipt;
|
|
889
920
|
},
|
|
890
921
|
}));
|
|
891
922
|
ctx.tools.register(defineTool({
|
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.7.
|
|
4
|
+
"version": "1.7.7",
|
|
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 && node verify/verify_windows.mjs && node verify/verify_history_nav.mjs && node verify/verify_metrics_throttle.mjs && node verify/verify_drag.mjs && node verify/verify_audit.mjs && node verify/verify_journal.mjs && node verify/verify_sessions.mjs && node verify/verify_compat_matrix.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 && node verify/verify_history_nav.mjs && node verify/verify_metrics_throttle.mjs && node verify/verify_drag.mjs && node verify/verify_audit.mjs && node verify/verify_journal.mjs && node verify/verify_sessions.mjs && node verify/verify_snapshot_page.mjs && node verify/verify_compat_matrix.mjs"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public",
|