@moxxy/cli 0.11.0 → 0.12.1
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/dist/bin.js +2301 -1131
- package/dist/bin.js.map +1 -1
- package/dist/public/app.js +8 -8
- package/dist/public/index.html +24 -0
- package/dist/sidecar.js +83 -0
- package/dist/sidecar.js.map +1 -1
- package/dist/skills/browser.md +32 -0
- package/dist/skills/terminal.md +33 -0
- package/package.json +4 -3
package/dist/public/index.html
CHANGED
|
@@ -125,6 +125,30 @@
|
|
|
125
125
|
border-radius: 10px; padding: 9px 11px; font: inherit; }
|
|
126
126
|
.chat-input button { background: var(--accent); color: #fff; border: 0; border-radius: 10px;
|
|
127
127
|
padding: 0 14px; font: inherit; font-weight: 600; cursor: pointer; }
|
|
128
|
+
/* file diff */
|
|
129
|
+
.v-diff { align-self: stretch; border: 1px solid var(--line); border-radius: var(--radius);
|
|
130
|
+
overflow: hidden; background: var(--panel); font-size: 12.5px; }
|
|
131
|
+
.v-diff-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
132
|
+
padding: 6px 10px; background: var(--panel2); border-bottom: 1px solid var(--line); }
|
|
133
|
+
.v-diff-path { font-weight: 600; word-break: break-all; }
|
|
134
|
+
.v-diff-sum { color: var(--ok); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
135
|
+
white-space: nowrap; }
|
|
136
|
+
.v-diff-body { overflow-x: auto; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
137
|
+
line-height: 1.45; }
|
|
138
|
+
.v-diff-row { display: flex; white-space: pre; }
|
|
139
|
+
.v-diff-no { flex: 0 0 auto; width: 3.5em; padding: 0 8px; text-align: right; color: var(--muted);
|
|
140
|
+
user-select: none; border-right: 1px solid var(--line); }
|
|
141
|
+
.v-diff-mark { flex: 0 0 auto; width: 1.5em; text-align: center; user-select: none; color: var(--muted); }
|
|
142
|
+
.v-diff-text { flex: 1 1 auto; padding-right: 10px; }
|
|
143
|
+
.v-diff-ctx { color: var(--muted); }
|
|
144
|
+
.v-diff-add { background: rgba(63, 185, 80, 0.16); }
|
|
145
|
+
.v-diff-add .v-diff-mark, .v-diff-add .v-diff-text { color: var(--ok); }
|
|
146
|
+
.v-diff-del { background: rgba(248, 81, 73, 0.16); }
|
|
147
|
+
.v-diff-del .v-diff-mark, .v-diff-del .v-diff-text { color: var(--danger); }
|
|
148
|
+
.v-diff-gap { color: var(--muted); opacity: 0.6; }
|
|
149
|
+
.v-diff-gap .v-diff-mark { color: var(--muted); }
|
|
150
|
+
.v-diff-trunc { padding: 4px 10px; color: var(--warn); font-size: 12px;
|
|
151
|
+
border-top: 1px solid var(--line); background: var(--panel2); }
|
|
128
152
|
</style>
|
|
129
153
|
</head>
|
|
130
154
|
<body>
|
package/dist/sidecar.js
CHANGED
|
@@ -182,6 +182,7 @@ async function ensurePlaywright(state2, opts) {
|
|
|
182
182
|
return state2.handle;
|
|
183
183
|
}
|
|
184
184
|
async function teardown(state2) {
|
|
185
|
+
await stopScreencast(state2);
|
|
185
186
|
if (!state2.handle) return;
|
|
186
187
|
try {
|
|
187
188
|
await state2.handle.context.close();
|
|
@@ -190,6 +191,18 @@ async function teardown(state2) {
|
|
|
190
191
|
}
|
|
191
192
|
state2.handle = null;
|
|
192
193
|
}
|
|
194
|
+
async function stopScreencast(state2) {
|
|
195
|
+
if (!state2.cdp) return;
|
|
196
|
+
try {
|
|
197
|
+
await state2.cdp.send("Page.stopScreencast");
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
await state2.cdp.detach();
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
state2.cdp = null;
|
|
205
|
+
}
|
|
193
206
|
async function dispatch(state2, req) {
|
|
194
207
|
try {
|
|
195
208
|
return await dispatchInner(state2, req);
|
|
@@ -260,6 +273,73 @@ async function dispatchInner(state2, req) {
|
|
|
260
273
|
const buf = await h.page.screenshot({ fullPage: fullPage ?? false });
|
|
261
274
|
return { id: req.id, ok: true, result: { mediaType: "image/png", base64: buf.toString("base64") } };
|
|
262
275
|
}
|
|
276
|
+
case "frame": {
|
|
277
|
+
const h = await ensurePlaywright(state2, {});
|
|
278
|
+
const buf = await h.page.screenshot({ type: "jpeg", quality: 55 });
|
|
279
|
+
const vp = h.page.viewportSize() ?? { width: 1280, height: 720 };
|
|
280
|
+
return {
|
|
281
|
+
id: req.id,
|
|
282
|
+
ok: true,
|
|
283
|
+
result: {
|
|
284
|
+
mediaType: "image/jpeg",
|
|
285
|
+
base64: buf.toString("base64"),
|
|
286
|
+
url: h.page.url(),
|
|
287
|
+
width: vp.width,
|
|
288
|
+
height: vp.height
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
case "mouse": {
|
|
293
|
+
const h = await ensurePlaywright(state2, {});
|
|
294
|
+
const { x, y } = req.params ?? {};
|
|
295
|
+
await h.page.mouse.click(x, y);
|
|
296
|
+
return { id: req.id, ok: true, result: { url: h.page.url() } };
|
|
297
|
+
}
|
|
298
|
+
case "key": {
|
|
299
|
+
const h = await ensurePlaywright(state2, {});
|
|
300
|
+
const { key } = req.params ?? {};
|
|
301
|
+
if (!key) throw badParams("key is required");
|
|
302
|
+
if (key.length === 1) await h.page.keyboard.type(key);
|
|
303
|
+
else await h.page.keyboard.press(key);
|
|
304
|
+
return { id: req.id, ok: true };
|
|
305
|
+
}
|
|
306
|
+
case "scroll": {
|
|
307
|
+
const h = await ensurePlaywright(state2, {});
|
|
308
|
+
const { dy } = req.params ?? {};
|
|
309
|
+
await h.page.mouse.wheel(0, dy ?? 0);
|
|
310
|
+
return { id: req.id, ok: true };
|
|
311
|
+
}
|
|
312
|
+
case "startScreencast": {
|
|
313
|
+
const h = await ensurePlaywright(state2, {});
|
|
314
|
+
if (state2.cdp) return { id: req.id, ok: true, result: { url: h.page.url() } };
|
|
315
|
+
if (!h.context.newCDPSession) {
|
|
316
|
+
return {
|
|
317
|
+
id: req.id,
|
|
318
|
+
ok: false,
|
|
319
|
+
error: { message: "screencast requires Chromium (CDP unavailable)", kind: "runtime" }
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
const client = await h.context.newCDPSession(h.page);
|
|
323
|
+
state2.cdp = client;
|
|
324
|
+
client.on("Page.screencastFrame", (params) => {
|
|
325
|
+
const p = params;
|
|
326
|
+
state2.emit?.({ event: "screencastFrame", data: p.data, url: h.page.url() });
|
|
327
|
+
void client.send("Page.screencastAck", { sessionId: p.sessionId }).catch(() => void 0);
|
|
328
|
+
});
|
|
329
|
+
const { maxWidth, maxHeight } = req.params ?? {};
|
|
330
|
+
await client.send("Page.startScreencast", {
|
|
331
|
+
format: "jpeg",
|
|
332
|
+
quality: 50,
|
|
333
|
+
maxWidth: maxWidth ?? 1280,
|
|
334
|
+
maxHeight: maxHeight ?? 800,
|
|
335
|
+
everyNthFrame: 1
|
|
336
|
+
});
|
|
337
|
+
return { id: req.id, ok: true, result: { url: h.page.url() } };
|
|
338
|
+
}
|
|
339
|
+
case "stopScreencast": {
|
|
340
|
+
await stopScreencast(state2);
|
|
341
|
+
return { id: req.id, ok: true };
|
|
342
|
+
}
|
|
263
343
|
case "eval": {
|
|
264
344
|
const h = await ensurePlaywright(state2, {});
|
|
265
345
|
const { expression } = req.params ?? {};
|
|
@@ -326,6 +406,9 @@ function startParentWatchdog() {
|
|
|
326
406
|
var queue = Promise.resolve();
|
|
327
407
|
async function main() {
|
|
328
408
|
startParentWatchdog();
|
|
409
|
+
state.emit = (event) => {
|
|
410
|
+
process.stdout.write(JSON.stringify(event) + "\n");
|
|
411
|
+
};
|
|
329
412
|
const rl = readline.createInterface({ input: process.stdin });
|
|
330
413
|
rl.on("line", (line) => {
|
|
331
414
|
if (!line.trim()) return;
|
package/dist/sidecar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../plugin-browser/src/ssrf-guard.ts","../../plugin-browser/src/sidecar/install.ts","../../plugin-browser/src/sidecar/types.ts","../../plugin-browser/src/sidecar/dispatch.ts","../../plugin-browser/src/sidecar.ts"],"names":["dnsLookup","state","text"],"mappings":"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","file":"sidecar.js","sourcesContent":["import { isIP } from 'node:net';\nimport { lookup as dnsLookup } from 'node:dns/promises';\n\n/**\n * Shared SSRF guard for every model-drivable navigation/fetch path in this\n * plugin: `web_fetch` (initial URL + every redirect hop), `browser_session`'s\n * goto (parent side), the sidecar's goto dispatch (child-process side), and\n * the sidecar's in-page navigation interceptor.\n *\n * Blocks non-HTTP(S) schemes, loopback (incl. `localhost`/`*.localhost`),\n * 0.0.0.0/8, RFC-1918 private ranges, link-local 169.254/16 (incl. the\n * 169.254.169.254 cloud metadata endpoint), CGNAT 100.64/10, multicast/\n * reserved, and IPv6 loopback/link-local/unique-local (+ v4-mapped). Hostnames\n * are resolved so an internal DNS name (or rebinding) can't smuggle a private\n * target past the literal-IP checks.\n *\n * NOTE: deliberately NO `@moxxy/sdk` import. The Playwright sidecar — a\n * separate child process whose bundle must stay free of non-builtin deps —\n * imports this module too. Parent-side callers wrap `SsrfBlockedError` into\n * `MoxxyError` themselves.\n */\n\n/** Thrown for every guard rejection (bad scheme, private/loopback target, unparseable URL). */\nexport class SsrfBlockedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SsrfBlockedError';\n }\n}\n\nexport type DnsResolver = (host: string) => Promise<ReadonlyArray<string>>;\nconst defaultResolver: DnsResolver = async (host) =>\n (await dnsLookup(host, { all: true })).map((a) => a.address);\nlet resolver: DnsResolver = defaultResolver;\n\n/** Test seam: override DNS resolution so SSRF tests stay hermetic. Pass null to reset. */\nexport function setSsrfDnsResolver(fn: DnsResolver | null): void {\n resolver = fn ?? defaultResolver;\n}\n\nfunction isBlockedIpv4(ip: string): boolean {\n const parts = ip.split('.').map((p) => Number(p));\n if (parts.length !== 4 || parts.some((p) => !Number.isInteger(p) || p < 0 || p > 255)) return true;\n const [a, b] = parts as [number, number, number, number];\n if (a === 0) return true; // 0.0.0.0/8 \"this host\"\n if (a === 127) return true; // loopback\n if (a === 10) return true; // private\n if (a === 172 && b >= 16 && b <= 31) return true; // private\n if (a === 192 && b === 168) return true; // private\n if (a === 169 && b === 254) return true; // link-local incl. 169.254.169.254 metadata\n if (a === 100 && b >= 64 && b <= 127) return true; // CGNAT 100.64/10\n if (a >= 224) return true; // multicast / reserved\n return false;\n}\n\nfunction isBlockedIpv6(ip: string): boolean {\n const addr = ip.toLowerCase().replace(/^\\[|\\]$/g, '').split('%')[0]!; // strip zone id\n if (addr === '::1' || addr === '::') return true; // loopback / unspecified\n if (addr.startsWith('fe80')) return true; // link-local\n if (addr.startsWith('fc') || addr.startsWith('fd')) return true; // unique-local fc00::/7\n const v4mapped = addr.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (v4mapped) return isBlockedIpv4(v4mapped[1]!);\n return false;\n}\n\nexport function isBlockedIp(ip: string): boolean {\n const kind = isIP(ip);\n if (kind === 4) return isBlockedIpv4(ip);\n if (kind === 6) return isBlockedIpv6(ip);\n return true; // not a parseable IP → block\n}\n\n/**\n * Reject `raw` unless it is an http(s) URL whose host is (and resolves to)\n * a public address. `label` prefixes the error message so each caller's\n * rejections stay attributable (e.g. \"web_fetch\", \"browser_session\").\n *\n * Returns the vetted resolved addresses so callers can PIN the subsequent\n * connection to exactly what was checked (closing the DNS-rebinding TOCTOU\n * where the guard resolves one answer and the fetch independently resolves\n * another — see web-fetch's pinned undici dispatcher). Returns `null` when\n * there is nothing to pin: the host is already an IP literal (vetted above,\n * no DNS involved) or resolution failed (fail-open — a name the guard can't\n * resolve, the fetch's identical system resolver can't reach either).\n */\nexport async function assertPublicUrl(\n raw: string,\n label = 'request',\n): Promise<ReadonlyArray<string> | null> {\n let u: URL;\n try {\n u = new URL(raw);\n } catch {\n throw new SsrfBlockedError(`${label}: invalid URL: ${raw}`);\n }\n if (u.protocol !== 'http:' && u.protocol !== 'https:') {\n throw new SsrfBlockedError(`${label}: refusing non-HTTP(S) scheme \"${u.protocol}\"`);\n }\n const host = u.hostname.replace(/^\\[|\\]$/g, '');\n if (host === 'localhost' || host.endsWith('.localhost')) {\n throw new SsrfBlockedError(`${label}: refusing to fetch loopback host \"${host}\"`);\n }\n if (isIP(host)) {\n if (isBlockedIp(host))\n throw new SsrfBlockedError(`${label}: refusing private/loopback address \"${host}\"`);\n return null;\n }\n // Resolve the name and block if it maps to a private range (internal name or\n // DNS rebinding). Fail OPEN on resolution error: a name we can't resolve here\n // the subsequent fetch can't reach either, so it's no SSRF vector.\n let addrs: ReadonlyArray<string>;\n try {\n addrs = await resolver(host);\n } catch {\n return null;\n }\n for (const addr of addrs) {\n if (isBlockedIp(addr)) {\n throw new SsrfBlockedError(\n `${label}: host \"${host}\" resolves to a private/loopback address (${addr})`,\n );\n }\n }\n return addrs;\n}\n","/**\n * Playwright lifecycle: import, launch, and one-shot auto-install of the\n * per-browser binary. Keeps the dispatch layer free of node:child_process\n * + Playwright-import noise.\n */\n\nimport { spawn } from 'node:child_process';\nimport { assertPublicUrl } from '../ssrf-guard.js';\nimport type { BrowserKind, BrowserType, PageHandle, PlaywrightHandle } from './types.js';\n\nexport interface LaunchResult {\n handle: PlaywrightHandle;\n /** Set when the browser binary was auto-downloaded during this launch. */\n installNotice: string | null;\n}\n\nexport async function importPlaywright(): Promise<{\n chromium: BrowserType;\n firefox: BrowserType;\n webkit: BrowserType;\n}> {\n try {\n return (await import('playwright')) as never;\n } catch (err) {\n const e = new Error(\n `Playwright is not installed. Run \\`pnpm add playwright\\` (or \\`npm i playwright\\`) and then \\`npx playwright install\\` in the moxxy install dir.\\n` +\n `Underlying: ${err instanceof Error ? err.message : String(err)}`,\n );\n (e as Error & { kind?: string }).kind = 'init';\n throw e;\n }\n}\n\n/**\n * Try to launch the browser. If the binary isn't downloaded yet\n * (Playwright distinguishes the npm install from the per-browser\n * binary download), run `npx playwright install <which>` once and\n * retry. The install can take 30s–2min on the first run depending on\n * connection; we surface progress on stderr (parent forwards to the\n * logger) and return a one-shot notice for the first tool response.\n */\nexport async function launchWithAutoInstall(\n browserType: BrowserType,\n which: BrowserKind,\n headless: boolean,\n): Promise<LaunchResult> {\n try {\n return { handle: await launchOnce(browserType, headless), installNotice: null };\n } catch (err) {\n if (!isMissingBrowserError(err)) throw err;\n process.stderr.write(\n `moxxy-browser: ${which} binary missing, running \\`npx playwright install ${which}\\` ` +\n `(one-time, ~150MB). This may take a minute…\\n`,\n );\n try {\n await runPlaywrightInstall(which);\n } catch (installErr) {\n const msg = installErr instanceof Error ? installErr.message : String(installErr);\n const e = new Error(\n `Playwright browser auto-install failed: ${msg}. ` +\n `Run \\`npx playwright install ${which}\\` manually in the moxxy dir.`,\n );\n (e as Error & { kind?: string }).kind = 'init';\n throw e;\n }\n process.stderr.write(`moxxy-browser: install complete, retrying launch\\n`);\n return {\n handle: await launchOnce(browserType, headless),\n installNotice: `Auto-installed Playwright ${which} browser (~150MB, one-time).`,\n };\n }\n}\n\nasync function launchOnce(browserType: BrowserType, headless: boolean): Promise<PlaywrightHandle> {\n const browser = await browserType.launch({ headless });\n const context = (await browser.newContext()) as PlaywrightHandle['context'];\n await installNavigationSsrfGuard(context);\n const page = (await context.newPage()) as unknown as PageHandle;\n return { browser, context, page };\n}\n\n/**\n * Block navigations to private/loopback origins for the lifetime of the\n * context. The goto RPC is validated in the parent AND in dispatch, but a\n * page reached via a legitimate public goto can then redirect or\n * script-navigate itself to e.g. http://169.254.169.254/ — those navigations\n * never pass through the RPC layer, so we intercept them here. Navigation\n * requests (top-level + iframes, which covers HTTP redirect hops too) go\n * through the same `assertPublicUrl` guard as web_fetch; everything else is\n * passed through untouched so ordinary page loads don't pay a DNS round-trip\n * per subresource.\n *\n * Residual risk (also stated in the browser_session tool description):\n * SUBRESOURCE requests (img/fetch/script) from a loaded page are NOT\n * filtered. The browser's same-origin policy stops the page reading those\n * responses, but blind request side effects against internal services remain\n * possible. Filtering every request was judged disproportionate for now.\n */\nasync function installNavigationSsrfGuard(context: PlaywrightHandle['context']): Promise<void> {\n if (typeof context.route !== 'function') return; // loose projection: tolerate stubs without route()\n await context.route('**/*', async (route) => {\n const request = route.request();\n if (!request.isNavigationRequest()) return route.continue();\n try {\n await assertPublicUrl(request.url(), 'browser_session navigation');\n } catch {\n return route.abort('blockedbyclient');\n }\n return route.continue();\n });\n}\n\nfunction isMissingBrowserError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n // Playwright's \"Executable doesn't exist at …\" launch error fires\n // when the npm package is installed but the per-browser binary\n // hasn't been downloaded. The message stays stable across versions.\n return /Executable doesn'?t exist at/i.test(err.message);\n}\n\n/**\n * Run `npx playwright install <which>` and stream its output to the\n * sidecar's stderr so the operator can watch progress. Resolves on\n * exit-0; rejects with the tail of stderr otherwise.\n */\nfunction runPlaywrightInstall(which: BrowserKind): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn('npx', ['playwright', 'install', which], {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n let stderrTail = '';\n child.stdout.on('data', (chunk: Buffer) => process.stderr.write(chunk));\n child.stderr.on('data', (chunk: Buffer) => {\n process.stderr.write(chunk);\n stderrTail += chunk.toString('utf8');\n if (stderrTail.length > 4000) stderrTail = stderrTail.slice(-4000);\n });\n child.once('error', (err) => reject(err));\n child.once('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`exit ${code}: ${stderrTail.trim() || '(no stderr)'}`));\n });\n });\n}\n","export type BrowserKind = 'chromium' | 'firefox' | 'webkit';\n\nexport type ErrorKind = 'init' | 'navigation' | 'runtime' | 'timeout' | 'unknown';\n\nexport interface Req {\n readonly id: string;\n readonly method: string;\n readonly params?: Record<string, unknown>;\n}\n\nexport interface Ok {\n readonly id: string;\n readonly ok: true;\n readonly result?: unknown;\n /** One-shot human-readable note (e.g. \"Auto-installed Chromium\"). */\n readonly notice?: string;\n}\n\nexport interface Err {\n readonly id: string;\n readonly ok: false;\n readonly error: { message: string; kind: ErrorKind };\n}\n\nexport type Reply = Ok | Err;\n\nexport interface BrowserType {\n launch(opts: {\n headless: boolean;\n }): Promise<{ close(): Promise<void>; newContext(): Promise<unknown> }>;\n}\n\nexport interface PageHandle {\n goto(url: string, opts?: unknown): Promise<unknown>;\n click(selector: string, opts?: unknown): Promise<void>;\n fill(selector: string, value: string, opts?: unknown): Promise<void>;\n textContent(selector: string): Promise<string | null>;\n content(): Promise<string>;\n screenshot(opts?: unknown): Promise<Buffer>;\n evaluate(fn: string): Promise<unknown>;\n url(): string;\n close(): Promise<void>;\n}\n\n/** Minimal slice of Playwright's `Request` used by the navigation SSRF guard. */\nexport interface RouteRequest {\n url(): string;\n isNavigationRequest(): boolean;\n}\n\n/** Minimal slice of Playwright's `Route` used by the navigation SSRF guard. */\nexport interface RouteHandle {\n request(): RouteRequest;\n abort(errorCode?: string): Promise<void>;\n continue(): Promise<void>;\n}\n\nexport interface PlaywrightHandle {\n // Loosely typed so we can avoid importing the playwright types at compile time —\n // they're an optional peer dependency.\n readonly browser: { close(): Promise<void> };\n readonly context: {\n newPage(): Promise<unknown>;\n close(): Promise<void>;\n /** Optional because the type is a loose projection; real Playwright contexts always have it. */\n route?(pattern: string, handler: (route: RouteHandle) => Promise<void> | void): Promise<void>;\n };\n readonly page: PageHandle;\n}\n\nexport function errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nexport function badParams(msg: string): Error {\n const e = new Error(msg);\n (e as Error & { kind?: string }).kind = 'runtime';\n return e;\n}\n","/**\n * JSON-RPC dispatch table for the sidecar. Each `method` here corresponds\n * one-to-one with the wire-format methods documented in `sidecar.ts`.\n */\n\nimport { assertPublicUrl } from '../ssrf-guard.js';\nimport { importPlaywright, launchWithAutoInstall } from './install.js';\nimport {\n badParams,\n errMsg,\n type BrowserKind,\n type Err,\n type PlaywrightHandle,\n type Reply,\n type Req,\n} from './types.js';\n\nexport interface SidecarState {\n handle: PlaywrightHandle | null;\n /**\n * Set after a successful auto-install of browser binaries so the next\n * tool result can carry a `notice` letting the user/model know the\n * one-time download happened. Cleared once the notice has been\n * delivered (handed to the reply once, then forgotten).\n */\n pendingInstallNotice: string | null;\n}\n\nasync function ensurePlaywright(\n state: SidecarState,\n opts: { browser?: BrowserKind; headless?: boolean },\n): Promise<PlaywrightHandle> {\n if (state.handle) return state.handle;\n const pw = await importPlaywright();\n const which = opts.browser ?? 'chromium';\n const browserType = pw[which];\n const { handle, installNotice } = await launchWithAutoInstall(browserType, which, opts.headless ?? true);\n state.handle = handle;\n if (installNotice) state.pendingInstallNotice = installNotice;\n return state.handle;\n}\n\nexport async function teardown(state: SidecarState): Promise<void> {\n if (!state.handle) return;\n try {\n await state.handle.context.close();\n await state.handle.browser.close();\n } catch {\n /* ignore */\n }\n state.handle = null;\n}\n\nexport async function dispatch(state: SidecarState, req: Req): Promise<Reply> {\n try {\n return await dispatchInner(state, req);\n } catch (err) {\n const kind = (err as Error & { kind?: string }).kind;\n return {\n id: req.id,\n ok: false,\n error: { message: errMsg(err), kind: (kind as Err['error']['kind']) ?? 'unknown' },\n };\n }\n}\n\nasync function dispatchInner(state: SidecarState, req: Req): Promise<Reply> {\n switch (req.method) {\n case 'init': {\n const opts = (req.params ?? {}) as { browser?: BrowserKind; headless?: boolean };\n await ensurePlaywright(state, opts);\n return { id: req.id, ok: true, result: { ready: true } };\n }\n case 'goto': {\n const { url, waitUntil, timeoutMs } = (req.params ?? {}) as {\n url: string;\n waitUntil?: 'load' | 'domcontentloaded' | 'networkidle';\n timeoutMs?: number;\n };\n if (!url) throw badParams('url is required');\n // Defence-in-depth: the parent already runs the full SSRF guard before\n // sending this RPC, but the sidecar is a distinct process driven over\n // JSON-RPC, so re-check here rather than trust the caller to have\n // validated. Blocks file:// / javascript: schemes AND loopback/private/\n // link-local (incl. 169.254.169.254 metadata)/CGNAT targets, resolving\n // hostnames. Runs BEFORE ensurePlaywright so a blocked URL never\n // launches (or auto-installs) a browser.\n try {\n await assertPublicUrl(url, 'goto');\n } catch (err) {\n return { id: req.id, ok: false, error: { message: errMsg(err), kind: 'navigation' } };\n }\n const h = await ensurePlaywright(state, {});\n try {\n await h.page.goto(url, { waitUntil: waitUntil ?? 'domcontentloaded', timeout: timeoutMs ?? 30_000 });\n } catch (err) {\n return { id: req.id, ok: false, error: { message: errMsg(err), kind: 'navigation' } };\n }\n return { id: req.id, ok: true, result: { url: h.page.url() } };\n }\n case 'click': {\n const h = await ensurePlaywright(state, {});\n const { selector, timeoutMs } = (req.params ?? {}) as { selector: string; timeoutMs?: number };\n if (!selector) throw badParams('selector is required');\n await h.page.click(selector, { timeout: timeoutMs ?? 10_000 });\n return { id: req.id, ok: true };\n }\n case 'fill': {\n const h = await ensurePlaywright(state, {});\n const { selector, value, timeoutMs } = (req.params ?? {}) as {\n selector: string;\n value: string;\n timeoutMs?: number;\n };\n if (!selector) throw badParams('selector is required');\n await h.page.fill(selector, value ?? '', { timeout: timeoutMs ?? 10_000 });\n return { id: req.id, ok: true };\n }\n case 'text': {\n const h = await ensurePlaywright(state, {});\n const { selector } = (req.params ?? {}) as { selector?: string };\n if (selector) {\n const text = await h.page.textContent(selector);\n return { id: req.id, ok: true, result: text ?? '' };\n }\n // Whole-document text via evaluate\n const text = (await h.page.evaluate('document.body ? document.body.innerText : \"\"')) as string;\n return { id: req.id, ok: true, result: text };\n }\n case 'html': {\n const h = await ensurePlaywright(state, {});\n const html = await h.page.content();\n return { id: req.id, ok: true, result: html };\n }\n case 'screenshot': {\n const h = await ensurePlaywright(state, {});\n const { fullPage } = (req.params ?? {}) as { fullPage?: boolean };\n const buf = await h.page.screenshot({ fullPage: fullPage ?? false });\n return { id: req.id, ok: true, result: { mediaType: 'image/png', base64: buf.toString('base64') } };\n }\n case 'eval': {\n const h = await ensurePlaywright(state, {});\n const { expression } = (req.params ?? {}) as { expression: string };\n if (!expression) throw badParams('expression is required');\n const value = await h.page.evaluate(expression);\n return { id: req.id, ok: true, result: value };\n }\n case 'url': {\n const h = await ensurePlaywright(state, {});\n return { id: req.id, ok: true, result: h.page.url() };\n }\n case 'close': {\n await teardown(state);\n return { id: req.id, ok: true };\n }\n default:\n return {\n id: req.id,\n ok: false,\n error: { message: `unknown method: ${req.method}`, kind: 'runtime' },\n };\n }\n}\n","#!/usr/bin/env node\n/**\n * Playwright sidecar — owns a single browser context. Speaks newline-delimited\n * JSON-RPC over stdio so the parent (`browser_session` tool) doesn't load\n * Playwright into its own process. Crash isolation, lazy install: the parent\n * only spawns this when the heavy tier is actually needed.\n *\n * Wire format (one line per message):\n * { \"id\": \"uuid\", \"method\": \"goto\"|\"click\"|\"fill\"|\"text\"|\"html\"|\"screenshot\"|\"eval\"|\"close\", \"params\": {...} }\n * { \"id\": \"uuid\", \"ok\": true, \"result\": ... }\n * { \"id\": \"uuid\", \"ok\": false, \"error\": { \"message\": \"...\", \"kind\": \"init\"|\"navigation\"|\"runtime\"|\"timeout\" } }\n *\n * Run with `node dist/sidecar.js` (the package's `bin` entry is\n * `moxxy-browser-sidecar`). Parent terminates by closing stdin or sending\n * `{method:'close'}`.\n */\n\nimport * as readline from 'node:readline';\nimport { dispatch, teardown, type SidecarState } from './sidecar/dispatch.js';\nimport { errMsg, type Reply, type Req } from './sidecar/types.js';\n\nconst state: SidecarState = { handle: null, pendingInstallNotice: null };\n\nfunction write(reply: Reply): void {\n // Drain the install-notice flag into the first reply that goes out\n // after the install completed, then clear it. Errors get the notice\n // too — sometimes the launch retry surfaces a different problem and\n // the user still wants to know we tried to install.\n if (state.pendingInstallNotice) {\n if (reply.ok) {\n reply = { ...reply, notice: state.pendingInstallNotice };\n } else {\n reply = {\n ...reply,\n error: {\n ...reply.error,\n message: `${state.pendingInstallNotice} Then: ${reply.error.message}`,\n },\n };\n }\n state.pendingInstallNotice = null;\n }\n process.stdout.write(JSON.stringify(reply) + '\\n');\n}\n\n/**\n * Tears down the browser context AND exits the process. Used both by\n * the explicit `close` RPC path and the parent-loss / stdin-close\n * paths so all cleanup goes through one routine.\n */\nasync function shutdownAndExit(code: number): Promise<void> {\n await teardown(state);\n process.exit(code);\n}\n\n/**\n * Parent watchdog: if the moxxy process that spawned this sidecar\n * disappears (crash, SIGKILL, terminal hangup), there's no stdin EOF\n * to rely on — orphan Chromium would keep running and chew CPU/memory\n * until the user notices. Poll the parent PID every few seconds and\n * self-terminate when it goes away.\n *\n * `process.kill(pid, 0)` is the POSIX trick for \"does this PID\n * exist?\" — no signal is actually delivered. Throws ESRCH when the\n * process is gone (or EPERM when it exists but we can't signal it —\n * still alive, so don't treat as gone).\n */\nfunction startParentWatchdog(): void {\n const parentPid = process.ppid;\n if (!parentPid || parentPid <= 1) return; // already orphaned (init), nothing to watch\n const interval = setInterval(() => {\n try {\n process.kill(parentPid, 0);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ESRCH') {\n clearInterval(interval);\n void shutdownAndExit(0);\n }\n // EPERM means the process exists but we can't signal it — still alive.\n }\n }, 2000);\n interval.unref?.(); // never block the event loop from exiting\n}\n\nlet queue: Promise<void> = Promise.resolve();\n\nasync function main(): Promise<void> {\n startParentWatchdog();\n const rl = readline.createInterface({ input: process.stdin });\n rl.on('line', (line) => {\n if (!line.trim()) return;\n let req: Req;\n try {\n req = JSON.parse(line) as Req;\n } catch {\n write({ id: 'unknown', ok: false, error: { message: 'invalid JSON', kind: 'runtime' } });\n return;\n }\n if (!req.id || !req.method) {\n write({\n id: req.id ?? 'unknown',\n ok: false,\n error: { message: 'request requires { id, method }', kind: 'runtime' },\n });\n return;\n }\n // Sequentially serve requests on the single page. Parent can pipeline by\n // sending more requests; we serialize them inside the sidecar so a goto\n // doesn't race a click.\n queue = queue.then(async () => {\n const reply = await dispatch(state, req);\n write(reply);\n });\n });\n rl.once('close', () => {\n void shutdownAndExit(0);\n });\n // Defensive: if our stdout pipe breaks (parent died mid-write), Node\n // would otherwise throw an uncaught EPIPE on the next write. Treat\n // it as a signal to clean up gracefully instead.\n process.stdout.on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'EPIPE') {\n void shutdownAndExit(0);\n }\n });\n}\n\nmain().catch((err) => {\n process.stderr.write(`sidecar fatal: ${errMsg(err)}\\n`);\n void shutdownAndExit(1);\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../plugin-browser/src/ssrf-guard.ts","../../plugin-browser/src/sidecar/install.ts","../../plugin-browser/src/sidecar/types.ts","../../plugin-browser/src/sidecar/dispatch.ts","../../plugin-browser/src/sidecar.ts"],"names":["dnsLookup","state","text"],"mappings":"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","file":"sidecar.js","sourcesContent":["import { isIP } from 'node:net';\nimport { lookup as dnsLookup } from 'node:dns/promises';\n\n/**\n * Shared SSRF guard for every model-drivable navigation/fetch path in this\n * plugin: `web_fetch` (initial URL + every redirect hop), `browser_session`'s\n * goto (parent side), the sidecar's goto dispatch (child-process side), and\n * the sidecar's in-page navigation interceptor.\n *\n * Blocks non-HTTP(S) schemes, loopback (incl. `localhost`/`*.localhost`),\n * 0.0.0.0/8, RFC-1918 private ranges, link-local 169.254/16 (incl. the\n * 169.254.169.254 cloud metadata endpoint), CGNAT 100.64/10, multicast/\n * reserved, and IPv6 loopback/link-local/unique-local (+ v4-mapped). Hostnames\n * are resolved so an internal DNS name (or rebinding) can't smuggle a private\n * target past the literal-IP checks.\n *\n * NOTE: deliberately NO `@moxxy/sdk` import. The Playwright sidecar — a\n * separate child process whose bundle must stay free of non-builtin deps —\n * imports this module too. Parent-side callers wrap `SsrfBlockedError` into\n * `MoxxyError` themselves.\n */\n\n/** Thrown for every guard rejection (bad scheme, private/loopback target, unparseable URL). */\nexport class SsrfBlockedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SsrfBlockedError';\n }\n}\n\nexport type DnsResolver = (host: string) => Promise<ReadonlyArray<string>>;\nconst defaultResolver: DnsResolver = async (host) =>\n (await dnsLookup(host, { all: true })).map((a) => a.address);\nlet resolver: DnsResolver = defaultResolver;\n\n/** Test seam: override DNS resolution so SSRF tests stay hermetic. Pass null to reset. */\nexport function setSsrfDnsResolver(fn: DnsResolver | null): void {\n resolver = fn ?? defaultResolver;\n}\n\nfunction isBlockedIpv4(ip: string): boolean {\n const parts = ip.split('.').map((p) => Number(p));\n if (parts.length !== 4 || parts.some((p) => !Number.isInteger(p) || p < 0 || p > 255)) return true;\n const [a, b] = parts as [number, number, number, number];\n if (a === 0) return true; // 0.0.0.0/8 \"this host\"\n if (a === 127) return true; // loopback\n if (a === 10) return true; // private\n if (a === 172 && b >= 16 && b <= 31) return true; // private\n if (a === 192 && b === 168) return true; // private\n if (a === 169 && b === 254) return true; // link-local incl. 169.254.169.254 metadata\n if (a === 100 && b >= 64 && b <= 127) return true; // CGNAT 100.64/10\n if (a >= 224) return true; // multicast / reserved\n return false;\n}\n\nfunction isBlockedIpv6(ip: string): boolean {\n const addr = ip.toLowerCase().replace(/^\\[|\\]$/g, '').split('%')[0]!; // strip zone id\n if (addr === '::1' || addr === '::') return true; // loopback / unspecified\n if (addr.startsWith('fe80')) return true; // link-local\n if (addr.startsWith('fc') || addr.startsWith('fd')) return true; // unique-local fc00::/7\n const v4mapped = addr.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (v4mapped) return isBlockedIpv4(v4mapped[1]!);\n return false;\n}\n\nexport function isBlockedIp(ip: string): boolean {\n const kind = isIP(ip);\n if (kind === 4) return isBlockedIpv4(ip);\n if (kind === 6) return isBlockedIpv6(ip);\n return true; // not a parseable IP → block\n}\n\n/**\n * Reject `raw` unless it is an http(s) URL whose host is (and resolves to)\n * a public address. `label` prefixes the error message so each caller's\n * rejections stay attributable (e.g. \"web_fetch\", \"browser_session\").\n *\n * Returns the vetted resolved addresses so callers can PIN the subsequent\n * connection to exactly what was checked (closing the DNS-rebinding TOCTOU\n * where the guard resolves one answer and the fetch independently resolves\n * another — see web-fetch's pinned undici dispatcher). Returns `null` when\n * there is nothing to pin: the host is already an IP literal (vetted above,\n * no DNS involved) or resolution failed (fail-open — a name the guard can't\n * resolve, the fetch's identical system resolver can't reach either).\n */\nexport async function assertPublicUrl(\n raw: string,\n label = 'request',\n): Promise<ReadonlyArray<string> | null> {\n let u: URL;\n try {\n u = new URL(raw);\n } catch {\n throw new SsrfBlockedError(`${label}: invalid URL: ${raw}`);\n }\n if (u.protocol !== 'http:' && u.protocol !== 'https:') {\n throw new SsrfBlockedError(`${label}: refusing non-HTTP(S) scheme \"${u.protocol}\"`);\n }\n const host = u.hostname.replace(/^\\[|\\]$/g, '');\n if (host === 'localhost' || host.endsWith('.localhost')) {\n throw new SsrfBlockedError(`${label}: refusing to fetch loopback host \"${host}\"`);\n }\n if (isIP(host)) {\n if (isBlockedIp(host))\n throw new SsrfBlockedError(`${label}: refusing private/loopback address \"${host}\"`);\n return null;\n }\n // Resolve the name and block if it maps to a private range (internal name or\n // DNS rebinding). Fail OPEN on resolution error: a name we can't resolve here\n // the subsequent fetch can't reach either, so it's no SSRF vector.\n let addrs: ReadonlyArray<string>;\n try {\n addrs = await resolver(host);\n } catch {\n return null;\n }\n for (const addr of addrs) {\n if (isBlockedIp(addr)) {\n throw new SsrfBlockedError(\n `${label}: host \"${host}\" resolves to a private/loopback address (${addr})`,\n );\n }\n }\n return addrs;\n}\n","/**\n * Playwright lifecycle: import, launch, and one-shot auto-install of the\n * per-browser binary. Keeps the dispatch layer free of node:child_process\n * + Playwright-import noise.\n */\n\nimport { spawn } from 'node:child_process';\nimport { assertPublicUrl } from '../ssrf-guard.js';\nimport type { BrowserKind, BrowserType, PageHandle, PlaywrightHandle } from './types.js';\n\nexport interface LaunchResult {\n handle: PlaywrightHandle;\n /** Set when the browser binary was auto-downloaded during this launch. */\n installNotice: string | null;\n}\n\nexport async function importPlaywright(): Promise<{\n chromium: BrowserType;\n firefox: BrowserType;\n webkit: BrowserType;\n}> {\n try {\n return (await import('playwright')) as never;\n } catch (err) {\n const e = new Error(\n `Playwright is not installed. Run \\`pnpm add playwright\\` (or \\`npm i playwright\\`) and then \\`npx playwright install\\` in the moxxy install dir.\\n` +\n `Underlying: ${err instanceof Error ? err.message : String(err)}`,\n );\n (e as Error & { kind?: string }).kind = 'init';\n throw e;\n }\n}\n\n/**\n * Try to launch the browser. If the binary isn't downloaded yet\n * (Playwright distinguishes the npm install from the per-browser\n * binary download), run `npx playwright install <which>` once and\n * retry. The install can take 30s–2min on the first run depending on\n * connection; we surface progress on stderr (parent forwards to the\n * logger) and return a one-shot notice for the first tool response.\n */\nexport async function launchWithAutoInstall(\n browserType: BrowserType,\n which: BrowserKind,\n headless: boolean,\n): Promise<LaunchResult> {\n try {\n return { handle: await launchOnce(browserType, headless), installNotice: null };\n } catch (err) {\n if (!isMissingBrowserError(err)) throw err;\n process.stderr.write(\n `moxxy-browser: ${which} binary missing, running \\`npx playwright install ${which}\\` ` +\n `(one-time, ~150MB). This may take a minute…\\n`,\n );\n try {\n await runPlaywrightInstall(which);\n } catch (installErr) {\n const msg = installErr instanceof Error ? installErr.message : String(installErr);\n const e = new Error(\n `Playwright browser auto-install failed: ${msg}. ` +\n `Run \\`npx playwright install ${which}\\` manually in the moxxy dir.`,\n );\n (e as Error & { kind?: string }).kind = 'init';\n throw e;\n }\n process.stderr.write(`moxxy-browser: install complete, retrying launch\\n`);\n return {\n handle: await launchOnce(browserType, headless),\n installNotice: `Auto-installed Playwright ${which} browser (~150MB, one-time).`,\n };\n }\n}\n\nasync function launchOnce(browserType: BrowserType, headless: boolean): Promise<PlaywrightHandle> {\n const browser = await browserType.launch({ headless });\n const context = (await browser.newContext()) as PlaywrightHandle['context'];\n await installNavigationSsrfGuard(context);\n const page = (await context.newPage()) as unknown as PageHandle;\n return { browser, context, page };\n}\n\n/**\n * Block navigations to private/loopback origins for the lifetime of the\n * context. The goto RPC is validated in the parent AND in dispatch, but a\n * page reached via a legitimate public goto can then redirect or\n * script-navigate itself to e.g. http://169.254.169.254/ — those navigations\n * never pass through the RPC layer, so we intercept them here. Navigation\n * requests (top-level + iframes, which covers HTTP redirect hops too) go\n * through the same `assertPublicUrl` guard as web_fetch; everything else is\n * passed through untouched so ordinary page loads don't pay a DNS round-trip\n * per subresource.\n *\n * Residual risk (also stated in the browser_session tool description):\n * SUBRESOURCE requests (img/fetch/script) from a loaded page are NOT\n * filtered. The browser's same-origin policy stops the page reading those\n * responses, but blind request side effects against internal services remain\n * possible. Filtering every request was judged disproportionate for now.\n */\nasync function installNavigationSsrfGuard(context: PlaywrightHandle['context']): Promise<void> {\n if (typeof context.route !== 'function') return; // loose projection: tolerate stubs without route()\n await context.route('**/*', async (route) => {\n const request = route.request();\n if (!request.isNavigationRequest()) return route.continue();\n try {\n await assertPublicUrl(request.url(), 'browser_session navigation');\n } catch {\n return route.abort('blockedbyclient');\n }\n return route.continue();\n });\n}\n\nfunction isMissingBrowserError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n // Playwright's \"Executable doesn't exist at …\" launch error fires\n // when the npm package is installed but the per-browser binary\n // hasn't been downloaded. The message stays stable across versions.\n return /Executable doesn'?t exist at/i.test(err.message);\n}\n\n/**\n * Run `npx playwright install <which>` and stream its output to the\n * sidecar's stderr so the operator can watch progress. Resolves on\n * exit-0; rejects with the tail of stderr otherwise.\n */\nfunction runPlaywrightInstall(which: BrowserKind): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn('npx', ['playwright', 'install', which], {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n let stderrTail = '';\n child.stdout.on('data', (chunk: Buffer) => process.stderr.write(chunk));\n child.stderr.on('data', (chunk: Buffer) => {\n process.stderr.write(chunk);\n stderrTail += chunk.toString('utf8');\n if (stderrTail.length > 4000) stderrTail = stderrTail.slice(-4000);\n });\n child.once('error', (err) => reject(err));\n child.once('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`exit ${code}: ${stderrTail.trim() || '(no stderr)'}`));\n });\n });\n}\n","export type BrowserKind = 'chromium' | 'firefox' | 'webkit';\n\nexport type ErrorKind = 'init' | 'navigation' | 'runtime' | 'timeout' | 'unknown';\n\nexport interface Req {\n readonly id: string;\n readonly method: string;\n readonly params?: Record<string, unknown>;\n}\n\nexport interface Ok {\n readonly id: string;\n readonly ok: true;\n readonly result?: unknown;\n /** One-shot human-readable note (e.g. \"Auto-installed Chromium\"). */\n readonly notice?: string;\n}\n\nexport interface Err {\n readonly id: string;\n readonly ok: false;\n readonly error: { message: string; kind: ErrorKind };\n}\n\nexport type Reply = Ok | Err;\n\nexport interface BrowserType {\n launch(opts: {\n headless: boolean;\n }): Promise<{ close(): Promise<void>; newContext(): Promise<unknown> }>;\n}\n\nexport interface PageHandle {\n goto(url: string, opts?: unknown): Promise<unknown>;\n click(selector: string, opts?: unknown): Promise<void>;\n fill(selector: string, value: string, opts?: unknown): Promise<void>;\n textContent(selector: string): Promise<string | null>;\n content(): Promise<string>;\n screenshot(opts?: unknown): Promise<Buffer>;\n evaluate(fn: string): Promise<unknown>;\n url(): string;\n close(): Promise<void>;\n // Coordinate-based input for the live browser surface (loosely typed — the\n // real Playwright Page provides all of these).\n viewportSize(): { width: number; height: number } | null;\n readonly mouse: {\n click(x: number, y: number, opts?: unknown): Promise<void>;\n wheel(dx: number, dy: number): Promise<void>;\n };\n readonly keyboard: {\n press(key: string): Promise<void>;\n type(text: string): Promise<void>;\n };\n}\n\n/** Minimal slice of Playwright's `Request` used by the navigation SSRF guard. */\nexport interface RouteRequest {\n url(): string;\n isNavigationRequest(): boolean;\n}\n\n/** Minimal slice of Playwright's `Route` used by the navigation SSRF guard. */\nexport interface RouteHandle {\n request(): RouteRequest;\n abort(errorCode?: string): Promise<void>;\n continue(): Promise<void>;\n}\n\n/** Minimal slice of a Chrome DevTools Protocol session (Playwright's\n * `CDPSession`) — used for the live browser-surface screencast. */\nexport interface CDPSession {\n send(method: string, params?: Record<string, unknown>): Promise<unknown>;\n on(event: string, handler: (params: unknown) => void): void;\n detach(): Promise<void>;\n}\n\nexport interface PlaywrightHandle {\n // Loosely typed so we can avoid importing the playwright types at compile time —\n // they're an optional peer dependency.\n readonly browser: { close(): Promise<void> };\n readonly context: {\n newPage(): Promise<unknown>;\n close(): Promise<void>;\n /** Optional because the type is a loose projection; real Playwright contexts always have it. */\n route?(pattern: string, handler: (route: RouteHandle) => Promise<void> | void): Promise<void>;\n /** Chromium-only CDP session for screencast (the live browser surface). */\n newCDPSession?(page: PageHandle): Promise<CDPSession>;\n };\n readonly page: PageHandle;\n}\n\nexport function errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nexport function badParams(msg: string): Error {\n const e = new Error(msg);\n (e as Error & { kind?: string }).kind = 'runtime';\n return e;\n}\n","/**\n * JSON-RPC dispatch table for the sidecar. Each `method` here corresponds\n * one-to-one with the wire-format methods documented in `sidecar.ts`.\n */\n\nimport { assertPublicUrl } from '../ssrf-guard.js';\nimport { importPlaywright, launchWithAutoInstall } from './install.js';\nimport {\n badParams,\n errMsg,\n type BrowserKind,\n type CDPSession,\n type Err,\n type PlaywrightHandle,\n type Reply,\n type Req,\n} from './types.js';\n\nexport interface SidecarState {\n handle: PlaywrightHandle | null;\n /**\n * Set after a successful auto-install of browser binaries so the next\n * tool result can carry a `notice` letting the user/model know the\n * one-time download happened. Cleared once the notice has been\n * delivered (handed to the reply once, then forgotten).\n */\n pendingInstallNotice: string | null;\n /**\n * Emit an unsolicited event line to the parent (no `id`). Set by the sidecar\n * main loop; used to push `screencastFrame` events for the live browser\n * surface. Undefined in unit tests that call `dispatch` directly.\n */\n emit?: (event: Record<string, unknown>) => void;\n /** Active CDP screencast session, when the browser surface is open. */\n cdp?: CDPSession | null;\n}\n\nasync function ensurePlaywright(\n state: SidecarState,\n opts: { browser?: BrowserKind; headless?: boolean },\n): Promise<PlaywrightHandle> {\n if (state.handle) return state.handle;\n const pw = await importPlaywright();\n const which = opts.browser ?? 'chromium';\n const browserType = pw[which];\n const { handle, installNotice } = await launchWithAutoInstall(browserType, which, opts.headless ?? true);\n state.handle = handle;\n if (installNotice) state.pendingInstallNotice = installNotice;\n return state.handle;\n}\n\nexport async function teardown(state: SidecarState): Promise<void> {\n await stopScreencast(state);\n if (!state.handle) return;\n try {\n await state.handle.context.close();\n await state.handle.browser.close();\n } catch {\n /* ignore */\n }\n state.handle = null;\n}\n\nasync function stopScreencast(state: SidecarState): Promise<void> {\n if (!state.cdp) return;\n try {\n await state.cdp.send('Page.stopScreencast');\n } catch {\n /* page may already be gone */\n }\n try {\n await state.cdp.detach();\n } catch {\n /* ignore */\n }\n state.cdp = null;\n}\n\nexport async function dispatch(state: SidecarState, req: Req): Promise<Reply> {\n try {\n return await dispatchInner(state, req);\n } catch (err) {\n const kind = (err as Error & { kind?: string }).kind;\n return {\n id: req.id,\n ok: false,\n error: { message: errMsg(err), kind: (kind as Err['error']['kind']) ?? 'unknown' },\n };\n }\n}\n\nasync function dispatchInner(state: SidecarState, req: Req): Promise<Reply> {\n switch (req.method) {\n case 'init': {\n const opts = (req.params ?? {}) as { browser?: BrowserKind; headless?: boolean };\n await ensurePlaywright(state, opts);\n return { id: req.id, ok: true, result: { ready: true } };\n }\n case 'goto': {\n const { url, waitUntil, timeoutMs } = (req.params ?? {}) as {\n url: string;\n waitUntil?: 'load' | 'domcontentloaded' | 'networkidle';\n timeoutMs?: number;\n };\n if (!url) throw badParams('url is required');\n // Defence-in-depth: the parent already runs the full SSRF guard before\n // sending this RPC, but the sidecar is a distinct process driven over\n // JSON-RPC, so re-check here rather than trust the caller to have\n // validated. Blocks file:// / javascript: schemes AND loopback/private/\n // link-local (incl. 169.254.169.254 metadata)/CGNAT targets, resolving\n // hostnames. Runs BEFORE ensurePlaywright so a blocked URL never\n // launches (or auto-installs) a browser.\n try {\n await assertPublicUrl(url, 'goto');\n } catch (err) {\n return { id: req.id, ok: false, error: { message: errMsg(err), kind: 'navigation' } };\n }\n const h = await ensurePlaywright(state, {});\n try {\n await h.page.goto(url, { waitUntil: waitUntil ?? 'domcontentloaded', timeout: timeoutMs ?? 30_000 });\n } catch (err) {\n return { id: req.id, ok: false, error: { message: errMsg(err), kind: 'navigation' } };\n }\n return { id: req.id, ok: true, result: { url: h.page.url() } };\n }\n case 'click': {\n const h = await ensurePlaywright(state, {});\n const { selector, timeoutMs } = (req.params ?? {}) as { selector: string; timeoutMs?: number };\n if (!selector) throw badParams('selector is required');\n await h.page.click(selector, { timeout: timeoutMs ?? 10_000 });\n return { id: req.id, ok: true };\n }\n case 'fill': {\n const h = await ensurePlaywright(state, {});\n const { selector, value, timeoutMs } = (req.params ?? {}) as {\n selector: string;\n value: string;\n timeoutMs?: number;\n };\n if (!selector) throw badParams('selector is required');\n await h.page.fill(selector, value ?? '', { timeout: timeoutMs ?? 10_000 });\n return { id: req.id, ok: true };\n }\n case 'text': {\n const h = await ensurePlaywright(state, {});\n const { selector } = (req.params ?? {}) as { selector?: string };\n if (selector) {\n const text = await h.page.textContent(selector);\n return { id: req.id, ok: true, result: text ?? '' };\n }\n // Whole-document text via evaluate\n const text = (await h.page.evaluate('document.body ? document.body.innerText : \"\"')) as string;\n return { id: req.id, ok: true, result: text };\n }\n case 'html': {\n const h = await ensurePlaywright(state, {});\n const html = await h.page.content();\n return { id: req.id, ok: true, result: html };\n }\n case 'screenshot': {\n const h = await ensurePlaywright(state, {});\n const { fullPage } = (req.params ?? {}) as { fullPage?: boolean };\n const buf = await h.page.screenshot({ fullPage: fullPage ?? false });\n return { id: req.id, ok: true, result: { mediaType: 'image/png', base64: buf.toString('base64') } };\n }\n case 'frame': {\n // Combined live-view frame for the browser SURFACE: a JPEG screenshot\n // plus the current url + viewport size, so the renderer can map clicks\n // back onto the page. One round-trip per frame.\n const h = await ensurePlaywright(state, {});\n const buf = await h.page.screenshot({ type: 'jpeg', quality: 55 });\n const vp = h.page.viewportSize() ?? { width: 1280, height: 720 };\n return {\n id: req.id,\n ok: true,\n result: {\n mediaType: 'image/jpeg',\n base64: buf.toString('base64'),\n url: h.page.url(),\n width: vp.width,\n height: vp.height,\n },\n };\n }\n case 'mouse': {\n const h = await ensurePlaywright(state, {});\n const { x, y } = (req.params ?? {}) as { x: number; y: number };\n await h.page.mouse.click(x, y);\n return { id: req.id, ok: true, result: { url: h.page.url() } };\n }\n case 'key': {\n const h = await ensurePlaywright(state, {});\n const { key } = (req.params ?? {}) as { key: string };\n if (!key) throw badParams('key is required');\n // A single printable char is typed (inserts it); a named key is pressed.\n if (key.length === 1) await h.page.keyboard.type(key);\n else await h.page.keyboard.press(key);\n return { id: req.id, ok: true };\n }\n case 'scroll': {\n const h = await ensurePlaywright(state, {});\n const { dy } = (req.params ?? {}) as { dy: number };\n await h.page.mouse.wheel(0, dy ?? 0);\n return { id: req.id, ok: true };\n }\n case 'startScreencast': {\n // Push live JPEG frames over CDP (`Page.screencastFrame`) for the browser\n // SURFACE — far smoother than polling screenshots. Chromium-only.\n const h = await ensurePlaywright(state, {});\n if (state.cdp) return { id: req.id, ok: true, result: { url: h.page.url() } };\n if (!h.context.newCDPSession) {\n return {\n id: req.id,\n ok: false,\n error: { message: 'screencast requires Chromium (CDP unavailable)', kind: 'runtime' },\n };\n }\n const client = await h.context.newCDPSession(h.page);\n state.cdp = client;\n client.on('Page.screencastFrame', (params) => {\n const p = params as { data: string; sessionId: number };\n state.emit?.({ event: 'screencastFrame', data: p.data, url: h.page.url() });\n // Ack so Chromium keeps streaming (it pauses without an ack).\n void client.send('Page.screencastAck', { sessionId: p.sessionId }).catch(() => undefined);\n });\n const { maxWidth, maxHeight } = (req.params ?? {}) as { maxWidth?: number; maxHeight?: number };\n await client.send('Page.startScreencast', {\n format: 'jpeg',\n quality: 50,\n maxWidth: maxWidth ?? 1280,\n maxHeight: maxHeight ?? 800,\n everyNthFrame: 1,\n });\n return { id: req.id, ok: true, result: { url: h.page.url() } };\n }\n case 'stopScreencast': {\n await stopScreencast(state);\n return { id: req.id, ok: true };\n }\n case 'eval': {\n const h = await ensurePlaywright(state, {});\n const { expression } = (req.params ?? {}) as { expression: string };\n if (!expression) throw badParams('expression is required');\n const value = await h.page.evaluate(expression);\n return { id: req.id, ok: true, result: value };\n }\n case 'url': {\n const h = await ensurePlaywright(state, {});\n return { id: req.id, ok: true, result: h.page.url() };\n }\n case 'close': {\n await teardown(state);\n return { id: req.id, ok: true };\n }\n default:\n return {\n id: req.id,\n ok: false,\n error: { message: `unknown method: ${req.method}`, kind: 'runtime' },\n };\n }\n}\n","#!/usr/bin/env node\n/**\n * Playwright sidecar — owns a single browser context. Speaks newline-delimited\n * JSON-RPC over stdio so the parent (`browser_session` tool) doesn't load\n * Playwright into its own process. Crash isolation, lazy install: the parent\n * only spawns this when the heavy tier is actually needed.\n *\n * Wire format (one line per message):\n * { \"id\": \"uuid\", \"method\": \"goto\"|\"click\"|\"fill\"|\"text\"|\"html\"|\"screenshot\"|\"eval\"|\"close\", \"params\": {...} }\n * { \"id\": \"uuid\", \"ok\": true, \"result\": ... }\n * { \"id\": \"uuid\", \"ok\": false, \"error\": { \"message\": \"...\", \"kind\": \"init\"|\"navigation\"|\"runtime\"|\"timeout\" } }\n *\n * Run with `node dist/sidecar.js` (the package's `bin` entry is\n * `moxxy-browser-sidecar`). Parent terminates by closing stdin or sending\n * `{method:'close'}`.\n */\n\nimport * as readline from 'node:readline';\nimport { dispatch, teardown, type SidecarState } from './sidecar/dispatch.js';\nimport { errMsg, type Reply, type Req } from './sidecar/types.js';\n\nconst state: SidecarState = { handle: null, pendingInstallNotice: null };\n\nfunction write(reply: Reply): void {\n // Drain the install-notice flag into the first reply that goes out\n // after the install completed, then clear it. Errors get the notice\n // too — sometimes the launch retry surfaces a different problem and\n // the user still wants to know we tried to install.\n if (state.pendingInstallNotice) {\n if (reply.ok) {\n reply = { ...reply, notice: state.pendingInstallNotice };\n } else {\n reply = {\n ...reply,\n error: {\n ...reply.error,\n message: `${state.pendingInstallNotice} Then: ${reply.error.message}`,\n },\n };\n }\n state.pendingInstallNotice = null;\n }\n process.stdout.write(JSON.stringify(reply) + '\\n');\n}\n\n/**\n * Tears down the browser context AND exits the process. Used both by\n * the explicit `close` RPC path and the parent-loss / stdin-close\n * paths so all cleanup goes through one routine.\n */\nasync function shutdownAndExit(code: number): Promise<void> {\n await teardown(state);\n process.exit(code);\n}\n\n/**\n * Parent watchdog: if the moxxy process that spawned this sidecar\n * disappears (crash, SIGKILL, terminal hangup), there's no stdin EOF\n * to rely on — orphan Chromium would keep running and chew CPU/memory\n * until the user notices. Poll the parent PID every few seconds and\n * self-terminate when it goes away.\n *\n * `process.kill(pid, 0)` is the POSIX trick for \"does this PID\n * exist?\" — no signal is actually delivered. Throws ESRCH when the\n * process is gone (or EPERM when it exists but we can't signal it —\n * still alive, so don't treat as gone).\n */\nfunction startParentWatchdog(): void {\n const parentPid = process.ppid;\n if (!parentPid || parentPid <= 1) return; // already orphaned (init), nothing to watch\n const interval = setInterval(() => {\n try {\n process.kill(parentPid, 0);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ESRCH') {\n clearInterval(interval);\n void shutdownAndExit(0);\n }\n // EPERM means the process exists but we can't signal it — still alive.\n }\n }, 2000);\n interval.unref?.(); // never block the event loop from exiting\n}\n\nlet queue: Promise<void> = Promise.resolve();\n\nasync function main(): Promise<void> {\n startParentWatchdog();\n // Lets dispatch handlers push unsolicited event lines (no `id`) — the live\n // browser surface's screencast frames ride this channel.\n state.emit = (event) => {\n process.stdout.write(JSON.stringify(event) + '\\n');\n };\n const rl = readline.createInterface({ input: process.stdin });\n rl.on('line', (line) => {\n if (!line.trim()) return;\n let req: Req;\n try {\n req = JSON.parse(line) as Req;\n } catch {\n write({ id: 'unknown', ok: false, error: { message: 'invalid JSON', kind: 'runtime' } });\n return;\n }\n if (!req.id || !req.method) {\n write({\n id: req.id ?? 'unknown',\n ok: false,\n error: { message: 'request requires { id, method }', kind: 'runtime' },\n });\n return;\n }\n // Sequentially serve requests on the single page. Parent can pipeline by\n // sending more requests; we serialize them inside the sidecar so a goto\n // doesn't race a click.\n queue = queue.then(async () => {\n const reply = await dispatch(state, req);\n write(reply);\n });\n });\n rl.once('close', () => {\n void shutdownAndExit(0);\n });\n // Defensive: if our stdout pipe breaks (parent died mid-write), Node\n // would otherwise throw an uncaught EPIPE on the next write. Treat\n // it as a signal to clean up gracefully instead.\n process.stdout.on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'EPIPE') {\n void shutdownAndExit(0);\n }\n });\n}\n\nmain().catch((err) => {\n process.stderr.write(`sidecar fatal: ${errMsg(err)}\\n`);\n void shutdownAndExit(1);\n});\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: browser
|
|
3
|
+
description: Open an in-window browser the user can watch, then navigate, click, and screenshot it for them.
|
|
4
|
+
triggers: ["open the browser", "in the browser", "go to this site", "navigate to", "show me the page", "screenshot the page", "click the button on", "fill the form on", "browse to"]
|
|
5
|
+
allowed-tools: [browser_session, web_fetch]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# In-window browser
|
|
9
|
+
|
|
10
|
+
The desktop has a **Browser** pane that shows a live view of the SAME browser
|
|
11
|
+
your `browser_session` tool drives. When you navigate or click via
|
|
12
|
+
`browser_session`, the user sees it happen in that pane — and they can take over
|
|
13
|
+
(click, type, scroll, change the URL) on the same page.
|
|
14
|
+
|
|
15
|
+
## When to use it
|
|
16
|
+
|
|
17
|
+
- The user asks you to open, browse, or act on a web page they want to watch.
|
|
18
|
+
- You need to see a JS-heavy/interactive page (not just fetch HTML) — use
|
|
19
|
+
`browser_session` (`goto`, `click`, `fill`, `screenshot`). For a plain GET,
|
|
20
|
+
`web_fetch` is lighter.
|
|
21
|
+
- You want to show the user a result visually rather than pasting text.
|
|
22
|
+
|
|
23
|
+
## How to use it well
|
|
24
|
+
|
|
25
|
+
- Drive the page with `browser_session`; the Browser pane reflects it live, so
|
|
26
|
+
you don't need to narrate every pixel — just say what you're doing.
|
|
27
|
+
- Take a `screenshot` when a visual check matters (did the page load, did the
|
|
28
|
+
form submit) or when the user asks to see it.
|
|
29
|
+
- Navigation is restricted to public http(s) origins (loopback / private /
|
|
30
|
+
metadata addresses are blocked); don't try to reach internal hosts.
|
|
31
|
+
- The user and you share one page — if they've navigated somewhere, read the
|
|
32
|
+
current state (`url`, `text`, `screenshot`) before acting.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: terminal
|
|
3
|
+
description: Run applications and shell commands for the user in a shared, user-visible terminal.
|
|
4
|
+
triggers: ["run a command", "run this", "open a terminal", "in the terminal", "execute", "start the server", "run the build", "npm run", "launch the app"]
|
|
5
|
+
allowed-tools: [terminal]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Shared terminal
|
|
9
|
+
|
|
10
|
+
You have a `terminal` tool that runs shell commands in a terminal the **user can
|
|
11
|
+
see and take over**. It is the same session the user has open in the desktop's
|
|
12
|
+
Terminal pane — so anything you run appears live in front of them.
|
|
13
|
+
|
|
14
|
+
## When to use it
|
|
15
|
+
|
|
16
|
+
- The user asks you to run, start, build, install, or launch something.
|
|
17
|
+
- You need to run a command on the user's behalf and show them the result.
|
|
18
|
+
- You want to demonstrate a command rather than just describe it.
|
|
19
|
+
|
|
20
|
+
Prefer `terminal` over any background/exec tool when the user should **watch**
|
|
21
|
+
what happens (builds, dev servers, installers, git operations they asked for).
|
|
22
|
+
|
|
23
|
+
## How to use it well
|
|
24
|
+
|
|
25
|
+
- Run ONE command per call; read its output before deciding the next step.
|
|
26
|
+
- The terminal is interactive and persistent: state (cwd, env, an activated
|
|
27
|
+
venv) carries across calls within the session.
|
|
28
|
+
- Long-running or interactive programs (a dev server, `top`) won't "finish" —
|
|
29
|
+
you'll get the output produced up to the timeout. Say so, and let the user
|
|
30
|
+
interact with the pane directly.
|
|
31
|
+
- Destructive commands still go through normal permission prompts. Don't try to
|
|
32
|
+
bypass them.
|
|
33
|
+
- Keep the user informed: briefly say what you're about to run and why.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moxxy/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "moxxy command-line binary. Subcommand dispatcher consuming the moxxy SDK.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"moxxy",
|
|
@@ -50,10 +50,11 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"zod": "^3.24.0",
|
|
53
|
-
"@moxxy/sdk": "0.
|
|
53
|
+
"@moxxy/sdk": "0.12.0"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
|
-
"@napi-rs/keyring": "^1.3.0"
|
|
56
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
57
|
+
"node-pty": "^1.0.0"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
60
|
"@clack/prompts": "^1.4.0",
|