@moxxy/plugin-browser 0.27.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/dist/browser-session.d.ts +43 -0
  3. package/dist/browser-session.d.ts.map +1 -0
  4. package/dist/browser-session.js +500 -0
  5. package/dist/browser-session.js.map +1 -0
  6. package/dist/browser-surface.d.ts +3 -0
  7. package/dist/browser-surface.d.ts.map +1 -0
  8. package/dist/browser-surface.js +255 -0
  9. package/dist/browser-surface.js.map +1 -0
  10. package/dist/html-extract.d.ts +20 -0
  11. package/dist/html-extract.d.ts.map +1 -0
  12. package/dist/html-extract.js +122 -0
  13. package/dist/html-extract.js.map +1 -0
  14. package/dist/index.d.ts +10 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +24 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/sidecar/dispatch.d.ts +18 -0
  19. package/dist/sidecar/dispatch.d.ts.map +1 -0
  20. package/dist/sidecar/dispatch.js +294 -0
  21. package/dist/sidecar/dispatch.js.map +1 -0
  22. package/dist/sidecar/install.d.ts +37 -0
  23. package/dist/sidecar/install.d.ts.map +1 -0
  24. package/dist/sidecar/install.js +242 -0
  25. package/dist/sidecar/install.js.map +1 -0
  26. package/dist/sidecar/types.d.ts +97 -0
  27. package/dist/sidecar/types.d.ts.map +1 -0
  28. package/dist/sidecar/types.js +20 -0
  29. package/dist/sidecar/types.js.map +1 -0
  30. package/dist/sidecar.d.ts +31 -0
  31. package/dist/sidecar.d.ts.map +1 -0
  32. package/dist/sidecar.js +165 -0
  33. package/dist/sidecar.js.map +1 -0
  34. package/dist/ssrf-guard.d.ts +43 -0
  35. package/dist/ssrf-guard.d.ts.map +1 -0
  36. package/dist/ssrf-guard.js +164 -0
  37. package/dist/ssrf-guard.js.map +1 -0
  38. package/dist/web-fetch.d.ts +23 -0
  39. package/dist/web-fetch.d.ts.map +1 -0
  40. package/dist/web-fetch.js +253 -0
  41. package/dist/web-fetch.js.map +1 -0
  42. package/package.json +74 -0
  43. package/src/browser-session.test.ts +333 -0
  44. package/src/browser-session.ts +567 -0
  45. package/src/browser-surface.test.ts +367 -0
  46. package/src/browser-surface.ts +275 -0
  47. package/src/html-extract.ts +152 -0
  48. package/src/index.ts +35 -0
  49. package/src/sidecar/dispatch.test.ts +313 -0
  50. package/src/sidecar/dispatch.ts +314 -0
  51. package/src/sidecar/install.ts +283 -0
  52. package/src/sidecar/types.test.ts +26 -0
  53. package/src/sidecar/types.ts +114 -0
  54. package/src/sidecar.test.ts +57 -0
  55. package/src/sidecar.ts +167 -0
  56. package/src/ssrf-guard.test.ts +109 -0
  57. package/src/ssrf-guard.ts +165 -0
  58. package/src/web-fetch.test.ts +305 -0
  59. package/src/web-fetch.ts +311 -0
