mcp-scraper 0.3.26 → 0.3.28

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 (41) hide show
  1. package/dist/bin/api-server.cjs +2367 -305
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +2 -2
  4. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  5. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  6. package/dist/bin/mcp-scraper-cli.js +1 -1
  7. package/dist/bin/mcp-scraper-install.cjs +2 -2
  8. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-install.js +2 -2
  10. package/dist/bin/mcp-stdio-server.cjs +132 -9
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +13 -1357
  13. package/dist/bin/mcp-stdio-server.js.map +1 -1
  14. package/dist/bin/paa-harvest.cjs +1 -1
  15. package/dist/bin/paa-harvest.cjs.map +1 -1
  16. package/dist/bin/paa-harvest.js +1 -1
  17. package/dist/chunk-7MY2NSQX.js +7 -0
  18. package/dist/chunk-7MY2NSQX.js.map +1 -0
  19. package/dist/{chunk-WYCER2HW.js → chunk-H74L743B.js} +12 -11
  20. package/dist/chunk-H74L743B.js.map +1 -0
  21. package/dist/{chunk-2GHI2D6Q.js → chunk-NGD4ITAO.js} +1383 -17
  22. package/dist/chunk-NGD4ITAO.js.map +1 -0
  23. package/dist/{chunk-G7PQ64LM.js → chunk-QSSH4RCX.js} +2 -2
  24. package/dist/chunk-QSSH4RCX.js.map +1 -0
  25. package/dist/{chunk-DOBQN3EY.js → chunk-T3VZD2I4.js} +2 -2
  26. package/dist/{chunk-DOBQN3EY.js.map → chunk-T3VZD2I4.js.map} +1 -1
  27. package/dist/index.cjs +1 -1
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.js +1 -1
  30. package/dist/{server-HIGHKTEV.js → server-IDDX7KGT.js} +696 -42
  31. package/dist/server-IDDX7KGT.js.map +1 -0
  32. package/dist/{worker-UC6D2756.js → worker-NV4GTIRG.js} +3 -3
  33. package/docs/spec-kernel-computer-controls.md +166 -0
  34. package/package.json +1 -1
  35. package/dist/chunk-2GHI2D6Q.js.map +0 -1
  36. package/dist/chunk-G7PQ64LM.js.map +0 -1
  37. package/dist/chunk-MS2UZ6HR.js +0 -7
  38. package/dist/chunk-MS2UZ6HR.js.map +0 -1
  39. package/dist/chunk-WYCER2HW.js.map +0 -1
  40. package/dist/server-HIGHKTEV.js.map +0 -1
  41. /package/dist/{worker-UC6D2756.js.map → worker-NV4GTIRG.js.map} +0 -0
@@ -4,10 +4,10 @@ import {
4
4
  createHarvestAttemptRecorder,
5
5
  harvestProblemResponse,
6
6
  serializeHarvestProblem
7
- } from "./chunk-WYCER2HW.js";
7
+ } from "./chunk-H74L743B.js";
8
8
  import {
9
9
  harvest
10
- } from "./chunk-G7PQ64LM.js";
10
+ } from "./chunk-QSSH4RCX.js";
11
11
  import {
12
12
  browserServiceApiKey
13
13
  } from "./chunk-3PRO376E.js";
@@ -125,4 +125,4 @@ export {
125
125
  startWorker,
126
126
  tickOnce
127
127
  };
