diff options
| author | amankumarsingh77 <amankumarsingh7702@gmail.com> | 2026-07-28 22:41:18 +0530 |
|---|---|---|
| committer | Aman Kumar <145339995+amankumarsingh77@users.noreply.github.com> | 2026-08-02 19:12:46 +0530 |
| commit | dcb79fc62bdd4d9ec0cbdb2c97d3eb2b2dacdf22 (patch) | |
| tree | 1b7d35ec51db118b06811aa72ea6cc36fdad948e /src/domains | |
| parent | b484c017807acb902eaaaa4e242f8b79f8e8eba6 (diff) | |
refactor: merge nine files whose reason to exist was gone
src/ goes from 84 files to 75 at the same line count — every merge below
had exactly one consumer.
cdp/event-queue.ts and cdp/pending-requests.ts become daemon/queue.ts.
I split these in the daemon-internals commit on the grounds that they
held two unrelated concerns, but the transport commit had already deleted
the second transport, so both halves had one consumer and the split
bought nothing. They also sat in cdp/ while only daemon/ used them.
cdp/transport.ts held only the CdpTransport interface after the second
transport went; five of its six consumers import it type-only, so it
moves to cdp/types.ts. cdp/evaluate.ts moves into cdp/session.ts rather
than into its lone consumer isolated-tab.ts — it reaches session.call
directly, so under src/domains/ it would trip the raw-cdp-call rule.
util/keycodes.ts and util/time.ts fold into their only callers
(keyboard.ts, page.ts); util/bytes.ts into util/truncate.ts, which
already owns output-size formatting; readpage/capture.ts and
search/extract.ts into the tools beside them; profile/picker.ts into
profile/command.ts.
Kept deliberately: mutex.ts and domains/target.ts each have their own
test suite, and folding target.ts into click.ts would make a tool file
export a helper. sharp-shim.ts is an isolation boundary carrying two
allowlisted casts. result.ts, guards.ts, js-template.ts and paths.ts are
small but have 44, 12, 8 and 3 consumers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'src/domains')
| -rw-r--r-- | src/domains/isolated-tab.ts | 2 | ||||
| -rw-r--r-- | src/domains/js.ts | 2 | ||||
| -rw-r--r-- | src/domains/keyboard.ts | 10 | ||||
| -rw-r--r-- | src/domains/network.ts | 2 | ||||
| -rw-r--r-- | src/domains/page.ts | 18 | ||||
| -rw-r--r-- | src/domains/readpage/capture.ts | 34 | ||||
| -rw-r--r-- | src/domains/readpage/read-page.ts | 36 | ||||
| -rw-r--r-- | src/domains/search/extract.ts | 28 | ||||
| -rw-r--r-- | src/domains/search/web-search.ts | 30 |
9 files changed, 93 insertions, 69 deletions
diff --git a/src/domains/isolated-tab.ts b/src/domains/isolated-tab.ts index 103c9cc..b76b101 100644 --- a/src/domains/isolated-tab.ts +++ b/src/domains/isolated-tab.ts @@ -2,7 +2,7 @@ import type { BrowserClient } from "../client"; import { type Result, err, ok } from "../util/result"; import { safeJs } from "../util/js-template"; import { ensureHarnessWindow, openHarnessTab } from "../cdp/target-factory"; -import { evaluateJson } from "../cdp/evaluate"; +import { evaluateJson } from "../cdp/session"; import type { ToolErr } from "../util/tool"; import { cdpCallBrowser, cdpCallOnTarget, cdpErrToToolErr, evalJs } from "./cdp-call"; diff --git a/src/domains/js.ts b/src/domains/js.ts index d2588d0..6ba21cd 100644 --- a/src/domains/js.ts +++ b/src/domains/js.ts @@ -6,7 +6,7 @@ import { Type } from "typebox"; import { Markdown, Text } from "@mariozechner/pi-tui"; import { getMarkdownTheme, keyHint } from "@mariozechner/pi-coding-agent"; import { type Result, err, ok } from "../util/result"; -import { formatBytes } from "../util/bytes"; +import { formatBytes } from "../util/truncate"; import { defineBrowserTool, type ToolErr, type ToolOk } from "../util/tool"; import { evalJs } from "./cdp-call"; import { applyTruncation } from "../util/truncate"; diff --git a/src/domains/keyboard.ts b/src/domains/keyboard.ts index 50bee62..17b56e7 100644 --- a/src/domains/keyboard.ts +++ b/src/domains/keyboard.ts @@ -1,11 +1,19 @@ import { Type } from "typebox"; import { safeJs } from "../util/js-template"; -import { virtualKeyCode } from "../util/keycodes"; import { type Result, err, ok } from "../util/result"; import { defineBrowserTool, type ToolErr, type ToolOk } from "../util/tool"; import { cdpCall, evalJs } from "./cdp-call"; import { resolveRefToObjectId } from "./ref-resolve"; +const VIRTUAL_KEY_CODES: Readonly<Record<string, number>> = { + Enter: 13, Tab: 9, Backspace: 8, Escape: 27, Delete: 46, " ": 32, + ArrowLeft: 37, ArrowUp: 38, ArrowRight: 39, ArrowDown: 40, + Home: 36, End: 35, PageUp: 33, PageDown: 34, +}; + +const virtualKeyCode = (key: string): number => + VIRTUAL_KEY_CODES[key] ?? (key.length === 1 ? key.charCodeAt(0) : 0); + const TypeArgs = Type.Object({ text: Type.String({ description: "Text to type" }), }); diff --git a/src/domains/network.ts b/src/domains/network.ts index aeb1aa6..a34dde3 100644 --- a/src/domains/network.ts +++ b/src/domains/network.ts @@ -2,7 +2,7 @@ import { Type } from "typebox"; import { Markdown, Text } from "@mariozechner/pi-tui"; import { getMarkdownTheme, keyHint } from "@mariozechner/pi-coding-agent"; import { type Result, err, ok } from "../util/result"; -import { formatBytes } from "../util/bytes"; +import { formatBytes } from "../util/truncate"; import { defineBrowserTool, type ToolErr, type ToolOk } from "../util/tool"; import { cdpCall } from "./cdp-call"; import { applyTruncation } from "../util/truncate"; diff --git a/src/domains/page.ts b/src/domains/page.ts index c202ef1..909963f 100644 --- a/src/domains/page.ts +++ b/src/domains/page.ts @@ -2,9 +2,25 @@ import { Type } from "typebox"; import { type Result, err, ok } from "../util/result"; import { defineBrowserTool, type ToolErr, type ToolOk } from "../util/tool"; import { evalJs } from "./cdp-call"; -import { sleep } from "../util/time"; import { safeJs } from "../util/js-template"; +const sleep = (ms: number, signal?: AbortSignal): Promise<void> => + new Promise((resolve, reject) => { + if (signal?.aborted) { + reject(signal.reason ?? new Error("aborted")); + return; + } + const onAbort = (): void => { + clearTimeout(timer); + reject(signal?.reason ?? new Error("aborted")); + }; + const timer = setTimeout(() => { + signal?.removeEventListener("abort", onAbort); + resolve(); + }, ms); + signal?.addEventListener("abort", onAbort, { once: true }); + }); + export const pageInfoTool = defineBrowserTool({ name: "browser_page_info", label: "Browser Page Info", diff --git a/src/domains/readpage/capture.ts b/src/domains/readpage/capture.ts deleted file mode 100644 index b0b7ae0..0000000 --- a/src/domains/readpage/capture.ts +++ /dev/null @@ -1,34 +0,0 @@ -const BODY_TEXT_LIMIT = 20_000; - -export const buildPageCaptureExpr = (): string => ` - (() => { - const BOILERPLATE = new Set(['NAV', 'HEADER', 'FOOTER', 'ASIDE']); - const inBoilerplate = (el) => { - for (let n = el; n; n = n.parentElement) { - if (BOILERPLATE.has(n.tagName)) return true; - const role = n.getAttribute && n.getAttribute('role'); - if (role === 'navigation' || role === 'banner' || role === 'contentinfo') return true; - } - return false; - }; - const kindOf = (tag) => { - if (/^H[1-6]$/.test(tag)) return 'heading'; - if (tag === 'LI') return 'listitem'; - if (tag === 'BLOCKQUOTE') return 'blockquote'; - if (tag === 'P') return 'paragraph'; - return 'other'; - }; - const selector = 'p, h1, h2, h3, h4, h5, h6, li, blockquote'; - const root = document.querySelector('article, main, [role=main]') || document.body; - const blocks = []; - for (const el of root.querySelectorAll(selector)) { - const text = (el.innerText || '').replace(/\\s+/g, ' ').trim(); - if (!text) continue; - let linkTextLength = 0; - for (const a of el.querySelectorAll('a')) linkTextLength += (a.innerText || '').length; - blocks.push({ kind: kindOf(el.tagName), text, linkTextLength, inBoilerplate: inBoilerplate(el) }); - } - const bodyText = (document.body.innerText || '').replace(/\\n{3,}/g, '\\n\\n').slice(0, ${BODY_TEXT_LIMIT}); - return JSON.stringify({ url: location.href, title: document.title, blocks, bodyText }); - })() -`; diff --git a/src/domains/readpage/read-page.ts b/src/domains/readpage/read-page.ts index 9d46c7e..fadee38 100644 --- a/src/domains/readpage/read-page.ts +++ b/src/domains/readpage/read-page.ts @@ -15,7 +15,6 @@ import { waitForIsolatedLoad, } from "../isolated-tab"; import { renderExpandableText } from "../render"; -import { buildPageCaptureExpr } from "./capture"; import { extractReadable, type PageCapture, type ReadablePage } from "./readability"; const LOAD_TIMEOUT_MS = 15_000; @@ -49,6 +48,41 @@ const PageCaptureSchema = Type.Object({ const pageCaptureValidator = Compile(PageCaptureSchema); +const BODY_TEXT_LIMIT = 20_000; + +const buildPageCaptureExpr = (): string => ` + (() => { + const BOILERPLATE = new Set(['NAV', 'HEADER', 'FOOTER', 'ASIDE']); + const inBoilerplate = (el) => { + for (let n = el; n; n = n.parentElement) { + if (BOILERPLATE.has(n.tagName)) return true; + const role = n.getAttribute && n.getAttribute('role'); + if (role === 'navigation' || role === 'banner' || role === 'contentinfo') return true; + } + return false; + }; + const kindOf = (tag) => { + if (/^H[1-6]$/.test(tag)) return 'heading'; + if (tag === 'LI') return 'listitem'; + if (tag === 'BLOCKQUOTE') return 'blockquote'; + if (tag === 'P') return 'paragraph'; + return 'other'; + }; + const selector = 'p, h1, h2, h3, h4, h5, h6, li, blockquote'; + const root = document.querySelector('article, main, [role=main]') || document.body; + const blocks = []; + for (const el of root.querySelectorAll(selector)) { + const text = (el.innerText || '').replace(/\\s+/g, ' ').trim(); + if (!text) continue; + let linkTextLength = 0; + for (const a of el.querySelectorAll('a')) linkTextLength += (a.innerText || '').length; + blocks.push({ kind: kindOf(el.tagName), text, linkTextLength, inBoilerplate: inBoilerplate(el) }); + } + const bodyText = (document.body.innerText || '').replace(/\\n{3,}/g, '\\n\\n').slice(0, ${BODY_TEXT_LIMIT}); + return JSON.stringify({ url: location.href, title: document.title, blocks, bodyText }); + })() +`; + const toPageCapture = (raw: unknown): PageCapture | undefined => { if (typeof raw === "string") { const parsed = parseJson(raw, pageCaptureValidator); diff --git a/src/domains/search/extract.ts b/src/domains/search/extract.ts deleted file mode 100644 index 4d9e892..0000000 --- a/src/domains/search/extract.ts +++ /dev/null @@ -1,28 +0,0 @@ -const PAGE_TEXT_LIMIT = 4000; - -export const buildSerpExtractionExpr = (): string => ` - (() => { - const main = document.querySelector('#search, #rso, #main') || document.body; - const headings = Array.from(main.querySelectorAll('a h3')); - const seen = new Set(); - const anchors = []; - for (const h3 of headings) { - const a = h3.closest('a[href]'); - if (!a) continue; - const href = a.href; - if (!href || seen.has(href)) continue; - seen.add(href); - const container = - a.closest('div.g') || - a.closest('div[data-hveid]') || - a.parentElement?.parentElement || - a.parentElement; - const containerText = container ? container.innerText || '' : ''; - const heading = h3.innerText || h3.textContent || ''; - const snippet = containerText.replace(heading, '').replace(/\\s+/g, ' ').trim().slice(0, 500); - anchors.push({ href, heading, snippet }); - } - const pageText = (document.body.innerText || '').slice(0, ${PAGE_TEXT_LIMIT}); - return JSON.stringify({ anchors, pageText }); - })() -`; diff --git a/src/domains/search/web-search.ts b/src/domains/search/web-search.ts index 9327f15..553135c 100644 --- a/src/domains/search/web-search.ts +++ b/src/domains/search/web-search.ts @@ -13,7 +13,6 @@ import { waitForIsolatedLoad, } from "../isolated-tab"; import { renderExpandableText } from "../render"; -import { buildSerpExtractionExpr } from "./extract"; import { classifySerp, parseGoogleSerp, type SearchResult, type SerpExtraction } from "./google-serp"; const LOAD_TIMEOUT_MS = 15_000; @@ -25,6 +24,35 @@ const WebSearchArgs = Type.Object({ ), }); +const PAGE_TEXT_LIMIT = 4000; + +const buildSerpExtractionExpr = (): string => ` + (() => { + const main = document.querySelector('#search, #rso, #main') || document.body; + const headings = Array.from(main.querySelectorAll('a h3')); + const seen = new Set(); + const anchors = []; + for (const h3 of headings) { + const a = h3.closest('a[href]'); + if (!a) continue; + const href = a.href; + if (!href || seen.has(href)) continue; + seen.add(href); + const container = + a.closest('div.g') || + a.closest('div[data-hveid]') || + a.parentElement?.parentElement || + a.parentElement; + const containerText = container ? container.innerText || '' : ''; + const heading = h3.innerText || h3.textContent || ''; + const snippet = containerText.replace(heading, '').replace(/\\s+/g, ' ').trim().slice(0, 500); + anchors.push({ href, heading, snippet }); + } + const pageText = (document.body.innerText || '').slice(0, ${PAGE_TEXT_LIMIT}); + return JSON.stringify({ anchors, pageText }); + })() +`; + const buildSerpUrl = (query: string, limit: number): string => `https://www.google.com/search?q=${encodeURIComponent(query)}&num=${limit}&hl=en`; |