@@ -0,0 +1,255 @@
1
+ import { defineSurface } from '@moxxy/sdk';
2
+ import { browserSidecarCall, closeBrowserSidecar, resolveBrowserInstallRoot, sidecarErrorKind, } from './browser-session.js';
3
+ import { installPlaywrightPackage } from './sidecar/install.js';
4
+ /**
5
+ * The `browser` surface: a live, in-window view of the SAME Playwright page the
6
+ * `browser_session` tool drives. We "stream" the page by polling a JPEG frame
7
+ * (`frame` sidecar method) a few times a second and forwarding it as a
8
+ * `surface.data` payload; the user's clicks/keys/scroll/navigate are proxied
9
+ * back onto the page via the sidecar's coordinate-based input methods. So the
10
+ * agent and the user operate ONE shared page — agent navigations show up in the
11
+ * pane, and the user can take over.
12
+ *
13
+ * Why polling and not a CDP `Page.startScreencast` push: the screencast only
14
+ * emits on visual change, so a freshly-opened (blank / static / headless) page
15
+ * produces no frames at all — the pane sat on "Loading…" forever with the
16
+ * underlying error swallowed. A screenshot poll always yields a frame (even a
17
+ * blank one), so the view comes up reliably and a real launch/install failure
18
+ * surfaces as a status line instead of an indefinite spinner. Polling rides the
19
+ * existing sidecar RPC surface and works on every Playwright browser, not just
20
+ * Chromium.
21
+ */
22
+ const FRAME_INTERVAL_MS = 300;
23
+ /** Consecutive `frame` failures (with no frame ever seen) before we stop
24
+ * assuming "still launching" and show the error. ~6 × 300ms ≈ 1.8s grace. */
25
+ const FAIL_GRACE = 6;
26
+ /** Validate a sidecar `frame` reply before emitting it: a partial / altered
27
+ * reply must be treated as a failed tick (so FAIL_GRACE eventually surfaces a
28
+ * status) rather than streamed to subscribers as `{base64: undefined, …}`. */
29
+ function isFrame(v) {
30
+ if (!v || typeof v !== 'object')
31
+ return false;
32
+ const f = v;
33
+ return typeof f.base64 === 'string' && typeof f.mediaType === 'string' && typeof f.url === 'string';
34
+ }
35
+ export function buildBrowserSurface(deps) {
36
+ return defineSurface({
37
+ kind: 'browser',
38
+ description: "A live view of the agent's browser; click, type, and navigate.",
39
+ open: () => {
40
+ const dataSubs = new Set();
41
+ let last = null;
42
+ let timer = null;
43
+ let inFlight = false;
44
+ let fails = 0;
45
+ // Set once we detect the `playwright` npm package isn't installed. Polling
46
+ // pauses (no point retrying an import that will keep failing) and the pane
47
+ // shows an Install affordance; an `install` input clears it.
48
+ let needsInstall = false;
49
+ let installing = false;
50
+ const emit = (payload) => {
51
+ for (const cb of dataSubs)
52
+ cb(payload);
53
+ };
54
+ const stopPolling = () => {
55
+ if (timer)
56
+ clearInterval(timer);
57
+ timer = null;
58
+ };
59
+ const startPolling = () => {
60
+ if (timer)
61
+ return;
62
+ void tick();
63
+ timer = setInterval(() => void tick(), FRAME_INTERVAL_MS);
64
+ };
65
+ const tick = async () => {
66
+ if (inFlight || needsInstall || installing)
67
+ return; // don't pile up / retry a known-missing dep
68
+ inFlight = true;
69
+ try {
70
+ const reply = await browserSidecarCall('frame', {}, deps);
71
+ if (!isFrame(reply))
72
+ throw new Error('malformed frame reply from sidecar');
73
+ last = reply;
74
+ fails = 0;
75
+ emit({ type: 'frame', base64: reply.base64, mime: reply.mediaType, url: reply.url });
76
+ }
77
+ catch (err) {
78
+ // The `playwright` npm package is simply absent — recoverable. Pause
79
+ // polling and ask the user (the download is ~200MB) rather than spin on
80
+ // a failing import or dump a raw "not installed" error.
81
+ if (sidecarErrorKind(err) === 'needs-install') {
82
+ needsInstall = true;
83
+ stopPolling();
84
+ emit({
85
+ type: 'status',
86
+ needsInstall: true,
87
+ text: 'The browser engine (Playwright) is not installed. It is a one-time ~200MB download.',
88
+ });
89
+ return;
90
+ }
91
+ // The first failures are usually the browser still launching (or a
92
+ // one-time binary install). Only surface a hard error once it's
93
+ // clearly not transient, so the user isn't left on a silent spinner.
94
+ // Fire EXACTLY on the FAIL_GRACE-th consecutive failure (a successful
95
+ // frame resets `fails` to 0), so the status is emitted once per
96
+ // failure streak rather than every tick. We surface it whether or not
97
+ // a prior frame exists: with no frame yet the page never came up;
98
+ // with a prior frame the page later died and the pane would otherwise
99
+ // sit frozen on a stale screenshot with no hint the live view is dead.
100
+ if (++fails === FAIL_GRACE) {
101
+ const message = err instanceof Error ? err.message : String(err);
102
+ emit({
103
+ type: 'status',
104
+ text: last ? `Browser disconnected: ${message}` : `Browser unavailable: ${message}`,
105
+ });
106
+ }
107
+ }
108
+ finally {
109
+ inFlight = false;
110
+ }
111
+ };
112
+ // After an interaction, grab a frame now and once more shortly after, so a
113
+ // click/keypress that kicks off async work (a navigation, a menu opening,
114
+ // an animation) shows up promptly instead of waiting for the next poll.
115
+ const bump = () => {
116
+ void tick();
117
+ setTimeout(() => void tick(), 140);
118
+ };
119
+ // Kick an immediate frame (launches the browser), then poll.
120
+ startPolling();
121
+ const runInstall = async () => {
122
+ if (installing)
123
+ return;
124
+ installing = true;
125
+ stopPolling();
126
+ emit({ type: 'status', text: 'Installing browser engine… (one-time, ~200MB)' });
127
+ try {
128
+ await installPlaywrightPackage({
129
+ rootDir: resolveBrowserInstallRoot(deps),
130
+ onProgress: (line) => emit({ type: 'status', text: line }),
131
+ });
132
+ // The sidecar cached its failed `import('playwright')`; drop it so the
133
+ // next frame call respawns a fresh sidecar that imports the now-present
134
+ // package.
135
+ await closeBrowserSidecar();
136
+ needsInstall = false;
137
+ fails = 0;
138
+ emit({ type: 'status', text: 'Installed. Starting browser…' });
139
+ startPolling();
140
+ }
141
+ catch (err) {
142
+ const message = err instanceof Error ? err.message : String(err);
143
+ emit({ type: 'status', needsInstall: true, text: `Install failed: ${message}` });
144
+ }
145
+ finally {
146
+ installing = false;
147
+ }
148
+ };
149
+ return {
150
+ id: 'browser',
151
+ kind: 'browser',
152
+ onData: (cb) => {
153
+ dataSubs.add(cb);
154
+ return () => dataSubs.delete(cb);
155
+ },
156
+ snapshot: () => needsInstall
157
+ ? {
158
+ type: 'status',
159
+ needsInstall: true,
160
+ text: 'The browser engine (Playwright) is not installed. It is a one-time ~200MB download.',
161
+ }
162
+ : last
163
+ ? { type: 'frame', base64: last.base64, mime: last.mediaType, url: last.url }
164
+ : { type: 'status', text: 'Starting browser…' },
165
+ resize: async (size) => {
166
+ // Match the page viewport to the pane so the live view fills the
167
+ // container (no letterboxing) and click coords map 1:1.
168
+ if (!size.width || !size.height)
169
+ return;
170
+ await browserSidecarCall('setviewport', { width: size.width, height: size.height }, deps).catch(() => undefined);
171
+ void tick();
172
+ },
173
+ input: async (msg) => {
174
+ if (msg.type === 'install') {
175
+ await runInstall();
176
+ return;
177
+ }
178
+ const vw = last?.width ?? 1280;
179
+ const vh = last?.height ?? 720;
180
+ if (msg.type === 'navigate' && typeof msg.url === 'string') {
181
+ // The sidecar's goto re-runs the SSRF guard (loopback/private/
182
+ // metadata blocked), so a hostile URL never navigates. Unlike the
183
+ // pointer/key proxies below, a navigate rejection is user-meaningful
184
+ // (bad URL format, SSRF block) — surface it as a status line instead
185
+ // of swallowing it, so the user knows why the address bar did nothing.
186
+ try {
187
+ await browserSidecarCall('goto', { url: msg.url }, deps);
188
+ }
189
+ catch (err) {
190
+ const text = err instanceof Error ? err.message : String(err);
191
+ emit({ type: 'status', text });
192
+ }
193
+ bump();
194
+ }
195
+ else if ((msg.type === 'click' || msg.type === 'dblclick') &&
196
+ typeof msg.fx === 'number' &&
197
+ typeof msg.fy === 'number') {
198
+ const count = msg.type === 'dblclick' ? 2 : 1;
199
+ await browserSidecarCall('mouse', { x: msg.fx * vw, y: msg.fy * vh, count }, deps).catch(() => undefined);
200
+ bump();
201
+ }
202
+ else if (msg.type === 'move' && typeof msg.fx === 'number' && typeof msg.fy === 'number') {
203
+ // Hover — drive the pointer so :hover styles/tooltips render. A single
204
+ // follow-up frame (not a bump) keeps the cost down at move frequency.
205
+ await browserSidecarCall('mousemove', { x: msg.fx * vw, y: msg.fy * vh }, deps).catch(() => undefined);
206
+ void tick();
207
+ }
208
+ else if (msg.type === 'capture' &&
209
+ typeof msg.fx === 'number' &&
210
+ typeof msg.fy === 'number' &&
211
+ typeof msg.fw === 'number' &&
212
+ typeof msg.fh === 'number') {
213
+ // Sharp PNG of the dragged region → handed back to the pane, which
214
+ // attaches it to the chat composer (the user then describes the change).
215
+ const shot = (await browserSidecarCall('capture', { x: msg.fx * vw, y: msg.fy * vh, width: msg.fw * vw, height: msg.fh * vh }, deps).catch(() => null));
216
+ // Validate before emitting so a partial/altered reply doesn't hand
217
+ // the composer a {base64: undefined} attachment.
218
+ if (shot &&
219
+ typeof shot === 'object' &&
220
+ typeof shot.base64 === 'string' &&
221
+ typeof shot.mediaType === 'string') {
222
+ const s = shot;
223
+ emit({ type: 'captured', base64: s.base64, mediaType: s.mediaType });
224
+ }
225
+ }
226
+ else if (msg.type === 'zoom' && typeof msg.factor === 'number') {
227
+ await browserSidecarCall('zoom', { factor: msg.factor }, deps).catch(() => undefined);
228
+ bump();
229
+ }
230
+ else if (msg.type === 'key' && typeof msg.key === 'string') {
231
+ await browserSidecarCall('key', { key: msg.key }, deps).catch(() => undefined);
232
+ bump();
233
+ }
234
+ else if (msg.type === 'scroll' && typeof msg.dy === 'number') {
235
+ await browserSidecarCall('scroll', { dy: msg.dy }, deps).catch(() => undefined);
236
+ bump();
237
+ }
238
+ else if (msg.type === 'back' || msg.type === 'forward' || msg.type === 'reload') {
239
+ await browserSidecarCall(msg.type, {}, deps).catch(() => undefined);
240
+ bump();
241
+ }
242
+ },
243
+ close: () => {
244
+ if (timer)
245
+ clearInterval(timer);
246
+ timer = null;
247
+ dataSubs.clear();
248
+ // The underlying page stays alive for the agent's browser_session tool;
249
+ // it's torn down on session shutdown (closeBrowserSidecar).
250
+ },
251
+ };
252
+ },
253
+ });
254
+ }
255
+ //# sourceMappingURL=browser-surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-surface.js","sourceRoot":"","sources":["../src/browser-surface.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Minimal HTML → text / markdown extractors used by `web_fetch`. Not a
3
+ * full DOM parser: regex-based, intentionally limited. For stricter
4
+ * extraction use the markdown converter with a selector or upgrade to
5
+ * browser_session.
6
+ */
7
+ export interface ExtractOptions {
8
+ selector?: string;
9
+ }
10
+ /**
11
+ * Minimal HTML → plain text. Strips <script>, <style>, comments, and tags.
12
+ * Collapses whitespace. Decodes the common HTML entities.
13
+ */
14
+ export declare function htmlToPlainText(html: string, opts?: ExtractOptions): string;
15
+ /**
16
+ * Minimal HTML → markdown. Maps headings, lists, links, code blocks, and
17
+ * paragraphs. Falls through to plain-text rules for unknown structure.
18
+ */
19
+ export declare function htmlToMarkdown(html: string, opts?: ExtractOptions): string;
20
+ //# sourceMappingURL=html-extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-extract.d.ts","sourceRoot":"","sources":["../src/html-extract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAyBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,MAAM,CAU/E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,MAAM,CAwC9E"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Minimal HTML → text / markdown extractors used by `web_fetch`. Not a
3
+ * full DOM parser: regex-based, intentionally limited. For stricter
4
+ * extraction use the markdown converter with a selector or upgrade to
5
+ * browser_session.
6
+ */
7
+ const COMMENT_RE = /<!--[\s\S]*?-->/g;
8
+ const SCRIPT_RE = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
9
+ const STYLE_RE = /<style\b[^>]*>[\s\S]*?<\/style>/gi;
10
+ /**
11
+ * Hard ceiling on the HTML fed to the regex extractors. web_fetch caps the raw
12
+ * body at up to 20MB (maxBytes), but the lazy `[\s\S]*?` passes here run
13
+ * SYNCHRONOUSLY on the runner event loop — a multi-MB adversarial page with
14
+ * many unmatched open tags could block it. Truncating to a generous 4MB bounds
15
+ * that worst case while still covering virtually every real article.
16
+ */
17
+ const MAX_EXTRACT_INPUT = 4 * 1024 * 1024;
18
+ /** Bound the regex-extractor input so a hostile, oversized page can't stall the
19
+ * event loop. Cuts at a tag boundary when one is near the limit to avoid
20
+ * splitting an entity/tag mid-token. */
21
+ function capInput(html) {
22
+ if (html.length <= MAX_EXTRACT_INPUT)
23
+ return html;
24
+ const slice = html.slice(0, MAX_EXTRACT_INPUT);
25
+ const lastLt = slice.lastIndexOf('<');
26
+ return lastLt > MAX_EXTRACT_INPUT - 1024 ? slice.slice(0, lastLt) : slice;
27
+ }
28
+ /**
29
+ * Minimal HTML → plain text. Strips <script>, <style>, comments, and tags.
30
+ * Collapses whitespace. Decodes the common HTML entities.
31
+ */
32
+ export function htmlToPlainText(html, opts = {}) {
33
+ let body = sliceBySelector(capInput(html), opts.selector);
34
+ body = body.replace(COMMENT_RE, '');
35
+ body = body.replace(SCRIPT_RE, '');
36
+ body = body.replace(STYLE_RE, '');
37
+ body = body.replace(/<br\b[^>]*>/gi, '\n');
38
+ body = body.replace(/<\/(p|div|li|tr|h[1-6])>/gi, '\n');
39
+ body = body.replace(/<[^>]+>/g, '');
40
+ body = decodeEntities(body);
41
+ return collapseWhitespace(body);
42
+ }
43
+ /**
44
+ * Minimal HTML → markdown. Maps headings, lists, links, code blocks, and
45
+ * paragraphs. Falls through to plain-text rules for unknown structure.
46
+ */
47
+ export function htmlToMarkdown(html, opts = {}) {
48
+ let body = sliceBySelector(capInput(html), opts.selector);
49
+ body = body.replace(COMMENT_RE, '');
50
+ body = body.replace(SCRIPT_RE, '');
51
+ body = body.replace(STYLE_RE, '');
52
+ // headings
53
+ body = body.replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_, lvl, inner) => `\n\n${'#'.repeat(Number(lvl))} ${stripTags(inner).trim()}\n\n`);
54
+ // links: keep text + url. Accept double-quoted, single-quoted, and unquoted
55
+ // hrefs — real-world HTML uses all three, and matching only "double" silently
56
+ // dropped the URL (the link text survived via the later tag-strip).
57
+ body = body.replace(/<a\b[^>]*\bhref=(?:"([^"]+)"|'([^']+)'|([^\s>]+))[^>]*>([\s\S]*?)<\/a>/gi, (_, dq, sq, uq, inner) => `[${stripTags(inner).trim()}](${dq ?? sq ?? uq})`);
58
+ // code
59
+ body = body.replace(/<pre\b[^>]*>([\s\S]*?)<\/pre>/gi, (_, inner) => `\n\n\`\`\`\n${stripTags(inner)}\n\`\`\`\n\n`);
60
+ body = body.replace(/<code\b[^>]*>([\s\S]*?)<\/code>/gi, (_, inner) => `\`${stripTags(inner)}\``);
61
+ // lists
62
+ body = body.replace(/<li\b[^>]*>([\s\S]*?)<\/li>/gi, (_, inner) => `\n- ${stripTags(inner).trim()}`);
63
+ body = body.replace(/<\/?(ul|ol)\b[^>]*>/gi, '\n');
64
+ // line breaks
65
+ body = body.replace(/<br\b[^>]*>/gi, '\n');
66
+ body = body.replace(/<\/?p\b[^>]*>/gi, '\n\n');
67
+ body = body.replace(/<[^>]+>/g, '');
68
+ body = decodeEntities(body);
69
+ return collapseWhitespace(body);
70
+ }
71
+ function sliceBySelector(html, selector) {
72
+ if (!selector)
73
+ return html;
74
+ const slice = extractFirstTagBlock(html, selector);
75
+ return slice ?? html;
76
+ }
77
+ function collapseWhitespace(s) {
78
+ return s.replace(/[ \t]+/g, ' ').replace(/\n[ \t]+/g, '\n').replace(/\n{3,}/g, '\n\n').trim();
79
+ }
80
+ function stripTags(s) {
81
+ return s.replace(/<[^>]+>/g, '');
82
+ }
83
+ function decodeEntities(s) {
84
+ const map = {
85
+ '&amp;': '&',
86
+ '&lt;': '<',
87
+ '&gt;': '>',
88
+ '&quot;': '"',
89
+ '&#39;': "'",
90
+ '&apos;': "'",
91
+ '&nbsp;': ' ',
92
+ };
93
+ return s.replace(/&[a-zA-Z]+;|&#\d+;/g, (m) => {
94
+ if (m in map)
95
+ return map[m];
96
+ const numMatch = /^&#(\d+);$/.exec(m);
97
+ if (numMatch)
98
+ return String.fromCharCode(Number(numMatch[1]));
99
+ return m;
100
+ });
101
+ }
102
+ /**
103
+ * Pull the first <tag>...</tag> block (or self-closing tag with id="x") whose
104
+ * tag name OR id matches `selector`. Very limited — supports `tagName` and
105
+ * `#id` only. For richer querying upgrade to browser_session.
106
+ */
107
+ function extractFirstTagBlock(html, selector) {
108
+ if (selector.startsWith('#')) {
109
+ const id = selector.slice(1);
110
+ const re = new RegExp(`<([a-z][a-z0-9-]*)\\b[^>]*\\bid=["']${escapeReSelector(id)}["'][^>]*>([\\s\\S]*?)<\\/\\1>`, 'i');
111
+ const match = re.exec(html);
112
+ return match ? match[2] : null;
113
+ }
114
+ const tag = selector.toLowerCase();
115
+ const re = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'i');
116
+ const match = re.exec(html);
117
+ return match ? match[1] : null;
118
+ }
119
+ function escapeReSelector(s) {
120
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
121
+ }
122
+ //# sourceMappingURL=html-extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-extract.js","sourceRoot":"","sources":["../src/html-extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type BrowserSessionDeps } from './browser-session.js';
2
+ export { webFetchTool, htmlToPlainText, htmlToMarkdown } from './web-fetch.js';
3
+ export { buildBrowserSessionTool, browserSidecarCall, closeBrowserSidecar, type BrowserSessionDeps, type SidecarStream, } from './browser-session.js';
4
+ export { buildBrowserSurface } from './browser-surface.js';
5
+ export interface BuildBrowserPluginOptions extends BrowserSessionDeps {
6
+ }
7
+ export declare function buildBrowserPlugin(opts?: BuildBrowserPluginOptions): import("@moxxy/sdk").Plugin;
8
+ export declare const browserPlugin: import("@moxxy/sdk").Plugin;
9
+ export default browserPlugin;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgD,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG7G,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;CAAG;AAExE,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,yBAA8B,+BAatE;AAED,eAAO,MAAM,aAAa,6BAAuB,CAAC;AAElD,eAAe,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ import { definePlugin } from '@moxxy/sdk';
2
+ import { webFetchTool } from './web-fetch.js';
3
+ import { buildBrowserSessionTool, closeBrowserSidecar } from './browser-session.js';
4
+ import { buildBrowserSurface } from './browser-surface.js';
5
+ export { webFetchTool, htmlToPlainText, htmlToMarkdown } from './web-fetch.js';
6
+ export { buildBrowserSessionTool, browserSidecarCall, closeBrowserSidecar, } from './browser-session.js';
7
+ export { buildBrowserSurface } from './browser-surface.js';
8
+ export function buildBrowserPlugin(opts = {}) {
9
+ return definePlugin({
10
+ name: '@moxxy/plugin-browser',
11
+ version: '0.0.0',
12
+ tools: [webFetchTool, buildBrowserSessionTool(opts)],
13
+ surfaces: [buildBrowserSurface(opts)],
14
+ hooks: {
15
+ onShutdown: async () => {
16
+ // Make sure the sidecar process exits with the session.
17
+ await closeBrowserSidecar();
18
+ },
19
+ },
20
+ });
21
+ }
22
+ export const browserPlugin = buildBrowserPlugin();
23
+ export default browserPlugin;
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAA2B,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GAGpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAI3D,MAAM,UAAU,kBAAkB,CAAC,OAAkC,EAAE;IACrE,OAAO,YAAY,CAAC;QAClB,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,OAAO;QAChB,KAAK,EAAE,CAAC,YAAY,EAAE,uBAAuB,CAAC,IAAI,CAAC,CAAC;QACpD,QAAQ,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACrC,KAAK,EAAE;YACL,UAAU,EAAE,KAAK,IAAI,EAAE;gBACrB,wDAAwD;gBACxD,MAAM,mBAAmB,EAAE,CAAC;YAC9B,CAAC;SACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,EAAE,CAAC;AAElD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * JSON-RPC dispatch table for the sidecar. Each `method` here corresponds
3
+ * one-to-one with the wire-format methods documented in `sidecar.ts`.
4
+ */
5
+ import { type PlaywrightHandle, type Reply, type Req } from './types.js';
6
+ export interface SidecarState {
7
+ handle: PlaywrightHandle | null;
8
+ /**
9
+ * Set after a successful auto-install of browser binaries so the next
10
+ * tool result can carry a `notice` letting the user/model know the
11
+ * one-time download happened. Cleared once the notice has been
12
+ * delivered (handed to the reply once, then forgotten).
13
+ */
14
+ pendingInstallNotice: string | null;
15
+ }
16
+ export declare function teardown(state: SidecarState): Promise<void>;
17
+ export declare function dispatch(state: SidecarState, req: Req): Promise<Reply>;
18
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/sidecar/dispatch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,KAAK,EACV,KAAK,GAAG,EACT,MAAM,YAAY,CAAC;AAepB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC;;;;;OAKG;IACH,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAgBD,wBAAsB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CASjE;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAU5E"}