128
- //# sourceMappingURL=worker-UC6D2756.js.map
128
+ //# sourceMappingURL=worker-NV4GTIRG.js.map
@@ -0,0 +1,166 @@
1
+ # Spec — Tighten Kernel Computer Controls usage (human-like input on the hosted browser)
2
+
3
+ ## Correction to the original premise
4
+ An earlier assessment claimed "we drive the Kernel browser over CDP with raw Playwright — we never use the Computer Controls API." **That was wrong** (a bad grep — searched raw HTTP paths + `page.mouse`, missed the SDK method names). Ground truth in `src/services/browser-agent/browser-agent-service.ts`:
5
+
6
+ | Action | Implementation today | API used |
7
+ |---|---|---|
8
+ | screenshot | `k.browsers.computer.captureScreenshot` | ✅ Computer Controls |
9
+ | click | `k.browsers.computer.clickMouse` | ✅ Computer Controls |
10
+ | type | `k.browsers.computer.typeText` | ✅ Computer Controls |
11
+ | scroll | `k.browsers.computer.scroll` | ✅ Computer Controls |
12
+ | press | `k.browsers.computer.pressKey` | ✅ Computer Controls |
13
+ | moveMouse | `k.browsers.computer.moveMouse({smooth:true})` | ✅ defined… but never called |
14
+ | goto / read / locate / captureFanout | `playwrightChromium.connectOverCDP` | Playwright/CDP (DOM access — required, kept) |
15
+
16
+ So the interactive agent's **input + screenshot already follow Kernel's API**. The DOM-read path uses Playwright-over-CDP because Computer Controls cannot read the DOM / element coordinates — that path stays (and is the same free Playwright code used off-Kernel).
17
+
18
+ ## Dual-path principle (the rule, stated)
19
+ - **On Kernel (hosted browser):** drive *input* (move/click/type/scroll/press/screenshot) through **Computer Controls** (`k.browsers.computer.*`) — native OS input, no CDP input fingerprint, bezier curves, inter-keystroke delays. Read the DOM via Playwright-over-CDP (no Computer Controls equivalent).
20
+ - **Off Kernel (free local/HTTP path, `BrowserDriver` non-hosted, future local mode):** keep Playwright. It's free and there's no Computer Controls layer to use.
21
+
22
+ This spec changes only the Kernel input path. It does not touch Playwright scraping (`BrowserDriver`, reddit/serp/maps/fb deterministic tools) — their bottleneck is IP/challenge, not input cadence, and they barely click.
23
+
24
+ ## The two real gaps (verified)
25
+ 1. **Clicks teleport.** `click()` calls `clickMouse(x,y)` with no preceding move. `moveMouse({smooth:true})` exists (bezier) but is **called from nowhere** (`grep` confirms one definition, zero call sites). So every click is an instant cursor jump to the target — a behavioral bot signal Kernel's docs flag ("identical cursor paths, rapid mouse movements"). The bezier capability is built and unused.
26
+ 2. **Typing has no human cadence by default.** `typeText(id, text, delayMs?)` only passes `delay` if the *caller* supplies `body.delay` (`browser-agent-routes.ts:486`). The model never sends it → `delay` is `undefined` → instant uniform typing ("uniform typing speeds" — another flagged signal).
27
+
28
+ ## Changes
29
+
30
+ ### 1. `src/services/browser-agent/browser-agent-service.ts`
31
+
32
+ **Add a constant + jitter helper near the top of the action section (after `client()`):**
33
+ ```ts
34
+ const HUMAN_TYPE_DELAY_MS = 55
35
+ function jitter(base: number, spread: number): number {
36
+ return Math.max(1, Math.round(base + (Math.random() * 2 - 1) * spread))
37
+ }
38
+ ```
39
+
40
+ **`click()` — move (bezier) to the target before clicking.** Replace the body:
41
+ ```ts
42
+ export async function click(
43
+ runtimeSessionId: string,
44
+ x: number,
45
+ y: number,
46
+ opts: { button?: 'left' | 'right' | 'middle'; numClicks?: number } = {},
47
+ ): Promise<void> {
48
+ const k = client()
49
+ await k.browsers.computer.moveMouse(runtimeSessionId, { x, y, smooth: true })
50
+ await k.browsers.computer.clickMouse(runtimeSessionId, {
51
+ x,
52
+ y,
53
+ button: opts.button ?? 'left',
54
+ ...(opts.numClicks ? { num_clicks: opts.numClicks } : {}),
55
+ })
56
+ }
57
+ ```
58
+ (Optional optimization — collapse the two round-trips into one with `computer.batch` if the installed SDK exposes it: `await k.browsers.computer.batch(runtimeSessionId, { actions: [{ type: 'move_mouse', x, y, smooth: true }, { type: 'click_mouse', x, y, button: opts.button ?? 'left', num_clicks: opts.numClicks ?? 1 }] })`. Verify the SDK method/shape before using; if unavailable, keep the two sequential calls above.)
59
+
60
+ **`typeText()` — default a jittered human delay when the caller gives none:**
61
+ ```ts
62
+ export async function typeText(runtimeSessionId: string, text: string, delayMs?: number): Promise<void> {
63
+ const k = client()
64
+ const delay = typeof delayMs === 'number' ? delayMs : jitter(HUMAN_TYPE_DELAY_MS, 30)
65
+ await k.browsers.computer.typeText(runtimeSessionId, { text, delay })
66
+ }
67
+ ```
68
+
69
+ No route changes required — `browser-agent-routes.ts` already forwards `x/y`, `button`, `num_clicks`, and an optional `delay`; the new behavior is purely in the service defaults. The MCP tool schemas/descriptions need no change (the human-like behavior is automatic; callers still just pass x/y/text).
70
+
71
+ ### 2. No change to
72
+ - `goto` / `readPage` / `locatePageTargets` / `captureFanout` — DOM access over CDP is required and free-path-shared. Leave as-is.
73
+ - `BrowserDriver` and all deterministic scrapers (reddit, serp, maps, fb, instagram, youtube) — Playwright stays.
74
+ - The free/local browser path — Playwright stays.
75
+
76
+ ## Verification
77
+ 1. `grep -n "moveMouse" src/services/browser-agent/browser-agent-service.ts` → now **2** hits (definition + the new call in `click`).
78
+ 2. `npm run build` clean.
79
+ 3. Deploy prod; open a session and drive it:
80
+ - `POST /agent/sessions {url:"https://example.com"}` → `POST /agent/sessions/:id/screenshot` (note an element's x/y) → `POST /agent/sessions/:id/click {x,y}` → screenshot again: the click lands (move+click path works).
81
+ - `POST /agent/sessions/:id/type {text:"hello world"}` (no `delay`) succeeds — typing now carries the default jittered cadence server-side.
82
+ - `DELETE /agent/sessions/:id`.
83
+ 4. Behavioral check on a detection-sensitive target (manual): drive a known bot-walled login form; clicks should approach via curve and typing should be paced (observable in the live-view / replay).
84
+
85
+ ## Release
86
+ - One file changed (`browser-agent-service.ts`). The behavior ships server-side → live on Vercel deploy; **no npm republish required** (the stdio package is a thin client; tool schemas/descriptions are unchanged).
87
+ - Commit on `main`: `feat(browser): human-like input on Kernel — bezier move-before-click + default typing cadence`.
88
+
89
+ ---
90
+
91
+ # Phase 2 — Move the DOM-read path to the Playwright Execution API (in-VM + Patchright)
92
+
93
+ ## Why (revised from "defer")
94
+ The read path (`goto`, `readPage`, `locatePageTargets`, `captureFanout`) uses `playwrightChromium.connectOverCDP(cdpWsUrl)` from our serverless function — **outside** the Kernel VM. Kernel's recommended-practices table explicitly says *"Prefer our Playwright Execution API or Computer Controls API over self-hosted Playwright/Puppeteer,"* and the bot-detection guide states the Execution API *"runs in the same VM as the browser, ensuring headers, user-agent strings, and environment match. Kernel automatically applies Patchright to remove automation fingerprints, including headless indicators."* So external CDP is the residual detection signal, and this is the documented fix — not optional polish.
95
+
96
+ ## The API
97
+ `POST /browsers/{id}/playwright/execute` — body `{ code: string, timeout_sec?: 1..300 }`. The `code` runs **in-VM** inside a function with `page`, `context`, `browser` in scope; `return` a value. Response `{ success, result, error, stdout, stderr }`. SDK call (verify exact name against installed `@onkernel/sdk`): `k.browsers.playwright.execute(runtimeSessionId, { code, timeout_sec })`.
98
+
99
+ ## Mechanic
100
+ Our read functions already do their work via `page.evaluate(FN)` over a `connectOverCDP` page. The migration is **mechanical**: take the existing in-page extraction JS (the function bodies passed to `page.evaluate`) and ship them as the `code` string to `execute`, returning the same shape — but now running in-VM with Patchright instead of over external CDP.
101
+
102
+ ### `src/services/browser-agent/browser-agent-service.ts`
103
+ Add a helper:
104
+ ```ts
105
+ async function runInVm<T>(runtimeSessionId: string, code: string, timeoutSec = 30): Promise<T> {
106
+ const k = client()
107
+ const res = await k.browsers.playwright.execute(runtimeSessionId, { code, timeout_sec: timeoutSec })
108
+ if (!res.success) throw new Error(res.error || 'playwright/execute failed')
109
+ return res.result as T
110
+ }
111
+ ```
112
+ Then rewrite each read function to call `runInVm` with its existing extraction logic as a code string instead of `connectOverCDP`:
113
+ - **`goto(runtimeSessionId, url)`** → `code`: `` await page.goto(${JSON.stringify(url)}, { waitUntil: 'domcontentloaded', timeout: 45000 }); return { url: page.url(), title: await page.title() }; `` (signature changes from `cdpWsUrl` to `runtimeSessionId`).
114
+ - **`readPage`** → move the current element/text-collection `page.evaluate` body into the `code` string; `return` the same `{ url, title, text, elements }` object.
115
+ - **`locatePageTargets`** → same: the selector/text-bounds logic becomes the `code` body, `return` the bounds array.
116
+ - **`captureFanout`** → port its capture logic; if it relies on response listeners over CDP, evaluate whether it can run in-VM or must stay on CDP (the fan-out hook listens to network events — this one may legitimately stay on CDP; verify before migrating).
117
+
118
+ ### Routes (`src/api/browser-agent-routes.ts`)
119
+ The read/locate/goto routes currently pass `row.cdp_ws_url` to the service. Change those call sites to pass `row.runtime_session_id`. No other route logic changes.
120
+
121
+ ### Keep CDP only where required
122
+ If `captureFanout` needs live network-event listeners (CDP-only), leave it on `connectOverCDP`. Everything else moves in-VM.
123
+
124
+ ## Dual-path still holds
125
+ - **On Kernel:** input → Computer Controls (Phase 1); DOM read → Playwright Execution API (Phase 2). Zero external CDP except any network-listener edge case.
126
+ - **Off Kernel (free/local):** unchanged local Playwright.
127
+
128
+ ## Verification (Phase 2)
129
+ 1. `grep -c connectOverCDP src/services/browser-agent/browser-agent-service.ts` → drops to 0 (or 1 if captureFanout stays).
130
+ 2. Build clean.
131
+ 3. Drive a session: `goto` → `read` → `locate` return the same shapes as before (regression check against current output).
132
+ 4. On a detection-sensitive page, confirm reads no longer attach an external CDP client (the session shows only in-VM execution + Computer Controls input).
133
+
134
+ ## Release (Phase 2)
135
+ - Same file (+ route call-site edits). Server-side → ships on deploy; no npm republish.
136
+ - Separate commit from Phase 1 so each is independently revertible: `feat(browser): read DOM via Kernel Playwright Execution API (in-VM, Patchright) instead of external CDP`.
137
+
138
+ ## Sequencing
139
+ Ship **Phase 1 first** (tiny, pure win), validate, then **Phase 2** (mechanical but touches 3–4 functions + their routes; needs a regression pass on read/locate output shape). Don't bundle — Phase 2 has real surface area.
140
+
141
+ ## Still out of scope
142
+ - Migrating the **deterministic scrapers** (`BrowserDriver` reddit/serp/maps/fb) from `connectOverCDP` to Playwright Execution API. Same benefit would apply, but their bottleneck was IP/challenge (solved) and they're a larger blast radius. Flag as Phase 3 if detection on those tools resurfaces.
143
+ - **Batch beyond move+click** — only if a concrete need appears.
144
+
145
+ ---
146
+
147
+ # Other Kernel APIs — assessment + findings (2026-06-30)
148
+
149
+ Reviewed the full Kernel API reference (browsers, playwright, computer-controls, filesystem, processes). Findings:
150
+
151
+ ## TESTED & REJECTED — browser curl for Reddit
152
+ **Hypothesis:** `client.browsers.curl(id, {url})` (`POST /browsers/{id}/curl`) sends an HTTP request through Chrome's stack (TLS fingerprint + cookies + proxy), so curling `old.reddit/...json` through a residential-proxied browser would return the comment tree cheaply, no render.
153
+ **Result: FAILED.** Both residential-proxied and no-proxy curl returned **HTTP 403 / Reddit block page** (no JSON). Reason: curl does NOT execute JavaScript, and Reddit's wall is a **JS challenge** that only a *rendered* browser solves. `reddit_thread` therefore MUST stay headful-render (the render is what beats the challenge; the ~$0.0009 cost is justified). **Do not re-attempt curl for Reddit.**
154
+ **Curl is still valid** for genuine JSON/REST APIs with no JS challenge (inherits the browser's authenticated session + residential IP). Keep as a back-pocket tier; not a current build item.
155
+
156
+ ## REJECTED — do NOT run ffmpeg/media in the Kernel VM (cost-wrong)
157
+ Initially flagged process-exec/filesystem for in-VM ffmpeg muxing. **Retracted — it's the wrong place cost-wise.** A Kernel browser VM bills $0.0001333/s (headful); muxing is pure CPU work unrelated to browsing, so running it there is the most expensive option. And we don't need it for product value:
158
+ - **Transcription already runs outside Kernel** via fal.ai wizper, which takes an `audio_url` and fetches it itself (`media-transcription.ts`) — no download, no ffmpeg, no Kernel. Cheap and correct.
159
+ - **Muxing** (`VideoMixer.ts`) is an optional convenience that gracefully degrades to returning separate track URLs when ffmpeg is absent.
160
+ - The `instagram_media` ENOENT was the **Vercel serverless read-only filesystem** (`mkdir ~/Downloads/...`), NOT a Kernel gap. Fix = write to `/tmp` or return track URLs; if muxing must be reliable, run ffmpeg on a cheap non-serverless worker — never in Kernel.
161
+ **Conclusion: keep all media transcode/transcription OUTSIDE Kernel.** Process-exec/filesystem APIs have no compelling MCP Scraper use today.
162
+
163
+ ## LOW / skip unless needed
164
+ - computer-controls extras: `drag` (slider captchas), `clipboard` read/write, `set_cursor`, `get_cursor_position` — situational agent polish.
165
+ - browsers: create/get/list/delete/update (already used), ad-hoc extension upload (niche), load-extensions.
166
+ - filesystem watch/stream, process async/PTY/signals — only for long-running in-VM jobs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-scraper",
3
- "version": "0.3.26",
3
+ "version": "0.3.28",
4
4
  "description": "MCP server for MCP Scraper web intelligence tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",