dsh-builtin-browser 0.1.14 → 0.1.16
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 +64 -18
- package/README.md +64 -18
- package/docs/README.md +1 -1
- package/docs/architecture.md +7 -4
- package/docs/tool-reference.md +22 -9
- package/docs/user-guide.md +8 -6
- package/lib/browser/runtime.d.ts +42 -8
- package/lib/browser/runtime.js +58 -6
- package/lib/browser/types.d.ts +267 -8
- package/lib/browser-electron/entry.d.ts +15 -5
- package/lib/browser-electron/entry.js +13 -5
- package/lib/browser-electron/host-main.d.ts +13 -4
- package/lib/browser-electron/host-main.js +686 -93
- package/lib/browser-electron/provider.d.ts +192 -18
- package/lib/browser-electron/provider.js +1063 -61
- package/lib/browser-electron/remote-host.d.ts +32 -2
- package/lib/browser-electron/remote-host.js +218 -42
- package/lib/index.d.ts +6 -6
- package/lib/index.js +4 -4
- package/lib/tool-browser/index.d.ts +4 -4
- package/lib/tool-browser/index.js +706 -79
- package/package.json +7 -10
- package/src/browser/runtime.ts +111 -10
- package/src/browser/types.ts +278 -8
- package/src/browser-electron/entry.ts +81 -63
- package/src/browser-electron/host-main.ts +635 -81
- package/src/browser-electron/provider.ts +1217 -58
- package/src/browser-electron/remote-host.ts +228 -43
- package/src/index.ts +21 -6
- package/src/tool-browser/index.ts +1528 -885
- package/src/types/electron-shim.d.ts +13 -0
package/lib/browser/runtime.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Service } from '@deepseek-ai/cordis';
|
|
10
10
|
import z from '@deepseek-ai/schemastery';
|
|
11
|
-
import { BrowserError } from
|
|
12
|
-
export { BrowserError, } from
|
|
11
|
+
import { BrowserError } from './types.js';
|
|
12
|
+
export { BrowserError, } from './types.js';
|
|
13
13
|
/**
|
|
14
14
|
* The browser access service. Registered as `ctx.browser` (one instance per
|
|
15
15
|
* context).
|
|
@@ -79,8 +79,8 @@ export class BrowserRuntime extends Service {
|
|
|
79
79
|
return single;
|
|
80
80
|
}
|
|
81
81
|
/** Open a new browser session through the selected provider. */
|
|
82
|
-
async open() {
|
|
83
|
-
return this.resolveProvider().open();
|
|
82
|
+
async open(label) {
|
|
83
|
+
return this.resolveProvider().open(label);
|
|
84
84
|
}
|
|
85
85
|
/** Open a URL through the selected provider, optionally in a new tab. */
|
|
86
86
|
async openUrl(session, request, signal) {
|
|
@@ -110,26 +110,78 @@ export class BrowserRuntime extends Service {
|
|
|
110
110
|
async execute(session, request, signal) {
|
|
111
111
|
return this.resolveProvider().execute(session, request, signal);
|
|
112
112
|
}
|
|
113
|
+
/** Wait until the session's page is ready (and optional URL/selector match). */
|
|
114
|
+
async waitFor(session, request, signal) {
|
|
115
|
+
return this.resolveProvider().waitFor(session, request, signal);
|
|
116
|
+
}
|
|
113
117
|
/** Produce an AI-friendly snapshot of the session's page. */
|
|
114
118
|
async snapshot(session, signal) {
|
|
115
119
|
return this.resolveProvider().snapshot(session, signal);
|
|
116
120
|
}
|
|
121
|
+
/** Read the session's page accessibility tree through the selected provider. */
|
|
122
|
+
async a11y(session, request, signal) {
|
|
123
|
+
return this.resolveProvider().a11y(session, request, signal);
|
|
124
|
+
}
|
|
125
|
+
/** Reload the session's active tab through the selected provider. */
|
|
126
|
+
async reload(session, signal) {
|
|
127
|
+
return this.resolveProvider().reload(session, signal);
|
|
128
|
+
}
|
|
117
129
|
/** Fetch page content in a requested format. */
|
|
118
130
|
async content(session, request, signal) {
|
|
119
131
|
return this.resolveProvider().content(session, request, signal);
|
|
120
132
|
}
|
|
121
|
-
/** Click at viewport coordinates through the selected provider. */
|
|
133
|
+
/** Click at viewport coordinates (or a located element) through the selected provider. */
|
|
122
134
|
async click(session, request, signal) {
|
|
123
135
|
return this.resolveProvider().click(session, request, signal);
|
|
124
136
|
}
|
|
125
|
-
/** Type into the focused element through the selected provider. */
|
|
137
|
+
/** Type into the focused element (or a located one) through the selected provider. */
|
|
126
138
|
async type(session, request, signal) {
|
|
127
139
|
return this.resolveProvider().type(session, request, signal);
|
|
128
140
|
}
|
|
141
|
+
/** Scroll the session's page through the selected provider. */
|
|
142
|
+
async scroll(session, request, signal) {
|
|
143
|
+
return this.resolveProvider().scroll(session, request, signal);
|
|
144
|
+
}
|
|
145
|
+
/** Go back in the session's page history through the selected provider. */
|
|
146
|
+
async back(session, signal) {
|
|
147
|
+
return this.resolveProvider().back(session, signal);
|
|
148
|
+
}
|
|
149
|
+
/** Go forward in the session's page history through the selected provider. */
|
|
150
|
+
async forward(session, signal) {
|
|
151
|
+
return this.resolveProvider().forward(session, signal);
|
|
152
|
+
}
|
|
153
|
+
/** Press one named key through the selected provider. */
|
|
154
|
+
async key(session, request, signal) {
|
|
155
|
+
return this.resolveProvider().key(session, request, signal);
|
|
156
|
+
}
|
|
129
157
|
/** Fill a form's fields in one batch through the selected provider. */
|
|
130
158
|
async fillForm(session, request, signal) {
|
|
131
159
|
return this.resolveProvider().fillForm(session, request, signal);
|
|
132
160
|
}
|
|
161
|
+
/** Set one element's value through the selected provider. */
|
|
162
|
+
async setValue(session, request, signal) {
|
|
163
|
+
return this.resolveProvider().setValue(session, request, signal);
|
|
164
|
+
}
|
|
165
|
+
/** Check or uncheck one checkbox/radio through the selected provider. */
|
|
166
|
+
async check(session, request, signal) {
|
|
167
|
+
return this.resolveProvider().check(session, request, signal);
|
|
168
|
+
}
|
|
169
|
+
/** Select one option of a `<select>` through the selected provider. */
|
|
170
|
+
async selectOption(session, request, signal) {
|
|
171
|
+
return this.resolveProvider().selectOption(session, request, signal);
|
|
172
|
+
}
|
|
173
|
+
/** Clear one element through the selected provider. */
|
|
174
|
+
async clearField(session, request, signal) {
|
|
175
|
+
return this.resolveProvider().clearField(session, request, signal);
|
|
176
|
+
}
|
|
177
|
+
/** Read one element's value through the selected provider. */
|
|
178
|
+
async getValue(session, request, signal) {
|
|
179
|
+
return this.resolveProvider().getValue(session, request, signal);
|
|
180
|
+
}
|
|
181
|
+
/** Extract structured data through the selected provider. */
|
|
182
|
+
async scrape(session, request, signal) {
|
|
183
|
+
return this.resolveProvider().scrape(session, request, signal);
|
|
184
|
+
}
|
|
133
185
|
/** Capture the current page through the selected provider. */
|
|
134
186
|
async screenshot(session, request, signal) {
|
|
135
187
|
return this.resolveProvider().screenshot(session, request, signal);
|
package/lib/browser/types.d.ts
CHANGED
|
@@ -20,7 +20,11 @@ export type BrowserSessionId = string;
|
|
|
20
20
|
* minimal so a provider swap never changes how the model asks.
|
|
21
21
|
*/
|
|
22
22
|
export interface BrowserNavigateRequest {
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The URL to open. Admission (HTTP(S) only) is provider-owned; the seam
|
|
25
|
+
* itself does not police credentials or private/localhost targets — a
|
|
26
|
+
* shared real browser legitimately reaches local dev servers.
|
|
27
|
+
*/
|
|
24
28
|
readonly url: string;
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
@@ -39,6 +43,132 @@ export interface BrowserTypeRequest {
|
|
|
39
43
|
/** The text to insert. */
|
|
40
44
|
readonly text: string;
|
|
41
45
|
}
|
|
46
|
+
/** Scroll the page: by pixel deltas, to an element, or to top/bottom. */
|
|
47
|
+
export interface BrowserScrollRequest {
|
|
48
|
+
/** Horizontal delta in CSS pixels. */
|
|
49
|
+
readonly deltaX?: number;
|
|
50
|
+
/** Vertical delta in CSS pixels. */
|
|
51
|
+
readonly deltaY?: number;
|
|
52
|
+
/** Scroll the given selector's element into view (top document; same-origin iframes reachable via execute). */
|
|
53
|
+
readonly selector?: string;
|
|
54
|
+
/** Scroll to the top of the page. */
|
|
55
|
+
readonly toTop?: boolean;
|
|
56
|
+
/** Scroll to the bottom of the page. */
|
|
57
|
+
readonly toBottom?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Press one named key (Enter, Tab, arrows, …). */
|
|
60
|
+
export interface BrowserKeyRequest {
|
|
61
|
+
/** Key name from the supported set (see the browser_key tool). */
|
|
62
|
+
readonly key: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* How to find one element for a targeted interaction (form controls, clicks,
|
|
66
|
+
* typing). `text` matches an element's own visible text — exact first, then
|
|
67
|
+
* contains, deepest element preferred (so a button inside a card wins over
|
|
68
|
+
* the card itself).
|
|
69
|
+
*/
|
|
70
|
+
export interface BrowserElementTarget {
|
|
71
|
+
/** Locator kind. */
|
|
72
|
+
readonly by: 'css' | 'text' | 'xpath';
|
|
73
|
+
/** The CSS selector / visible text / XPath expression. */
|
|
74
|
+
readonly value: string;
|
|
75
|
+
/** 0-based index of the match to use. Default 0. */
|
|
76
|
+
readonly index?: number;
|
|
77
|
+
}
|
|
78
|
+
/** Set the value of one input/textarea/select/contenteditable. */
|
|
79
|
+
export interface BrowserSetValueRequest {
|
|
80
|
+
/** The element to set (css/text/xpath). */
|
|
81
|
+
readonly target: BrowserElementTarget;
|
|
82
|
+
/** The value to set (string, number, or boolean). */
|
|
83
|
+
readonly value: string | number | boolean;
|
|
84
|
+
/** Element-lookup budget in ms. Default 5000. */
|
|
85
|
+
readonly timeoutMs?: number;
|
|
86
|
+
}
|
|
87
|
+
/** Result of a single setValue. */
|
|
88
|
+
export interface BrowserSetValueResult {
|
|
89
|
+
/** How the value was applied: input | textarea | select | contenteditable. */
|
|
90
|
+
readonly method: string;
|
|
91
|
+
/** The value as applied. */
|
|
92
|
+
readonly value: string;
|
|
93
|
+
}
|
|
94
|
+
/** Check/uncheck one checkbox or radio. */
|
|
95
|
+
export interface BrowserCheckRequest {
|
|
96
|
+
/** The element to toggle (css/text/xpath). */
|
|
97
|
+
readonly target: BrowserElementTarget;
|
|
98
|
+
/** Desired state. Default true (check). */
|
|
99
|
+
readonly checked?: boolean;
|
|
100
|
+
/** Element-lookup budget in ms. Default 5000. */
|
|
101
|
+
readonly timeoutMs?: number;
|
|
102
|
+
}
|
|
103
|
+
/** Select one option of a `<select>`, by value, visible text, or index. */
|
|
104
|
+
export interface BrowserSelectRequest {
|
|
105
|
+
/** The `<select>` element (css/text/xpath). */
|
|
106
|
+
readonly target: BrowserElementTarget;
|
|
107
|
+
/** Match the option by its value attribute. */
|
|
108
|
+
readonly optionValue?: string;
|
|
109
|
+
/** Match the option by its visible text. */
|
|
110
|
+
readonly optionText?: string;
|
|
111
|
+
/** Match the option by its 0-based index. */
|
|
112
|
+
readonly optionIndex?: number;
|
|
113
|
+
/** Element-lookup budget in ms. Default 5000. */
|
|
114
|
+
readonly timeoutMs?: number;
|
|
115
|
+
}
|
|
116
|
+
/** Result of a single select. */
|
|
117
|
+
export interface BrowserSelectResult {
|
|
118
|
+
/** The selected option's value attribute. */
|
|
119
|
+
readonly value: string;
|
|
120
|
+
/** The selected option's visible text. */
|
|
121
|
+
readonly text: string;
|
|
122
|
+
}
|
|
123
|
+
/** Clear one input/textarea/contenteditable (or uncheck a checkbox/radio). */
|
|
124
|
+
export interface BrowserClearRequest {
|
|
125
|
+
/** The element to clear (css/text/xpath). */
|
|
126
|
+
readonly target: BrowserElementTarget;
|
|
127
|
+
/** Element-lookup budget in ms. Default 5000. */
|
|
128
|
+
readonly timeoutMs?: number;
|
|
129
|
+
}
|
|
130
|
+
/** Read one element's current value (for verification). */
|
|
131
|
+
export interface BrowserGetValueRequest {
|
|
132
|
+
/** The element to read (css/text/xpath). */
|
|
133
|
+
readonly target: BrowserElementTarget;
|
|
134
|
+
/** Element-lookup budget in ms. Default 5000. */
|
|
135
|
+
readonly timeoutMs?: number;
|
|
136
|
+
}
|
|
137
|
+
/** Result of a getValue. */
|
|
138
|
+
export interface BrowserGetValueResult {
|
|
139
|
+
/** The element's value; null when the element has none (e.g. a checkbox). */
|
|
140
|
+
readonly value: string | null;
|
|
141
|
+
/** For checkbox/radio: the checked state. */
|
|
142
|
+
readonly checked?: boolean;
|
|
143
|
+
/** For `<select>`: the selected option's visible text. */
|
|
144
|
+
readonly selectedText?: string;
|
|
145
|
+
}
|
|
146
|
+
/** One field of a scrape: a name plus a selector (optionally `selector@attr`). */
|
|
147
|
+
export interface BrowserScrapeField {
|
|
148
|
+
/** Output key. */
|
|
149
|
+
readonly name: string;
|
|
150
|
+
/**
|
|
151
|
+
* CSS selector relative to each item; append `@attr` to take an attribute
|
|
152
|
+
* instead of text (`a@href` returns an absolute URL for links).
|
|
153
|
+
*/
|
|
154
|
+
readonly selector: string;
|
|
155
|
+
}
|
|
156
|
+
/** Extract structured data from repeated DOM items (static, CSP-safe). */
|
|
157
|
+
export interface BrowserScrapeRequest {
|
|
158
|
+
/** Required: CSS selector of each result container (e.g. `div.card`). */
|
|
159
|
+
readonly item: string;
|
|
160
|
+
/** Field map: name -> selector[@attr]. */
|
|
161
|
+
readonly fields: readonly BrowserScrapeField[];
|
|
162
|
+
/** Wait budget for the item selector to appear. Default 5000. */
|
|
163
|
+
readonly timeoutMs?: number;
|
|
164
|
+
}
|
|
165
|
+
/** Structured extraction result. */
|
|
166
|
+
export interface BrowserScrapeResult {
|
|
167
|
+
/** Number of items extracted. */
|
|
168
|
+
readonly count: number;
|
|
169
|
+
/** One object per item; missing fields are null. */
|
|
170
|
+
readonly items: readonly Record<string, string | null>[];
|
|
171
|
+
}
|
|
42
172
|
/** One field of a batch form fill. Match by selector, or by name/label/placeholder. */
|
|
43
173
|
export interface BrowserFillField {
|
|
44
174
|
/** CSS selector; when present, candidates are scoped to it. */
|
|
@@ -81,6 +211,27 @@ export interface BrowserFillResult {
|
|
|
81
211
|
/** Whether the containing form was submitted. */
|
|
82
212
|
readonly submitted: boolean;
|
|
83
213
|
}
|
|
214
|
+
/** Wait request: poll until the page is ready (and optional URL/selector). */
|
|
215
|
+
export interface BrowserWaitRequest {
|
|
216
|
+
/** Maximum wait in ms. Default 30000. */
|
|
217
|
+
readonly timeoutMs?: number;
|
|
218
|
+
/**
|
|
219
|
+
* Optional URL the top document must show (exact or prefix match). Pass the
|
|
220
|
+
* URL you navigated to — this distinguishes the NEW document from the old
|
|
221
|
+
* one still on screen while navigation is in flight.
|
|
222
|
+
*/
|
|
223
|
+
readonly url?: string;
|
|
224
|
+
/** Optional CSS selector that must exist (top document or same-origin frames). */
|
|
225
|
+
readonly selector?: string;
|
|
226
|
+
/** Wait for the page load (readyState complete). Default true. */
|
|
227
|
+
readonly loaded?: boolean;
|
|
228
|
+
}
|
|
229
|
+
/** Outcome of a wait: whether the condition was met before the budget ran out. */
|
|
230
|
+
export interface BrowserWaitResult {
|
|
231
|
+
readonly ready: boolean;
|
|
232
|
+
/** Why the wait ended: 'condition met' or a short explanation of the miss. */
|
|
233
|
+
readonly reason: string;
|
|
234
|
+
}
|
|
84
235
|
/**
|
|
85
236
|
* A screenshot of the current page state. `dataUrl` is a base64 data URL the
|
|
86
237
|
* tool layer renders directly; the live view itself never crosses the wire
|
|
@@ -92,12 +243,24 @@ export interface BrowserScreenshotResult {
|
|
|
92
243
|
/** File path the capture was also saved to, when the request asked for it. */
|
|
93
244
|
readonly path?: string;
|
|
94
245
|
}
|
|
95
|
-
/** Screenshot capture options.
|
|
246
|
+
/** Screenshot capture options. */
|
|
96
247
|
export interface BrowserScreenshotRequest {
|
|
97
248
|
/** Capture the full scrollable page instead of the viewport. Default false. */
|
|
98
249
|
readonly fullPage?: boolean;
|
|
99
|
-
/** Absolute file path to also save the
|
|
250
|
+
/** Absolute file path to also save the image to (for vision-tool reads). */
|
|
100
251
|
readonly savePath?: string;
|
|
252
|
+
/**
|
|
253
|
+
* Image format. Default 'png'. JPEG is only available on the self-hosted
|
|
254
|
+
* native capture path; the desktop-shell CDP fallback always returns PNG
|
|
255
|
+
* (CDP JPEG hangs on Electron 43 — a documented platform defect).
|
|
256
|
+
*/
|
|
257
|
+
readonly format?: 'png' | 'jpeg';
|
|
258
|
+
/** JPEG quality 1-100 (default 80); ignored for PNG. */
|
|
259
|
+
readonly quality?: number;
|
|
260
|
+
/** Downscale to fit within this width (aspect preserved). */
|
|
261
|
+
readonly maxWidth?: number;
|
|
262
|
+
/** Downscale to fit within this height (aspect preserved). */
|
|
263
|
+
readonly maxHeight?: number;
|
|
101
264
|
}
|
|
102
265
|
/** Download options: fetch a URL into a local file, keeping the session's cookies. */
|
|
103
266
|
export interface BrowserDownloadRequest {
|
|
@@ -159,6 +322,12 @@ export interface BrowserSnapshotElement {
|
|
|
159
322
|
/** Viewport-relative center, for coordinate fallbacks. */
|
|
160
323
|
readonly x: number;
|
|
161
324
|
readonly y: number;
|
|
325
|
+
/**
|
|
326
|
+
* True when the element lives inside an (same-origin) iframe: coordinates
|
|
327
|
+
* are still top-document, but DOM selectors are frame-scoped — reach the
|
|
328
|
+
* element via `iframe.contentDocument` in browser_execute.
|
|
329
|
+
*/
|
|
330
|
+
readonly frame?: boolean;
|
|
162
331
|
}
|
|
163
332
|
/**
|
|
164
333
|
* AI-friendly page snapshot: a compact, numbered inventory of interactive
|
|
@@ -176,6 +345,53 @@ export interface BrowserSnapshotResult {
|
|
|
176
345
|
/** Human-verification challenge blocking the page, when one is detected. */
|
|
177
346
|
readonly challenge?: BrowserChallenge;
|
|
178
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* One node of the accessibility tree, with a stable reference number the
|
|
350
|
+
* model can cite. Roles/names prefer Chrome's `computedRole`/`computedName`
|
|
351
|
+
* with tag/attribute inference as fallback; coordinates are top-document
|
|
352
|
+
* viewport-relative so the node can also be driven by click/type.
|
|
353
|
+
*/
|
|
354
|
+
export interface BrowserA11yNode {
|
|
355
|
+
/** 1-based reference number, stable for the lifetime of one a11y call. */
|
|
356
|
+
readonly ref: number;
|
|
357
|
+
/** ARIA role: button | link | textbox | checkbox | radio | combobox | … */
|
|
358
|
+
readonly role: string;
|
|
359
|
+
/** Accessible name (aria-label, label text, placeholder, own text, …). */
|
|
360
|
+
readonly name: string;
|
|
361
|
+
/** Current value of input-like nodes (null when not applicable). */
|
|
362
|
+
readonly value: string | null;
|
|
363
|
+
/** ARIA/DOM states: enabled/disabled/checked/unchecked/expanded/… */
|
|
364
|
+
readonly states: readonly string[];
|
|
365
|
+
/** DOM depth (within its document), for reconstructing the tree. */
|
|
366
|
+
readonly depth: number;
|
|
367
|
+
/** Element tag name (lowercase). */
|
|
368
|
+
readonly tag: string;
|
|
369
|
+
/** Viewport-relative center, for coordinate fallbacks. */
|
|
370
|
+
readonly x: number;
|
|
371
|
+
readonly y: number;
|
|
372
|
+
/** True when the node lives inside a same-origin iframe. */
|
|
373
|
+
readonly frame?: boolean;
|
|
374
|
+
}
|
|
375
|
+
/** A11y tree request: what to include and how many nodes to return. */
|
|
376
|
+
export interface BrowserA11yRequest {
|
|
377
|
+
/** Include hidden elements (display:none, hidden, aria-hidden). Default false. */
|
|
378
|
+
readonly includeHidden?: boolean;
|
|
379
|
+
/** Maximum nodes returned. Default 500; clamped to 10-5000. */
|
|
380
|
+
readonly maxNodes?: number;
|
|
381
|
+
}
|
|
382
|
+
/** The page's accessibility tree, flattened into a numbered node list. */
|
|
383
|
+
export interface BrowserA11yResult {
|
|
384
|
+
/** Final page URL. */
|
|
385
|
+
readonly url: string;
|
|
386
|
+
/** Page title, if any. */
|
|
387
|
+
readonly title?: string;
|
|
388
|
+
/** Number of nodes returned. */
|
|
389
|
+
readonly count: number;
|
|
390
|
+
/** Flattened a11y nodes (depth restores the tree). */
|
|
391
|
+
readonly nodes: readonly BrowserA11yNode[];
|
|
392
|
+
/** True when the result was truncated (maxNodes reached). */
|
|
393
|
+
readonly truncated: boolean;
|
|
394
|
+
}
|
|
179
395
|
/**
|
|
180
396
|
* A detected human-verification (bot-detection / CAPTCHA) challenge blocking
|
|
181
397
|
* the page. Detection is best-effort and marker-based; a clean result is not a
|
|
@@ -245,8 +461,11 @@ export interface BrowserProvider {
|
|
|
245
461
|
* Open a new session. The provider mints the session id and prepares its
|
|
246
462
|
* backing surface (a view, a headless page, and so on). Sessions are isolated from
|
|
247
463
|
* each other; a session must not be visible to any other session's caller.
|
|
464
|
+
* @param label - optional human-readable label (e.g. the calling DSH task
|
|
465
|
+
* id) the provider may surface on the visible surface (window title) so a
|
|
466
|
+
* human can tell which task's page is currently shown.
|
|
248
467
|
*/
|
|
249
|
-
open(): Promise<BrowserSessionId>;
|
|
468
|
+
open(label?: string): Promise<BrowserSessionId>;
|
|
250
469
|
/** Open a URL, optionally in a new tab. Honor `signal` for cancellation. */
|
|
251
470
|
openUrl(session: BrowserSessionId, request: BrowserOpenRequest, signal?: AbortSignal): Promise<void>;
|
|
252
471
|
/** List the session's tabs. */
|
|
@@ -261,16 +480,56 @@ export interface BrowserProvider {
|
|
|
261
480
|
navigate(session: BrowserSessionId, request: BrowserNavigateRequest, signal?: AbortSignal): Promise<void>;
|
|
262
481
|
/** Execute JS in the active tab's page context. */
|
|
263
482
|
execute(session: BrowserSessionId, request: BrowserExecuteRequest, signal?: AbortSignal): Promise<BrowserExecuteResult>;
|
|
483
|
+
/** Wait until the page is ready (and optional URL/selector match). Honor `signal` for cancellation. */
|
|
484
|
+
waitFor(session: BrowserSessionId, request: BrowserWaitRequest, signal?: AbortSignal): Promise<BrowserWaitResult>;
|
|
264
485
|
/** Produce an AI-friendly snapshot of the active tab. */
|
|
265
486
|
snapshot(session: BrowserSessionId, signal?: AbortSignal): Promise<BrowserSnapshotResult>;
|
|
487
|
+
/** Read the active tab's accessibility tree (semantic, interactive nodes). */
|
|
488
|
+
a11y(session: BrowserSessionId, request: BrowserA11yRequest, signal?: AbortSignal): Promise<BrowserA11yResult>;
|
|
489
|
+
/** Reload the active tab. Honor `signal` for cancellation. */
|
|
490
|
+
reload(session: BrowserSessionId, signal?: AbortSignal): Promise<void>;
|
|
266
491
|
/** Check whether a human-verification challenge is blocking the active tab. */
|
|
267
492
|
detectChallenge(session: BrowserSessionId, signal?: AbortSignal): Promise<BrowserChallenge>;
|
|
268
493
|
/** Fetch page content in a requested format. */
|
|
269
494
|
content(session: BrowserSessionId, request: BrowserContentRequest, signal?: AbortSignal): Promise<BrowserContentResult>;
|
|
270
|
-
/**
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
495
|
+
/**
|
|
496
|
+
* Click at viewport coordinates, or at a located element's center when the
|
|
497
|
+
* request carries a `target`. Honor `signal` for cancellation.
|
|
498
|
+
*/
|
|
499
|
+
click(session: BrowserSessionId, request: BrowserClickRequest | {
|
|
500
|
+
readonly target: BrowserElementTarget;
|
|
501
|
+
}, signal?: AbortSignal): Promise<void>;
|
|
502
|
+
/**
|
|
503
|
+
* Type into the focused element, or focus a located element first when the
|
|
504
|
+
* request carries a `target`. Honor `signal` for cancellation.
|
|
505
|
+
*/
|
|
506
|
+
type(session: BrowserSessionId, request: BrowserTypeRequest | {
|
|
507
|
+
readonly target: BrowserElementTarget;
|
|
508
|
+
}, signal?: AbortSignal): Promise<void>;
|
|
509
|
+
/** Set one element's value (native setter + input/change events). */
|
|
510
|
+
setValue(session: BrowserSessionId, request: BrowserSetValueRequest, signal?: AbortSignal): Promise<BrowserSetValueResult>;
|
|
511
|
+
/** Check or uncheck one checkbox/radio. */
|
|
512
|
+
check(session: BrowserSessionId, request: BrowserCheckRequest, signal?: AbortSignal): Promise<{
|
|
513
|
+
readonly checked: boolean;
|
|
514
|
+
}>;
|
|
515
|
+
/** Select one option of a `<select>`. */
|
|
516
|
+
selectOption(session: BrowserSessionId, request: BrowserSelectRequest, signal?: AbortSignal): Promise<BrowserSelectResult>;
|
|
517
|
+
/** Clear one input/textarea/contenteditable (or uncheck). */
|
|
518
|
+
clearField(session: BrowserSessionId, request: BrowserClearRequest, signal?: AbortSignal): Promise<{
|
|
519
|
+
readonly cleared: boolean;
|
|
520
|
+
}>;
|
|
521
|
+
/** Read one element's current value (for verification). */
|
|
522
|
+
getValue(session: BrowserSessionId, request: BrowserGetValueRequest, signal?: AbortSignal): Promise<BrowserGetValueResult>;
|
|
523
|
+
/** Extract structured data from repeated DOM items (static CSS, CSP-safe). */
|
|
524
|
+
scrape(session: BrowserSessionId, request: BrowserScrapeRequest, signal?: AbortSignal): Promise<BrowserScrapeResult>;
|
|
525
|
+
/** Scroll the active tab. Honor `signal` for cancellation. */
|
|
526
|
+
scroll(session: BrowserSessionId, request: BrowserScrollRequest, signal?: AbortSignal): Promise<void>;
|
|
527
|
+
/** Go back in the active tab's history. Honor `signal` for cancellation. */
|
|
528
|
+
back(session: BrowserSessionId, signal?: AbortSignal): Promise<void>;
|
|
529
|
+
/** Go forward in the active tab's history. Honor `signal` for cancellation. */
|
|
530
|
+
forward(session: BrowserSessionId, signal?: AbortSignal): Promise<void>;
|
|
531
|
+
/** Press one named key (Enter/Tab/arrows/…). Honor `signal` for cancellation. */
|
|
532
|
+
key(session: BrowserSessionId, request: BrowserKeyRequest, signal?: AbortSignal): Promise<void>;
|
|
274
533
|
/** Fill a form's fields in one batch. Honor `signal` for cancellation. */
|
|
275
534
|
fillForm(session: BrowserSessionId, request: BrowserFillRequest, signal?: AbortSignal): Promise<BrowserFillResult>;
|
|
276
535
|
/** Capture the current page. Honor `signal` for cancellation. */
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Context } from '@deepseek-ai/cordis';
|
|
13
13
|
import z from '@deepseek-ai/schemastery';
|
|
14
|
-
import type { BrowserRuntime } from '../browser/runtime.
|
|
15
|
-
import type { ElectronBrowserViewHost } from './provider.
|
|
16
|
-
export { ELECTRON_BROWSER_PROVIDER_ID, ElectronBrowserProvider, } from './provider.
|
|
17
|
-
export type { ElectronBrowserViewHost, ElectronViewHandle } from './provider.
|
|
18
|
-
export { RemoteElectronViewHost, defaultHostMainPath } from './remote-host.
|
|
14
|
+
import type { BrowserRuntime } from '../browser/runtime.js';
|
|
15
|
+
import type { ElectronBrowserViewHost } from './provider.js';
|
|
16
|
+
export { ELECTRON_BROWSER_PROVIDER_ID, ElectronBrowserProvider, } from './provider.js';
|
|
17
|
+
export type { ElectronBrowserViewHost, ElectronViewHandle } from './provider.js';
|
|
18
|
+
export { RemoteElectronViewHost, defaultHostMainPath } from './remote-host.js';
|
|
19
19
|
/** Cordis plugin name used by loader diagnostics. */
|
|
20
20
|
export declare const name = "browser-electron";
|
|
21
21
|
/** The browser seam this provider registers into. */
|
|
@@ -26,6 +26,16 @@ export interface Config {
|
|
|
26
26
|
readonly viewHost?: ElectronBrowserViewHost;
|
|
27
27
|
/** Allow navigation only to HTTP(S) URLs. Default true. */
|
|
28
28
|
readonly httpOnly?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Directory `browser_download` save paths must resolve inside (prevents a
|
|
31
|
+
* prompt-injected agent from writing arbitrary machine paths). Default:
|
|
32
|
+
* the user's Downloads folder; override to confine downloads elsewhere.
|
|
33
|
+
*/
|
|
34
|
+
readonly downloadDir?: string;
|
|
35
|
+
/** Maximum snapshot elements before truncation. Default 60. */
|
|
36
|
+
readonly snapshotMaxElements?: number;
|
|
37
|
+
/** Maximum content characters before truncation when no maxChars is given. Default 100_000. */
|
|
38
|
+
readonly contentMaxChars?: number;
|
|
29
39
|
}
|
|
30
40
|
export declare const Config: z<Config>;
|
|
31
41
|
/** Register the Electron browser provider with `ctx.browser`. */
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* @module dsh-browser/browser-electron
|
|
11
11
|
*/
|
|
12
12
|
import z from '@deepseek-ai/schemastery';
|
|
13
|
-
import { ElectronBrowserProvider } from
|
|
14
|
-
import { defaultHostMainPath, RemoteElectronViewHost } from
|
|
15
|
-
export { ELECTRON_BROWSER_PROVIDER_ID, ElectronBrowserProvider, } from
|
|
16
|
-
export { RemoteElectronViewHost, defaultHostMainPath } from
|
|
13
|
+
import { ElectronBrowserProvider } from './provider.js';
|
|
14
|
+
import { defaultHostMainPath, RemoteElectronViewHost } from './remote-host.js';
|
|
15
|
+
export { ELECTRON_BROWSER_PROVIDER_ID, ElectronBrowserProvider, } from './provider.js';
|
|
16
|
+
export { RemoteElectronViewHost, defaultHostMainPath } from './remote-host.js';
|
|
17
17
|
/** Cordis plugin name used by loader diagnostics. */
|
|
18
18
|
export const name = 'browser-electron';
|
|
19
19
|
/** The browser seam this provider registers into. */
|
|
@@ -22,6 +22,9 @@ export const Config = z.object({
|
|
|
22
22
|
// Absent on surfaces without a desktop shell; the plugin self-hosts then.
|
|
23
23
|
viewHost: z.any(),
|
|
24
24
|
httpOnly: z.boolean().default(true),
|
|
25
|
+
downloadDir: z.string(),
|
|
26
|
+
snapshotMaxElements: z.number(),
|
|
27
|
+
contentMaxChars: z.number(),
|
|
25
28
|
});
|
|
26
29
|
/** Register the Electron browser provider with `ctx.browser`. */
|
|
27
30
|
export function apply(ctx, config) {
|
|
@@ -32,7 +35,12 @@ export function apply(ctx, config) {
|
|
|
32
35
|
// is bound to the seam's own fiber (the browser row), so a reload of this
|
|
33
36
|
// row would otherwise collide with the still-registered provider
|
|
34
37
|
// (BROWSER_DUPLICATE_PROVIDER) or leave a stale provider behind.
|
|
35
|
-
const unregister = ctx.browser.registerBrowserProvider(new ElectronBrowserProvider(host, {
|
|
38
|
+
const unregister = ctx.browser.registerBrowserProvider(new ElectronBrowserProvider(host, {
|
|
39
|
+
httpOnly: config.httpOnly,
|
|
40
|
+
downloadDir: config.downloadDir,
|
|
41
|
+
snapshotMaxElements: config.snapshotMaxElements,
|
|
42
|
+
contentMaxChars: config.contentMaxChars,
|
|
43
|
+
}));
|
|
36
44
|
ctx.effect(() => () => {
|
|
37
45
|
unregister();
|
|
38
46
|
if (config.viewHost === undefined && host instanceof RemoteElectronViewHost) {
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Self-hosted Electron browser host (child side): the Electron main process
|
|
3
|
-
* spawned by {@link RemoteElectronViewHost}. Owns one `BrowserWindow`
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* spawned by {@link RemoteElectronViewHost}. Owns one `BrowserWindow` PER
|
|
4
|
+
* SESSION (each session's views live in their own window) plus a browser
|
|
5
|
+
* toolbar (address bar, back/forward/reload, tab strip) that makes the
|
|
6
|
+
* built-in browser usable as a real browser by the human — every window has
|
|
7
|
+
* its own toolbar. Views are `WebContentsView`s driven over
|
|
8
|
+
* `webContents.debugger` (CDP); user toolbar actions are routed back to the
|
|
9
|
+
* parent so the agent and the human always share one tab/navigation model.
|
|
6
10
|
*
|
|
7
11
|
* Protocol (one JSON object per line, both directions):
|
|
8
12
|
* <- { id, op: 'ping' } | { id, op: 'createView', viewId } |
|
|
9
13
|
* { id, op: 'destroyView', viewId } | { id, op: 'showView', viewId } |
|
|
10
|
-
* { id, op: '
|
|
14
|
+
* { id, op: 'groupView', viewId, windowId, label? } |
|
|
15
|
+
* { id, op: 'command', viewId, method, params } |
|
|
16
|
+
* { id, op: 'userActionError', windowId, message }
|
|
17
|
+
* -> { id: 0, op: 'hello', token } (our FIRST message — proves we know the
|
|
18
|
+
* parent's stdin token; the parent refuses the connection otherwise)
|
|
11
19
|
* -> { id, ok: true, result? } | { id, ok: false, err }
|
|
20
|
+
* -> { id: 0, op: 'userAction', action } (fire-and-forget; no reply)
|
|
12
21
|
*
|
|
13
22
|
* The parent never parses stderr, so diagnostics may go there freely.
|
|
14
23
|
* @module dsh-browser/browser-electron/host-main
|