mcp-scraper 0.95.2 → 0.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/README.md +19 -3
  3. package/dist/actions-YCMJY3PF.js +1 -0
  4. package/dist/artifacts-4ZDNP2JW.js +1 -0
  5. package/dist/artifacts-N62OPIT5.js +1 -0
  6. package/dist/artifacts-PSADD5OC.js +1 -0
  7. package/dist/artifacts-RUR3OHAK.js +1 -0
  8. package/dist/bin/api-server.js +1 -1
  9. package/dist/bin/mcp-scraper-cli.js +22 -22
  10. package/dist/bin/mcp-scraper-core.js +1 -1
  11. package/dist/bin/mcp-scraper-install.js +1 -1
  12. package/dist/bin/mcp-stdio-server.js +1 -1
  13. package/dist/bin/paa-harvest.js +1 -1
  14. package/dist/blob-store-GIH26EDO.js +1 -0
  15. package/dist/bundle-TTJP6DHU.js +1 -0
  16. package/dist/chunk-24YDQ3EO.js +5 -0
  17. package/dist/chunk-2PRH4Z3N.js +181 -0
  18. package/dist/chunk-3F7AFM53.js +1 -0
  19. package/dist/chunk-4HZXATHB.js +27 -0
  20. package/dist/chunk-55ZANTHL.js +3 -0
  21. package/dist/chunk-5BKSKLHX.js +1 -0
  22. package/dist/chunk-63VWBMYR.js +3 -0
  23. package/dist/chunk-6N5EC7ZO.js +1 -0
  24. package/dist/chunk-6SNOUSSZ.js +182 -0
  25. package/dist/chunk-7POHNTVW.js +1 -0
  26. package/dist/chunk-7SR7MEU5.js +62 -0
  27. package/dist/chunk-7UHDOIUE.js +1 -0
  28. package/dist/chunk-ARSQZCTJ.js +945 -0
  29. package/dist/chunk-B3JRL45T.js +1 -0
  30. package/dist/chunk-B4ZKPLSP.js +1 -0
  31. package/dist/chunk-C7MGJUTU.js +1 -0
  32. package/dist/{chunk-W2BVJ7S2.js → chunk-CNSVZTTT.js} +4 -4
  33. package/dist/chunk-DGAW7FD7.js +12 -0
  34. package/dist/chunk-FRXKMOXI.js +41 -0
  35. package/dist/chunk-GVYGAZV7.js +1 -0
  36. package/dist/chunk-HCK6PLM3.js +3 -0
  37. package/dist/{chunk-Y3Q4FIKK.js → chunk-HJIOH7HF.js} +199 -84
  38. package/dist/chunk-HNNYXMSI.js +1 -0
  39. package/dist/chunk-HUNSEGUN.js +57 -0
  40. package/dist/chunk-IFPION5D.js +121 -0
  41. package/dist/{chunk-MC4W6TZT.js → chunk-JAV5V3JT.js} +41 -8
  42. package/dist/chunk-JMZIDHYQ.js +2 -0
  43. package/dist/{chunk-ENZUJEIM.js → chunk-JW4GDY7S.js} +1 -1
  44. package/dist/chunk-KXJCGV3H.js +1 -0
  45. package/dist/chunk-LFRAA3MY.js +4 -0
  46. package/dist/chunk-MVMWPKRC.js +2 -0
  47. package/dist/chunk-NALXKTUU.js +1 -0
  48. package/dist/chunk-NNOX3FYG.js +198 -0
  49. package/dist/chunk-OB6E6UTD.js +1 -0
  50. package/dist/chunk-OILOHQNL.js +2 -0
  51. package/dist/chunk-OLYPLX5C.js +1 -0
  52. package/dist/chunk-QER2ELZP.js +1 -0
  53. package/dist/chunk-QWE7VKIX.js +1 -0
  54. package/dist/chunk-R5LJL6IR.js +225 -0
  55. package/dist/chunk-RDHGQ2WZ.js +1 -0
  56. package/dist/chunk-SDQW7IJN.js +1 -0
  57. package/dist/chunk-SEZETSB3.js +1 -0
  58. package/dist/chunk-SNJR5N36.js +1 -0
  59. package/dist/chunk-TPN25AFZ.js +159 -0
  60. package/dist/chunk-UIOSADPB.js +1718 -0
  61. package/dist/{chunk-ZH75KWBI.js → chunk-UNEJYZ4T.js} +1 -1
  62. package/dist/chunk-VAC3SQ5T.js +16 -0
  63. package/dist/{chunk-VREXSMC6.js → chunk-VVSHZV5S.js} +5 -5
  64. package/dist/chunk-WNUXMPVX.js +1 -0
  65. package/dist/chunk-WP3QS4CJ.js +1 -0
  66. package/dist/chunk-ZPQNY5TI.js +1 -0
  67. package/dist/contracts-GUXGFCVV.js +1 -0
  68. package/dist/core-schema-423BSTE6.js +936 -0
  69. package/dist/dispatch-3HY4735M.js +1 -0
  70. package/dist/index.cjs +104 -214
  71. package/dist/index.d.cts +181 -169
  72. package/dist/index.d.ts +181 -169
  73. package/dist/index.js +1 -1
  74. package/dist/media-artifacts-67CJWI7O.js +1 -0
  75. package/dist/metering-ZH2P62IC.js +1 -0
  76. package/dist/migrations-3AKEC5QE.js +1 -0
  77. package/dist/queue-HUP2GNXF.js +1 -0
  78. package/dist/reconciliation-ANXNJWME.js +1 -0
  79. package/dist/report-artifact-offload-6OOY2PTM.js +1 -0
  80. package/dist/repository-4EC5EXVN.js +1 -0
  81. package/dist/{location-data-repository-A2QZVP7O.js → repository-CYXVMYAI.js} +1 -1
  82. package/dist/server-VQU6S24A.js +5825 -0
  83. package/dist/settlement-6CCE2PDH.js +1 -0
  84. package/dist/worker-5G5BK23B.js +1 -0
  85. package/package.json +2 -2
  86. package/dist/analytics-repository-DZZKDZL6.js +0 -1
  87. package/dist/chunk-6DRKDSXY.js +0 -182
  88. package/dist/chunk-6ND5AFV5.js +0 -27
  89. package/dist/chunk-7XDBGQDY.js +0 -6
  90. package/dist/chunk-7YOXSMHG.js +0 -1565
  91. package/dist/chunk-BLVBUMH5.js +0 -21
  92. package/dist/chunk-EDQ57S4P.js +0 -1
  93. package/dist/chunk-GLTECY3U.js +0 -1282
  94. package/dist/chunk-GPSPSJBJ.js +0 -1
  95. package/dist/chunk-HE45FFBU.js +0 -1
  96. package/dist/chunk-HUV2WTRW.js +0 -1
  97. package/dist/chunk-IXJQHXDC.js +0 -2595
  98. package/dist/chunk-K3D2S36Q.js +0 -16
  99. package/dist/chunk-KFOFU3LA.js +0 -1
  100. package/dist/chunk-LMWU7VPZ.js +0 -63
  101. package/dist/chunk-O4LUUIYG.js +0 -4
  102. package/dist/chunk-OLSW4AN4.js +0 -19
  103. package/dist/chunk-RLFCJGHA.js +0 -100
  104. package/dist/chunk-T4VTJNDZ.js +0 -84
  105. package/dist/chunk-TMB56NCA.js +0 -1
  106. package/dist/chunk-VXOXB3IB.js +0 -1
  107. package/dist/chunk-WO3N5FH2.js +0 -5
  108. package/dist/chunk-WO46LTBW.js +0 -1
  109. package/dist/chunk-XPEJB4BZ.js +0 -1
  110. package/dist/db-CDEZHBRN.js +0 -1
  111. package/dist/extract-bundle-SXC6EWJR.js +0 -26
  112. package/dist/gmail-service-EWR76XE7.js +0 -1
  113. package/dist/lead-list-enrichment-repository-SIWGXWQV.js +0 -1
  114. package/dist/operation-metering-UJ53YX7S.js +0 -1
  115. package/dist/server-QVEKDIDQ.js +0 -5014
  116. package/dist/site-extract-repository-23ND4N77.js +0 -1
  117. package/dist/stripe-event-worker-LPXTITVC.js +0 -1
  118. package/dist/worker-EDN2TJH3.js +0 -1
  119. /package/dist/{chunk-YGBTTW5D.js → chunk-2IATUJO6.js} +0 -0
  120. /package/dist/{chunk-6DTXIZY2.js → chunk-4ZVO6GX4.js} +0 -0
  121. /package/dist/{chunk-XG6GCEUE.js → chunk-U4WIJZCX.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,52 @@ All notable changes to MCP Scraper are documented here. The format is based on [
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.96.0] - 2026-10-12
8
+
9
+ ### Security
10
+
11
+ - Keep raw browser and provider diagnostics out of public Maps failures and SERP results, including retained files. SERP clients should read normalized capture content from `harvestResult` instead of `providerPayload`.
12
+
13
+ ### Added
14
+
15
+ - Retrieve complete retained results through `artifact_describe`, `artifact_read` and `artifact_download`. Text reads continue from the returned offset; binary files remain downloadable in their original format, with explicit file and download-link expiry. Temporary MCP results remain accessible for seven days; retained reports keep their existing 24-hour access window.
16
+ - Inspect saved failures, retries, fallback attempts, provider costs, billing and cleanup through the admin trace endpoint and CTO report. Inspection reads existing work without rerunning acquisition or changing charges.
17
+ - Report customer usage by service and client, including failures, retries, refunds, provider costs, credit spending and cash receipts. Filter customer and fleet reports by time, service, plan and declared client labels, and inspect request chains and degradation windows. Request capture is opt-in through `MCP_ANALYTICS_CAPTURE=1`; missing historical data remains explicit.
18
+ - Reconcile reported transcript and search provider charges through hourly maintenance, including spend from failed attempts. Replace estimates on the original receipt without double-counting costs or changing customer credits; delayed and unavailable charges remain distinguishable from zero.
19
+
20
+ ### Changed
21
+
22
+ - Keep the approved initial MCP routing map concise, and replace the old domain manual with on-demand guidance for chat and terminal delivery, intent and flags, result coverage and recovery. Correct stale generic Memory write descriptions to use the Research and CRM owners; keep tool requirements in their schemas and preparation responses.
23
+ - Organize implementations by behavior and state ownership so contributors can edit features in focused files. HTTP and MCP adapters call the feature owners; shared database, execution, billing, concurrency, browser and provider-cost code have dedicated homes. Update the edit guide and remove unused empty source placeholders while preserving public entrypoints and existing credentials.
24
+ - Bound ordinary Google Search recovery to two primary attempts followed by one SERP API fallback. Both primary attempts retain the requested page count; the one-page fallback reports partial coverage for a two-page request. Rich one-page searches use the API directly, while date-filtered searches retain their browser route.
25
+ - Use account-confirmed browser usage rates for new cost calculations. Show monthly plan allowances and configured hosting estimates separately from request costs, with their date, scope and calculation basis.
26
+ - Provide a testing skill for the current implementation that distinguishes isolated candidate checks from production checks.
27
+ - Serve the migration branch across the server, web app, Memory and scheduler, with branch-specific automatic deployments. Package branch-local Memory storage and tools into its hosted build so retained-file reads use the same implementation that was tested.
28
+
29
+ ### Fixed
30
+
31
+ - Preserve complete CRM and Research tool descriptions, correct domain-specific field meanings and keep transport authentication out of public CRM search arguments. Align video limits, transcription fallbacks, ongoing browser costs and write/retry guidance with actual behavior.
32
+ - Preserve request IDs on streamed MCP responses so SDK callbacks and customer reports can correlate the completed call.
33
+ - Keep oversized MCP tool, resource and Task responses within the shared delivery budget while retaining complete formatted results and transcripts. Preserve small responses, readable previews and recovery identifiers; report storage failures without claiming complete content was saved.
34
+ - Authorize retained files against their producer's owner, preserve immutable file versions and reconstruct Unicode across text windows. Reject text reads of binary files, support storage streams without byte-length headers, renew download links and expose retained ZIPs through the existing archive reader. Terminal clients retrieve hosted files through the authenticated backend.
35
+ - Preserve complete URL, Wayback, website, connected-data, directory and media results before offload, including page bodies and metadata when local report saving is disabled. Provide full plain-text transcripts alongside structured results and readable manifests for original binary files. Prevent report filename collisions and resume bounded expired-file cleanup without changing durable retention.
36
+ - Preserve captured People Also Ask answers, linked entities, citations, source titles, descriptions and organic snippets. Expand available answer controls, report unfinished expansion as partial and retain full JSON and Markdown alongside bounded chat previews.
37
+ - Verify the requested Maps business before merging profile data and preserve full review prose through fallback and saved retries. Keep valid profile material when enrichment fails, report incomplete fields and recognize ratings and review counts rendered on separate lines.
38
+ - Use native rendered settings for managed browsers and submit Google searches through the homepage form. Preserve query filters, allow bounded automatic challenge recovery within the existing deadline and require readable results after recovery. Explicit interactive viewport requests and screenshot output sizes remain supported.
39
+ - Require dedicated SERP credentials and reject incomplete configuration before acquisition. Preserve existing keys and other products' credential selection.
40
+ - Preserve funded Reddit comments and the original retry identity across uncertain outcomes. Bound browser work by the request deadline, require confirmed remote cleanup before another attempt and derive refunds from applied charges, reporting unresolved settlement explicitly.
41
+ - Preserve captured PAA and Search results, original holds and billing identity across interrupted creation, scheduling, settlement and publication. Same-key retries inspect or resume saved work instead of acquiring it again; stale workers cannot overwrite newer results, and uncertain cleanup stops automatic acquisition retries.
42
+ - Recover pending PAA and Search callbacks without discarding completed results or changing their charges. Preserve accepted or uncertain delivery across restarts so a lost response cannot cause an automatic repeat send.
43
+ - Prevent overlapping lead-list and directory workers from duplicating row or city acquisition. Require current funding and execution permission, preserve known results after later failures and recover original settlement without duplicate charges or refunds.
44
+ - Preserve queued website, lead-list and directory work across expired dispatch claims and lost acknowledgments. Retain original holds when accepted work may still start and report unconfirmed handoffs accurately.
45
+ - Prevent overlapping website phases, stale recovery attempts and cancellation from overwriting saved pages or settling another attempt. Rebuild exports from saved content with isolated assembly, validate cached content references and retain complete HTML and Markdown through interrupted recovery.
46
+ - Prevent expired or replaced Sourcebook acquisition attempts from publishing stale revisions or refunds. Commit saved revisions and events together and recover lost replies without repeating acquisition.
47
+ - Recover operation admissions and billing from saved execution and settlement records. Reject stale wallet writes, preserve original credit allocation and expiry rules, and retain existing billing keys when adopting original holds.
48
+ - Charge media transcription by observed clip duration or timed transcript coverage instead of model processing time. Preserve available text and refund the transcription charge when duration is unavailable; expose transcript quality warnings and count spoken words across scripts.
49
+ - Run CEO and CTO reports against an explicitly selected source checkout with credentials supplied separately. Support exact hourly windows, maintenance outcomes and customer/degradation reports; scope subscriber counts and recurring revenue to MCP products, distinguish calculated and reported costs and leave unavailable queue or shared-account costs unknown.
50
+ - Remove credentials from browser connection errors before they reach logs or callers. Keep source-based startup compatible with extracted formatters, prevent imported test servers from starting background pollers and handle Sourcebook polling failures without unhandled rejections.
51
+ - Show the 367-tool inventory consistently in documentation, rate metadata and the terminal installer.
52
+
7
53
  ## [0.95.2] - 2026-09-29
8
54
 
9
55
  ### Fixed
@@ -2154,7 +2200,7 @@ All notable changes to MCP Scraper are documented here. The format is based on [
2154
2200
  - Write actions remain unavailable until the account owner explicitly enables them.
2155
2201
  - Provider-specific connection data is normalized into one agent-facing contract.
2156
2202
 
2157
- [Unreleased]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.95.2...HEAD
2203
+ [Unreleased]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.96.0...HEAD
2158
2204
  [0.95.2]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.95.1...v0.95.2
2159
2205
  [0.95.1]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.95.0...v0.95.1
2160
2206
  [0.95.0]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.94.4...v0.95.0
@@ -2323,3 +2369,4 @@ All notable changes to MCP Scraper are documented here. The format is based on [
2323
2369
  [0.89.3]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.89.2...v0.89.3
2324
2370
  [0.89.4]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.89.3...v0.89.4
2325
2371
  [0.89.5]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.89.4...v0.89.5
2372
+ [0.96.0]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.95.2...v0.96.0
package/README.md CHANGED
@@ -175,7 +175,7 @@ Build the branded one-click bundle:
175
175
  npm run build:mcpb
176
176
  ```
177
177
 
178
- The generated bundle is written to `build/mcpb/mcp-scraper-<version>.mcpb` and copied to `public/downloads/` for the hosted download. The current public bundle is `https://mcpscraper.dev/downloads/mcp-scraper.mcpb` (`0.95.2`, SHA-256 `0288142fd0ef400300902a6c5bb1e7596f70aa329cbbe078adff98a5311f1e37`). Install it by opening or dragging it into Claude Desktop. Claude displays the `MCP Scraper` install card, icon, API-key configuration field, and manually curated current-release message from the bundle manifest.
178
+ The generated bundle is written to `build/mcpb/mcp-scraper-<version>.mcpb` and copied to `public/downloads/` for the hosted download. The current public bundle is `https://mcpscraper.dev/downloads/mcp-scraper.mcpb` (`0.96.0`, SHA-256 `72fd074fb10f0d4dc4bbf7d0dff35b45f7fa228bba3aa201185e7e036791cefa`). Install it by opening or dragging it into Claude Desktop. Claude displays the `MCP Scraper` install card, icon, API-key configuration field, and manually curated current-release message from the bundle manifest.
179
179
 
180
180
  The MCPB install exposes every tool — web-intelligence plus all `browser_*` tools — through the one `mcp-scraper` server.
181
181
 
@@ -345,13 +345,13 @@ Google Search Console exposes eight bounded reads and eight gated property and s
345
345
 
346
346
  For accurate annotated videos, do not guess annotation times from a script. Start the replay, navigate until each target is visible and stable, call `browser_replay_mark` for each callout, then stop the replay and pass the returned annotations to `browser_replay_annotate` with the returned `source_width` and `source_height`.
347
347
 
348
- For `search_serp`, callers provide a query and can reuse an `idempotencyKey` after an uncertain response. Light mode returns organic positions, URLs, titles, and descriptions. Unfiltered one-page full mode adds available same-page SERP features, including local results, discussions, videos, AI features, and on-page questions. Both modes default to one page; request `pages: 2` explicitly for a second page. Set `recency: "week"` or `recency: "month"` to apply Google's past-week or past-month filter. Filtered searches use Option 2 Browser API as the primary provider and return organic results only. A one-page filtered search costs 35 Credits. With `pages: 2`, the browser scrolls to Google's pagination control and clicks the native Page 2 link; two delivered pages cost 70 Credits. For unfiltered searches, Browser API is the last resort after the normal provider path fails; rich features are marked unsupported if it supplies the result. Full mode does not open result URLs, expand PAA questions, or fetch Maps business profiles. Unfiltered light mode costs 20 Credits per delivered page, or 35 Credits when a backup supplies it; full mode costs 35 Credits per delivered page. Two-page searches return organic results only. Location, language, device, and legacy optional-module fields remain accepted for compatibility but do not change ordinary searches. Other Google SERP and Maps tools retain their own regional inputs. MCP Scraper owns transport selection and bounded retries internally; implementation controls and receipts are not part of the public tool contract.
348
+ For `search_serp`, callers provide a query and can reuse an `idempotencyKey` after an uncertain response. Light mode returns organic positions, URLs, titles, and descriptions. Unfiltered one-page full mode adds available same-page SERP features, including local results, discussions, videos, AI features, and on-page questions. Both modes default to one page; request `pages: 2` explicitly for a second page. Set `recency: "week"` or `recency: "month"` to apply Google's past-week or past-month filter. Filtered searches use Option 2 Browser API as the primary provider and return organic results only. A one-page filtered search costs 35 Credits. With `pages: 2`, the browser scrolls to Google's pagination control and clicks the native Page 2 link; two delivered pages cost 70 Credits. Unfiltered light searches use up to two primary-provider attempts followed by one SERP API fallback, with no browser recovery. Unknown execution outcomes stop retries. Both primary attempts retain the requested page count; the API fallback delivers one page and reports partial coverage for a two-page request. Unfiltered one-page full searches use the SERP API directly to preserve requested feature semantics. Full mode does not open result URLs, expand PAA questions, or fetch Maps business profiles. Unfiltered light mode costs 20 Credits per delivered page, or 35 Credits when a backup supplies it; full mode costs 35 Credits per delivered page. Two-page searches return organic results only. Location, language, device, and legacy optional-module fields remain accepted for compatibility but do not change ordinary searches. Other Google SERP and Maps tools retain their own regional inputs. MCP Scraper owns transport selection and bounded retries internally; implementation controls and receipts are not part of the public tool contract.
349
349
 
350
350
  The `mcp-scraper` server (and the MCPB bundle, which runs it) exposes both sections through one MCP server.
351
351
 
352
352
  All MCP tools return `structuredContent` with the IDs, URLs, CSV paths, transcripts, browser session handles, replay paths, artifacts, recipe fields, or blueprint fields needed by the next step, plus readable text content for compatibility. Runtime `tools/list` omits output schemas so strict clients can register the complete catalog; the generated developer manifest retains every canonical output schema for validation and typed SDK generation. All tools carry MCP annotations; file-writing tools such as replay downloads and annotations state their filesystem side effects.
353
353
 
354
- The canonical tool inventory is generated at `docs/mcp-tool-manifest.generated.json`. The unified server exposes 364 tools: 238 scraper, browser, workflow, billing, and connected-service tools plus 126 durable-memory tools. Personal Assistant tools and routes are retired from active builds; their source history and persisted data remain recoverable from the archived pre-retirement branch. The scraper-side inventory includes complete Gmail selection, message, attachment, export, bulk-action, and Memory-import workflows; durable SERP, PAA, and single-page extraction starts and status; rendered site-content similarity; governed Local Sourcebook and Transparent Commons workflows; direct site-export reads; Editorial Reading Room and News Publisher templates; and production X-Ray setup, analytics, seven-model attribution, structured post-purchase surveys, reported impact, truthful view-evidence status, CRM policy and receipt, campaign, export, and scheduled-report tools. Provider setup remains absent until its authorization, ingestion, reconciliation, privacy, canary, cleanup, and deployment receipts are complete. Successful evidence-compiled Local Sourcebook revisions publish automatically to their canonical `localsourcebook.com` category profile and review URLs; administrator controls handle exceptional rejection or unpublishing. Release verification compares the exact local and hosted tool-name sets, not only the count.
354
+ The canonical tool inventory is generated at `docs/mcp-tool-manifest.generated.json`. The unified server exposes 367 tools: 241 scraper, browser, workflow, billing, and connected-service tools plus 126 durable-memory tools. Personal Assistant tools and routes are retired from active builds; their source history and persisted data remain recoverable from the archived pre-retirement branch. The scraper-side inventory includes complete Gmail selection, message, attachment, export, bulk-action, and Memory-import workflows; durable SERP, PAA, and single-page extraction starts and status; rendered site-content similarity; governed Local Sourcebook and Transparent Commons workflows; direct site-export reads; Editorial Reading Room and News Publisher templates; and production X-Ray setup, analytics, seven-model attribution, structured post-purchase surveys, reported impact, truthful view-evidence status, CRM policy and receipt, campaign, export, and scheduled-report tools. Provider setup remains absent until its authorization, ingestion, reconciliation, privacy, canary, cleanup, and deployment receipts are complete. Successful evidence-compiled Local Sourcebook revisions publish automatically to their canonical `localsourcebook.com` category profile and review URLs; administrator controls handle exceptional rejection or unpublishing. Release verification compares the exact local and hosted tool-name sets, not only the count.
355
355
 
356
356
  For contract parity, stdio and MCPB memory calls invoke the matching public tool on the hosted MCP Scraper `/mcp` endpoint. The hosted aggregate runtime owns MCP Scraper-specific billing, scheduling, credential, and in-process cutover policy; its internal `/memory/mcp-call` bridge is a fallback to the standalone Memory service, not a second customer setup path. Existing direct Memory credentials remain compatible for one release, but all new customer setup uses the root endpoint and `MCP_SCRAPER_API_KEY`.
357
357
 
@@ -410,4 +410,20 @@ The bundle runs `mcp-scraper` internally, so the user installs `MCP Scraper` onc
410
410
 
411
411
  ## Development
412
412
 
413
+ Production deployments track `codex/mcp-ownership-tree` for the server, web app, Memory and scheduler. Their Vercel configurations admit this branch and disable automatic deployments from `main`. Before deploying server schema changes, run `npm run release:server:predeploy` against the production database. Customer request capture additionally requires `MCP_ANALYTICS_CAPTURE=1`; missing historical attribution stays unknown.
414
+
415
+ See the [codebase layout and edit guide](docs/architecture/codebase-file-tree.md) for the populated tree, feature homes and compatibility entrypoints.
416
+
417
+ Edit behavior in its domain under `src/`: `paa`, `google-search`, `reddit`, `maps`, `web-extraction`, `transcripts`, `site-audit`, `analytics`, `personal-assistant`, `twilio`, `gmail`, `integrations`, `commons`, `leads`, and the other named features.
418
+
419
+ - `src/api/server.ts` assembles HTTP routes; handlers live in `src/api/routes/`.
420
+ - `src/mcp/features/<feature>/` holds each feature's tool registrations, schemas, and formatting. `src/mcp/paa-mcp-server.ts` assembles the main server. Common MCP delivery and errors live in `src/mcp/shared/`.
421
+ - Domain repositories keep their queries beside the behavior they serve. `src/platform/` owns shared database connections, core schema migrations, files, and HTTP plumbing.
422
+ - `src/runtime/` holds shared execution, concurrency, maintenance, and provider-cost machinery. `src/browser/` and `src/providers/` hold vendor adapters.
423
+ - `src/artifacts/` and `src/delivery/` own stored content, expiry, downloads, and delivery.
424
+ - `bin/` and `api/` are terminal and hosted entrypoints; `apps/`, `packages/`, `ops/`, and `nango-integrations/` retain their separate product and provider code.
425
+
426
+ Use `npm run typecheck`, `npm test`, and `npm run build` for ordinary changes. Tests requiring databases or live providers belong in `tests/integration/` or `tests/live/`.
427
+
428
+
413
429
  - [MCP Tool Quality Spec](docs/mcp-tool-quality-spec.md) defines the shipping bar for model-facing tool names, descriptions, schemas, structured outputs, errors, packaging, and deployment.
@@ -0,0 +1 @@
1
+ import{e as a,f as b,g as c,h as d,i as e,j as f,k as g,l as h}from"./chunk-JW4GDY7S.js";import"./chunk-TPN25AFZ.js";import"./chunk-NALXKTUU.js";import"./chunk-R5LJL6IR.js";import"./chunk-RDHGQ2WZ.js";import"./chunk-QWE7VKIX.js";import"./chunk-ZPQNY5TI.js";import"./chunk-SEZETSB3.js";import"./chunk-SDQW7IJN.js";import"./chunk-VVSHZV5S.js";import"./chunk-4ZVO6GX4.js";import"./chunk-WNUXMPVX.js";import"./chunk-WP3QS4CJ.js";import"./chunk-OILOHQNL.js";export{d as GMAIL_ATTACHMENT_REF_TTL_MS,c as GMAIL_INLINE_BODY_BYTES,b as GMAIL_MAIN_CONTRACT_HASH,a as GMAIL_MAIN_CONTRACT_VERSION,h as GmailService,e as GmailServiceError,g as normalizeGmailMessage,f as parseGmailAddresses};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e,f,g,h,i,j,k,l,m}from"./chunk-RDHGQ2WZ.js";import"./chunk-SEZETSB3.js";import"./chunk-SDQW7IJN.js";import"./chunk-OILOHQNL.js";export{a as CONNECTED_DATA_ARTIFACT_PREFIX,b as CONNECTED_DATA_ARTIFACT_TTL_MS,c as CONNECTED_DATA_DOWNLOAD_TTL_MS,m as cleanupExpiredConnectedDataArtifacts,f as connectedDataArtifactExpiresAt,g as connectedDataArtifactIsExpired,e as connectedDataArtifactOwnerId,d as connectedDataArtifactPolicy,h as createConnectedDataArtifact,i as createConnectedDataBinaryArtifact,l as readConnectedDataArtifactBytes,k as readConnectedDataArtifactWindow,j as renewConnectedDataArtifactDownload};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e,f}from"./chunk-55ZANTHL.js";import"./chunk-SEZETSB3.js";import"./chunk-SDQW7IJN.js";import"./chunk-B3JRL45T.js";import"./chunk-OILOHQNL.js";export{a as MAPS_MEDIA_ARTIFACT_PREFIX,b as MAPS_MEDIA_ARTIFACT_TTL_MS,d as mapsMediaArtifactOwnerId,c as mapsMediaArtifactPolicy,f as packageMapsMedia,e as readOwnedMapsMediaArtifactBuffer};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o}from"./chunk-KXJCGV3H.js";import"./chunk-SEZETSB3.js";import"./chunk-SDQW7IJN.js";import"./chunk-OILOHQNL.js";export{a as SITE_EXTRACT_ARTIFACT_PREFIX,b as SITE_EXTRACT_ARTIFACT_TTL_MS,c as SITE_EXTRACT_DOWNLOAD_TTL_MS,o as cleanupExpiredSiteExtractArtifacts,f as createSiteExtractBundleArtifact,g as createSiteExtractBundleArtifactStream,i as createSiteExtractContentChunkArtifact,h as createSiteExtractImageArtifact,l as readOwnedSiteExtractArtifactBuffer,m as readOwnedSiteExtractContentChunk,n as readOwnedSiteExtractImageArtifact,k as readSiteExtractArtifactBuffer,j as renewSiteExtractArtifactDownload,e as siteExtractArtifactOwnerId,d as siteExtractArtifactPolicy};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e,f,g,h,i,j}from"./chunk-3F7AFM53.js";import"./chunk-SEZETSB3.js";import"./chunk-SDQW7IJN.js";import"./chunk-OILOHQNL.js";export{c as DIRECTORY_ARTIFACT_DOWNLOAD_TTL_MS,a as DIRECTORY_ARTIFACT_PREFIX,b as DIRECTORY_ARTIFACT_TTL_MS,d as DIRECTORY_CSV_CONTENT_TYPE,j as cleanupExpiredDirectoryArtifacts,g as createDirectoryCsvArtifact,f as directoryArtifactOwnerId,e as directoryArtifactPolicy,i as readDirectoryArtifactWindow,h as renewDirectoryArtifactDownload};
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
  import{readFileSync as s}from"fs";function c(){try{for(let r of s(".env","utf8").split(`
3
- `)){let o=r.indexOf("=");if(o<1||r.trimStart().startsWith("#"))continue;let e=r.slice(0,o).trim();process.env[e]||(process.env[e]=r.slice(o+1).trim())}}catch{}}c();async function a(){let[{serve:r},{app:o},{startWorker:e},{migrate:i}]=await Promise.all([import("@hono/node-server"),import("../server-QVEKDIDQ.js"),import("../worker-EDN2TJH3.js"),import("../db-CDEZHBRN.js")]),n=parseInt(process.env.PORT??"3001");try{if(await i(),process.env.ANALYTICS_DATABASE_URL){let{migrateAnalytics:t}=await import("../analytics-repository-DZZKDZL6.js");await t()}e(),r({fetch:o.fetch,port:n},t=>{console.log(`[server] http://localhost:${t.port}`),console.log(`[server] admin auth: ${process.env.ADMIN_KEY?"configured":"not configured"}`)})}catch(t){console.error("[startup] server preflight failed",t instanceof Error?t.name:"unknown_error"),process.exit(1)}}a();
3
+ `)){let o=r.indexOf("=");if(o<1||r.trimStart().startsWith("#"))continue;let e=r.slice(0,o).trim();process.env[e]||(process.env[e]=r.slice(o+1).trim())}}catch{}}c();async function a(){let[{serve:r},{app:o},{startWorker:e},{migrate:i}]=await Promise.all([import("@hono/node-server"),import("../server-VQU6S24A.js"),import("../worker-5G5BK23B.js"),import("../core-schema-423BSTE6.js")]),n=parseInt(process.env.PORT??"3001");try{if(await i(),process.env.ANALYTICS_DATABASE_URL){let{migrateAnalytics:t}=await import("../migrations-3AKEC5QE.js");await t()}e(),r({fetch:o.fetch,port:n},t=>{console.log(`[server] http://localhost:${t.port}`),console.log(`[server] admin auth: ${process.env.ADMIN_KEY?"configured":"not configured"}`)})}catch(t){console.error("[startup] server preflight failed",t instanceof Error?t.name:"unknown_error"),process.exit(1)}}a();
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import{b as E,c as N,d as M,e as L,f as T,j as U}from"../chunk-6ND5AFV5.js";import"../chunk-KJQXUZ4Y.js";import"../chunk-XPEJB4BZ.js";import{g as v,j as b,k as D,l as K,m as H}from"../chunk-WO3N5FH2.js";import"../chunk-HE45FFBU.js";import{a as P}from"../chunk-VXOXB3IB.js";import{Command as he}from"commander";import{spawn as ne}from"child_process";import{mkdir as ke,writeFile as Pe}from"fs/promises";import{basename as Ce,join as Z}from"path";function se(e){return e.apiKey?.trim()||"sk_live_your_key"}function ce(e){return e.packageSpec?.trim()||"mcp-scraper@latest"}function A(e={}){return["-y","--package",ce(e),"mcp-scraper"]}function pe(e){let n={MCP_SCRAPER_API_KEY:se(e)},c=e.browserProfileName?.trim();return c&&(n.BROWSER_AGENT_PROFILE_NAME=c),e.browserProfileSaveChanges===!0&&(n.BROWSER_AGENT_PROFILE_SAVE_CHANGES="true"),n}function q(){return["mcp","remove","mcp-scraper","-s","user"]}function J(){return["mcp","get","mcp-scraper"]}function B(e){let n=e.match(/^\s*Command:\s*(.+?)\s*$/m)?.[1];if(!n)return null;let c=e.match(/^\s*Args:\s*(.*?)\s*$/m)?.[1]??"",i=c.length?c.split(/\s+/):[],p={},u=e.split(/^\s*Environment:\s*$/m)[1];if(u)for(let a of u.split(`
3
- `)){let l=a.match(/^\s{2,}([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);if(!l){if(a.trim().length&&!/^\s{2,}/.test(a))break;continue}p[l[1]]=l[2]}return{command:n,args:i,env:p}}function j(e){let n=["mcp","add","mcp-scraper","--scope","user"];for(let[c,i]of Object.entries(e.env))n.push("--env",`${c}=${i}`);return n.push("--",e.command,...e.args),n}function G(e={}){let n=["mcp","add","mcp-scraper","--scope","user"];for(let[c,i]of Object.entries(pe(e)))n.push("--env",`${c}=${i}`);return n.push("--","npx",...A(e)),n}function O(e){if(e==="claude-code")return"claude";if(e==="claude"||D.hosts.some(n=>n.id===e))return e;throw new Error('Unknown host "'+e+'". Use: codex, claude, claude-code, claude-desktop, cursor, windsurf, cline, or user-action-only')}function ue(e){return K(e==="claude"?"claude-code":e)}function W(e,n={}){let c=O(e),i=ue(c),p="Restart the MCP client so it starts a fresh npx process.",u='MCP_SCRAPER_API_KEY="$MCP_SCRAPER_API_KEY" npx -y -p mcp-scraper@latest mcp-scraper-cli agent install claude --apply',a=`X-Ray install protocol: ${v} (${b})`;return c==="codex"?["# Codex MCP config",a,i.exactConfig,"",`Continuation: ${i.continuation}`,`Rollback: ${i.rollback}`,"",p].join(`
2
+ import{k as N,l as j,m as L,n as T,r as U}from"../chunk-4HZXATHB.js";import"../chunk-KJQXUZ4Y.js";import"../chunk-6N5EC7ZO.js";import{g as v,j as b,k as D,l as K,m as H}from"../chunk-24YDQ3EO.js";import"../chunk-OLYPLX5C.js";import"../chunk-B3JRL45T.js";import{c as _}from"../chunk-OILOHQNL.js";import{a as P}from"../chunk-7UHDOIUE.js";import{Command as he}from"commander";import{spawn as te}from"child_process";import{mkdir as ke,writeFile as Pe}from"fs/promises";import{basename as Ce,join as X}from"path";function ie(e){return e.apiKey?.trim()||"sk_live_your_key"}function ae(e){return e.packageSpec?.trim()||"mcp-scraper@latest"}function A(e={}){return["-y","--package",ae(e),"mcp-scraper"]}function se(e){let n={MCP_SCRAPER_API_KEY:ie(e)},c=e.browserProfileName?.trim();return c&&(n.BROWSER_AGENT_PROFILE_NAME=c),e.browserProfileSaveChanges===!0&&(n.BROWSER_AGENT_PROFILE_SAVE_CHANGES="true"),n}function q(){return["mcp","remove","mcp-scraper","-s","user"]}function B(){return["mcp","get","mcp-scraper"]}function J(e){let n=e.match(/^\s*Command:\s*(.+?)\s*$/m)?.[1];if(!n)return null;let c=e.match(/^\s*Args:\s*(.*?)\s*$/m)?.[1]??"",i=c.length?c.split(/\s+/):[],p={},u=e.split(/^\s*Environment:\s*$/m)[1];if(u)for(let a of u.split(`
3
+ `)){let l=a.match(/^\s{2,}([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);if(!l){if(a.trim().length&&!/^\s{2,}/.test(a))break;continue}p[l[1]]=l[2]}return{command:n,args:i,env:p}}function O(e){let n=["mcp","add","mcp-scraper","--scope","user"];for(let[c,i]of Object.entries(e.env))n.push("--env",`${c}=${i}`);return n.push("--",e.command,...e.args),n}function G(e={}){let n=["mcp","add","mcp-scraper","--scope","user"];for(let[c,i]of Object.entries(se(e)))n.push("--env",`${c}=${i}`);return n.push("--","npx",...A(e)),n}function I(e){if(e==="claude-code")return"claude";if(e==="claude"||D.hosts.some(n=>n.id===e))return e;throw new Error('Unknown host "'+e+'". Use: codex, claude, claude-code, claude-desktop, cursor, windsurf, cline, or user-action-only')}function ce(e){return K(e==="claude"?"claude-code":e)}function W(e,n={}){let c=I(e),i=ce(c),p="Restart the MCP client so it starts a fresh npx process.",u='MCP_SCRAPER_API_KEY="$MCP_SCRAPER_API_KEY" npx -y -p mcp-scraper@latest mcp-scraper-cli agent install claude --apply',a=`X-Ray install protocol: ${v} (${b})`;return c==="codex"?["# Codex MCP config",a,i.exactConfig,"",`Continuation: ${i.continuation}`,`Rollback: ${i.rollback}`,"",p].join(`
4
4
  `):c==="claude"?["# Claude Code command",a,i.exactConfig,"","# One-command Claude Code setup",u,"",`Continuation: ${i.continuation}`,`Rollback: ${i.rollback}`,"",p].join(`
5
5
  `):c==="claude-desktop"?["# Claude Desktop config",a,i.exactConfig,"","Desktop Extension: https://mcpscraper.dev/downloads/mcp-scraper.mcpb",`Continuation: ${i.continuation}`,`Rollback: ${i.rollback}`,p].join(`
6
6
  `):[`# ${i.label} MCP config`,a,i.exactConfig,"",`Continuation: ${i.continuation}`,`Rollback: ${i.rollback}`,...i.restartRequired?["",p]:[]].join(`
7
- `)}import{access as le,mkdir as de,readFile as me}from"fs/promises";import{homedir as Y}from"os";import{join as F}from"path";function fe(e,n=!1){return e?"pass":n?"warn":"fail"}function ge(e){return Number(e.replace(/^v/,"").split(".")[0]??0)}async function we(){let e=process.env.MCP_SCRAPER_KEY_PATH?.trim()||F(Y(),".mcp-scraper-key");try{return(await me(e,"utf8")).trim()||null}catch{return null}}async function ye(e){try{let n=await e("https://registry.npmjs.org/mcp-scraper/latest",{signal:AbortSignal.timeout(5e3)});return n.ok?(await n.json()).version??null:null}catch{return null}}async function z(e={}){let n=e.fetchImpl??fetch,c=(e.apiUrl??process.env.MCP_SCRAPER_API_URL??"https://mcpscraper.dev").replace(/\/$/,""),i=e.outputDir??process.env.MCP_SCRAPER_OUTPUT_DIR??F(Y(),"Downloads","mcp-scraper"),p=e.apiKey?.trim()||process.env.MCP_SCRAPER_API_KEY?.trim()||await we(),u=await ye(n),a=[],l=ge(process.version);a.push({id:"node",label:"Node.js",status:fe(l>=20),detail:process.version,fix:l>=20?void 0:"Install Node.js 20 or newer."}),a.push({id:"package_version",label:"Local package",status:u&&u!==P?"warn":"pass",detail:u?`local ${P}, npm latest ${u}`:`local ${P}, npm latest unavailable`,fix:u&&u!==P?"Use mcp-scraper@latest and restart the MCP client.":void 0}),a.push({id:"api_key",label:"API key",status:p?"pass":"warn",detail:p?"configured":"not configured",fix:p?void 0:"Set MCP_SCRAPER_API_KEY or pass --api-key."});try{await de(i,{recursive:!0}),await le(i),a.push({id:"output_dir",label:"Output directory",status:"pass",detail:i})}catch(t){a.push({id:"output_dir",label:"Output directory",status:"fail",detail:i,fix:t instanceof Error?t.message:"Create a writable output directory."})}if(p)try{let t=await n(`${c}/me`,{headers:{"x-api-key":p},signal:AbortSignal.timeout(8e3)});a.push({id:"api_reachability",label:"Hosted API",status:t.ok?"pass":"fail",detail:`${c}/me returned ${t.status}`,fix:t.ok?void 0:"Verify the API key and account status."})}catch(t){a.push({id:"api_reachability",label:"Hosted API",status:"fail",detail:t instanceof Error?t.message:String(t),fix:"Check network access and MCP_SCRAPER_API_URL."})}else a.push({id:"api_reachability",label:"Hosted API",status:"skip",detail:"skipped because no API key is configured"});return a.push({id:"mcp_config",label:"MCP command",status:"pass",detail:`npx ${A().join(" ")}`,fix:"Restart the MCP client after package updates."}),{ok:a.every(t=>t.status==="pass"||t.status==="skip"),version:P,npmLatest:u,checks:a,recommendedConfig:{command:"npx",args:A(),env:{MCP_SCRAPER_API_KEY:p?"$MCP_SCRAPER_API_KEY":"sk_live_your_key"}}}}function V(e){let n={pass:"PASS",warn:"WARN",fail:"FAIL",skip:"SKIP"};return[`mcp-scraper doctor v${e.version}`,"",...e.checks.map(i=>{let p=i.fix?`
7
+ `)}import{access as pe,mkdir as ue,readFile as le}from"fs/promises";import{homedir as de}from"os";import{join as me}from"path";function fe(e,n=!1){return e?"pass":n?"warn":"fail"}function ge(e){return Number(e.replace(/^v/,"").split(".")[0]??0)}async function we(){let e=process.env.MCP_SCRAPER_KEY_PATH?.trim()||me(de(),".mcp-scraper-key");try{return(await le(e,"utf8")).trim()||null}catch{return null}}async function ye(e){try{let n=await e("https://registry.npmjs.org/mcp-scraper/latest",{signal:AbortSignal.timeout(5e3)});return n.ok?(await n.json()).version??null:null}catch{return null}}async function Y(e={}){let n=e.fetchImpl??fetch,c=(e.apiUrl??process.env.MCP_SCRAPER_API_URL??"https://mcpscraper.dev").replace(/\/$/,""),i=_(e.outputDir),p=e.apiKey?.trim()||process.env.MCP_SCRAPER_API_KEY?.trim()||await we(),u=await ye(n),a=[],l=ge(process.version);a.push({id:"node",label:"Node.js",status:fe(l>=20),detail:process.version,fix:l>=20?void 0:"Install Node.js 20 or newer."}),a.push({id:"package_version",label:"Local package",status:u&&u!==P?"warn":"pass",detail:u?`local ${P}, npm latest ${u}`:`local ${P}, npm latest unavailable`,fix:u&&u!==P?"Use mcp-scraper@latest and restart the MCP client.":void 0}),a.push({id:"api_key",label:"API key",status:p?"pass":"warn",detail:p?"configured":"not configured",fix:p?void 0:"Set MCP_SCRAPER_API_KEY or pass --api-key."});try{await ue(i,{recursive:!0}),await pe(i),a.push({id:"output_dir",label:"Output directory",status:"pass",detail:i})}catch(r){a.push({id:"output_dir",label:"Output directory",status:"fail",detail:i,fix:r instanceof Error?r.message:"Create a writable output directory."})}if(p)try{let r=await n(`${c}/me`,{headers:{"x-api-key":p},signal:AbortSignal.timeout(8e3)});a.push({id:"api_reachability",label:"Hosted API",status:r.ok?"pass":"fail",detail:`${c}/me returned ${r.status}`,fix:r.ok?void 0:"Verify the API key and account status."})}catch(r){a.push({id:"api_reachability",label:"Hosted API",status:"fail",detail:r instanceof Error?r.message:String(r),fix:"Check network access and MCP_SCRAPER_API_URL."})}else a.push({id:"api_reachability",label:"Hosted API",status:"skip",detail:"skipped because no API key is configured"});return a.push({id:"mcp_config",label:"MCP command",status:"pass",detail:`npx ${A().join(" ")}`,fix:"Restart the MCP client after package updates."}),{ok:a.every(r=>r.status==="pass"||r.status==="skip"),version:P,npmLatest:u,checks:a,recommendedConfig:{command:"npx",args:A(),env:{MCP_SCRAPER_API_KEY:p?"$MCP_SCRAPER_API_KEY":"sk_live_your_key"}}}}function F(e){let n={pass:"PASS",warn:"WARN",fail:"FAIL",skip:"SKIP"};return[`mcp-scraper doctor v${e.version}`,"",...e.checks.map(i=>{let p=i.fix?`
8
8
  Fix: ${i.fix}`:"";return`[${n[i.status]}] ${i.label}: ${i.detail}${p}`}),"",`Recommended MCP command: npx ${e.recommendedConfig.args.join(" ")}`].join(`
9
- `)}var X={"xray-setup":H(),"agent-packet":["# MCP Scraper Agent Packet Prompt","","Use MCP Scraper as the evidence layer. Run the agent-packet workflow for the target keyword/domain, then treat `evidence.json`, `sources.csv`, and `competitors.csv` as source of truth.","","Do not invent citations. If a recommendation is not supported by the packet, mark it as an assumption. Turn the evidence into a concise SEO brief, an implementation task list, and content recommendations tied to source rows."].join(`
9
+ `)}var z={"xray-setup":H(),"agent-packet":["# MCP Scraper Agent Packet Prompt","","Use MCP Scraper as the evidence layer. Run the agent-packet workflow for the target keyword/domain, then treat `evidence.json`, `sources.csv`, and `competitors.csv` as source of truth.","","Do not invent citations. If a recommendation is not supported by the packet, mark it as an assumption. Turn the evidence into a concise SEO brief, an implementation task list, and content recommendations tied to source rows."].join(`
10
10
  `),"local-competitive-audit":["# MCP Scraper Local Competitive Audit Prompt","","Run the local-competitive-audit workflow for the niche and markets. Use the city summary, competitor CSV, and review-insight CSV to identify market difficulty, review themes, GBP category patterns, and weak competitors.","","Ground recommendations in Maps rank position, review count, star rating, categories, profile details, and review topics. Do not overstate heuristic opportunity scores."].join(`
11
11
  `),"directory-workflow":["# MCP Scraper Directory Workflow Prompt","","Use directory_workflow when the user wants cities selected by population and Google Maps candidates per city. Keep query and location separate. Preserve result_position, source_location, review stars/count, categories, and profile URLs for downstream CSV or directory use."].join(`
12
12
  `),"map-comparison":["# MCP Scraper Maps Comparison Prompt","","Run the map-comparison workflow when the user wants to compare local Maps competitors in a city or across selected markets. Use `maps-results.csv`, `map-comparison.csv`, and `profile-insights.csv` as source of truth.","","Ground recommendations in result_position, review_stars, review_count, category, website presence, review topics, and profile attributes. Treat review gaps and missing websites as opportunity signals, not guaranteed ranking factors."].join(`
@@ -15,26 +15,26 @@ import{b as E,c as N,d as M,e as L,f as T,j as U}from"../chunk-6ND5AFV5.js";impo
15
15
  `),"ai-overview-language":["# MCP Scraper AI Overview Language Prompt","","Run the ai-overview-language workflow when the user wants to know how to phrase content for AI Overview inclusion. Use `claim-patterns.csv`, `language-guidance.csv`, `ai-overview-citations.csv`, and PAA follow-ups as evidence.","","Recommend concise answer blocks, criteria/step language, citation hooks, and follow-up sections. Do not claim the target will be cited; frame the output as evidence-based language guidance."].join(`
16
16
  `),"ai-citation-monitor":["# MCP Scraper AI Citation Monitor Prompt","","Use search_serp and harvest_paa to check whether the target brand/domain appears in AI Overview citations, organic results, People Also Ask sources, local pack, forums, and videos. Save evidence before summarizing trends or gaps."].join(`
17
17
  `),"serp-brief":["# MCP Scraper SERP Brief Prompt","","Use live SERP, PAA, AI Overview, forum, video, and source evidence to create a writer brief. Tie each recommended section to evidence rows and identify missing proof, entities, comparisons, and customer questions."].join(`
18
- `)};function _(){return Object.keys(X).sort()}function Q(e){let n=X[e];if(!n)throw new Error(`Unknown prompt "${e}". Available: ${_().join(", ")}`);return n}function f(e){if(e==null||e==="")return;let n=Number(e);return Number.isFinite(n)?n:void 0}function Ae(e){if(e!==void 0){if(typeof e=="boolean")return e;if(e==="true")return!0;if(e==="false")return!1}}function x(e){return Object.fromEntries(Object.entries(e).filter(([,n])=>n!==void 0))}function ee(e,n){return x(e==="agent-packet"||e==="serp-comparison"||e==="ai-overview-language"?{keyword:n.keyword,domain:n.domain,url:n.url,location:n.location,maxResults:f(n.maxResults),maxQuestions:f(n.maxQuestions),extractTop:f(n.extractTop),includeSerp:n.serp===!1?!1:void 0,includePaa:n.paa===!1?!1:void 0,includeAiOverview:Ae(n.includeAiOverview),returnPartial:n.returnPartial===!1?!1:void 0}:e==="paa-expansion-brief"?{keyword:n.keyword,location:n.location,maxQuestions:f(n.maxQuestions),depth:f(n.depth),returnPartial:n.returnPartial===!1?!1:void 0}:e==="directory"||e==="local-competitive-audit"||e==="map-comparison"?{query:n.query,location:n.location,state:n.state,minPopulation:f(n.minPop??n.minPopulation),maxCities:f(n.maxCities),maxResultsPerCity:f(n.perCity??n.maxResultsPerCity??n.maxResults),concurrency:f(n.concurrency),proxyMode:n.proxyMode,hydrateTop:e==="local-competitive-audit"||e==="map-comparison"?f(n.hydrateTop):void 0,maxReviews:e==="local-competitive-audit"||e==="map-comparison"?f(n.reviews??n.maxReviews):void 0,returnPartial:n.returnPartial===!1?!1:void 0}:n)}function s(e,n){n?process.stdout.write(`${JSON.stringify(e,null,2)}
18
+ `)};function x(){return Object.keys(z).sort()}function V(e){let n=z[e];if(!n)throw new Error(`Unknown prompt "${e}". Available: ${x().join(", ")}`);return n}function f(e){if(e==null||e==="")return;let n=Number(e);return Number.isFinite(n)?n:void 0}function Ae(e){if(e!==void 0){if(typeof e=="boolean")return e;if(e==="true")return!0;if(e==="false")return!1}}function $(e){return Object.fromEntries(Object.entries(e).filter(([,n])=>n!==void 0))}function Q(e,n){return $(e==="agent-packet"||e==="serp-comparison"||e==="ai-overview-language"?{keyword:n.keyword,domain:n.domain,url:n.url,location:n.location,maxResults:f(n.maxResults),maxQuestions:f(n.maxQuestions),extractTop:f(n.extractTop),includeSerp:n.serp===!1?!1:void 0,includePaa:n.paa===!1?!1:void 0,includeAiOverview:Ae(n.includeAiOverview),returnPartial:n.returnPartial===!1?!1:void 0}:e==="paa-expansion-brief"?{keyword:n.keyword,location:n.location,maxQuestions:f(n.maxQuestions),depth:f(n.depth),returnPartial:n.returnPartial===!1?!1:void 0}:e==="directory"||e==="local-competitive-audit"||e==="map-comparison"?{query:n.query,location:n.location,state:n.state,minPopulation:f(n.minPop??n.minPopulation),maxCities:f(n.maxCities),maxResultsPerCity:f(n.perCity??n.maxResultsPerCity??n.maxResults),concurrency:f(n.concurrency),proxyMode:n.proxyMode,hydrateTop:e==="local-competitive-audit"||e==="map-comparison"?f(n.hydrateTop):void 0,maxReviews:e==="local-competitive-audit"||e==="map-comparison"?f(n.reviews??n.maxReviews):void 0,returnPartial:n.returnPartial===!1?!1:void 0}:n)}function s(e,n){n?process.stdout.write(`${JSON.stringify(e,null,2)}
19
19
  `):typeof e=="string"?process.stdout.write(`${e}
20
20
  `):process.stdout.write(`${JSON.stringify(e,null,2)}
21
- `)}function re(e){return e.replace(/sk_[A-Za-z0-9_-]+/g,"sk_***")}function S(e,n){return new Promise(c=>{let i=ne(e,n,{stdio:["ignore","pipe","pipe"]}),p=[],u=[],a=!1,l=t=>{a||(a=!0,c(t))};i.stdout.on("data",t=>p.push(Buffer.from(t))),i.stderr.on("data",t=>u.push(Buffer.from(t))),i.on("error",t=>{l({code:127,stdout:"",stderr:t.message})}),i.on("close",t=>{l({code:typeof t=="number"?t:1,stdout:Buffer.concat(p).toString("utf8"),stderr:Buffer.concat(u).toString("utf8")})})})}function ve(e){return e.daily?"daily":e.monthly?"monthly":"weekly"}function oe(e){let n=String(e.apiKey??process.env.MCP_SCRAPER_API_KEY??"").trim();if(!n)throw new Error("MCP_SCRAPER_API_KEY is required. Pass --api-key or set the environment variable.");return{apiUrl:String(e.apiUrl??process.env.MCP_SCRAPER_API_URL??"https://mcpscraper.dev").replace(/\/$/,""),apiKey:n}}function te(e){let n=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",c=process.platform==="win32"?["/c","start","",e]:[e];try{return ne(n,c,{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}async function m(e,n,c,i){let{apiUrl:p,apiKey:u}=oe(c),a=await fetch(`${p}${e}`,{method:n,headers:{"Content-Type":"application/json","x-api-key":u},body:i==null?void 0:JSON.stringify(i)}),l=await a.json().catch(()=>({}));if(!a.ok){let t=typeof l.error=="string"?l.error:`API request failed with ${a.status}`;throw new Error(t)}return l}function be(e){return e.option("--keyword <keyword>","Agent packet keyword").option("--domain <domain>","Target domain").option("--url <url>","Target page URL").option("--location <location>","Target location").option("--max-results <n>","Maximum SERP or Maps results").option("--max-questions <n>","Maximum PAA questions").option("--extract-top <n>","Top result pages or citation pages to extract").option("--depth <n>","PAA expansion depth").option("--no-serp","Skip SERP evidence for agent packet").option("--no-paa","Skip PAA evidence for agent packet").option("--query <query>","Business category or workflow query").option("--state <state>","US state").option("--min-pop <n>","Minimum city population").option("--max-cities <n>","Maximum selected cities").option("--per-city <n>","Maps results per city").option("--concurrency <n>","City search concurrency").option("--proxy-mode <mode>","Proxy mode: location, configured, or none").option("--hydrate-top <n>","Profiles to hydrate per city for competitive audits").option("--reviews <n>","Review cards to collect per hydrated profile").option("--no-return-partial","Fail instead of writing partial artifacts")}function Se(){let e=new he;e.name("mcp-scraper-cli").description("Human CLI for MCP Scraper setup, workflows, reports, and agent-ready SEO artifacts.").version(P),e.command("doctor").description("Check setup, API key, output directory, package version, and recommended MCP config.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--output-dir <path>","Output directory to test").option("--json","Print machine-readable JSON").action(async t=>{let r=await z({apiKey:t.apiKey,apiUrl:t.apiUrl,outputDir:t.outputDir});s(t.json?r:V(r),t.json),r.ok||(process.exitCode=1)});let n=e.command("billing").description("Inspect billing and start checkout flows."),c=n.command("concurrency").description("Manage MCP Scraper concurrency packs.");c.command("info").description("Show current concurrency limit and concurrency-pack price.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async t=>{let r=await m("/billing/credits","POST",t,{});if(t.json)s(r.concurrency,!0);else{let o=r.concurrency?.upgrade;s([`Current limit: ${r.concurrency?.current_limit??"unknown"} concurrent operations`,`Extra slots: ${r.concurrency?.current_extra_slots??"unknown"}`,`Concurrency pack: ${o?.price_label??"$5/month for 2 extra browsers"}`,`Upgrade command: ${o?.terminal_command??"mcp-scraper-cli billing concurrency checkout"}`].join(`
22
- `),!1)}}),c.command("checkout").description("Create or update a hosted Stripe checkout for concurrency packs (two browsers per pack).").argument("[quantity]","Desired pack quantity","1").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").option("--no-open","Print the checkout URL without opening a browser").action(async(t,r)=>{let o=await m("/billing/concurrency/terminal-checkout","POST",r,{quantity:Number(t)});if(r.json){s(o,!0);return}let d=!!o.checkout_url&&r.open!==!1&&te(o.checkout_url);s([`Concurrency pack: ${o.price?.price_label??"$5/month for 2 extra browsers"}`,o.updated?"Concurrency pack quantity updated.":d?`Opened checkout: ${o.checkout_url}`:`Checkout URL: ${o.checkout_url}`,o.next_step??"Complete checkout, then retry the MCP request."].join(`
23
- `),!1)}),n.command("subscribe <tier>").description("Subscribe to a plan (starter | growth | scale) via a hosted Stripe checkout link.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").option("--no-open","Print the checkout URL without opening a browser").action(async(t,r)=>{let o=await m("/billing/subscribe/terminal-checkout","POST",r,{tier:String(t).toLowerCase()});if(r.json){s(o,!0);return}if(o.updated){s(o.message??`Switched to ${o.tier}.`,!1);return}let d=r.open!==!1&&!!o.checkout_url&&te(o.checkout_url),C=o.billing_interval==="year"?"year":"month",h=o.amount_usd??o.monthly_usd,k=o.credits_per_interval??o.credits_per_month,g=C==="year"?"/yr":"/mo",w=C==="year"?"credits/yr":"credits/mo",y=[o.credits_never_expire?"credits never expire":null,o.includes_memory?"includes Memory Pro":null,o.intro].filter(Boolean).join(" \xB7 ");s([`Plan: ${o.label??t} \u2014 $${h}${g} \xB7 ${k?.toLocaleString()} ${w} \xB7 ${o.concurrency} concurrency${y?` \xB7 ${y}`:""}`,d?`Opened checkout: ${o.checkout_url}`:`Checkout URL: ${o.checkout_url}`,o.next_step??"Complete payment in the browser."].join(`
24
- `),!1)});let i=e.command("agent").description("Generate AI-agent install configs and workflow prompts.");i.command("install <host>").description("Print or apply install/config instructions for Codex, Claude, Cursor, Windsurf, Cline, or a handoff-only MCP host.").option("--api-key <key>","API key to place in generated config").option("--package <spec>","npm package spec","mcp-scraper@latest").option("--browser-profile <name>","Default saved browser profile for browser_open sessions.").option("--save-browser-profile-changes","Persist cookies and browser storage back to the named browser profile when sessions close").option("--apply","Apply the config to this client when supported. For Claude Code, this upserts the user-scope mcp-scraper server.").option("--json","Print machine-readable JSON").action(async(t,r)=>{let o=O(t),d=r.apiKey??process.env.MCP_SCRAPER_API_KEY;if(r.apply){if(o!=="claude")throw new Error('--apply is currently supported for Claude Code only. Use host "claude" or "claude-code".');if(!d?.trim())throw new Error("MCP_SCRAPER_API_KEY is required for --apply. Set it in the environment or pass --api-key.");let h={apiKey:d,packageSpec:r.package,browserProfileName:r.browserProfile,browserProfileSaveChanges:r.saveBrowserProfileChanges},k=await S("claude",J()),g=k.code===0?B(k.stdout):null,w=await S("claude",q()),y=await S("claude",G(h));if(y.code!==0){let ae=re([y.stderr,y.stdout].filter(Boolean).join(`
25
- `).trim()),R;throw w.code!==0?R="No existing entry was removed, so your configuration is unchanged.":g?R=(await S("claude",j(g))).code===0?"Your previous mcp-scraper entry was restored; nothing was lost.":`WARNING: the previous mcp-scraper entry was removed and could NOT be restored. Re-add it with:
26
- claude `+j(g).join(" "):R="WARNING: the previous mcp-scraper entry was removed and could not be captured for rollback. Re-add it manually.",new Error(["Claude Code MCP registration failed.",ae||"No error output returned.",R,"Make sure Claude Code is installed and the `claude` command is on PATH."].join(`
27
- `))}let I=await S("claude",["mcp","list"]),$={host:"claude",applied:!0,replacedExisting:w.code===0,command:"npx",args:A(h),nextStep:"Fully exit Claude Code, start a new Claude terminal, then run: claude mcp list",list:re([I.stdout,I.stderr].filter(Boolean).join(`
28
- `).trim())};if(r.json){s($,!0);return}s(["Applied Claude Code MCP config: mcp-scraper",w.code===0?"Replaced existing mcp-scraper entry.":"No existing mcp-scraper entry found; added a new one.","Command: npx "+A(h).join(" "),r.browserProfile?`Browser profile: ${r.browserProfile}`:"","","Next step: fully exit Claude Code, start a new Claude terminal, then run:"," claude mcp list","",$.list?`Current Claude MCP list:
29
- ${$.list}`:""].filter(Boolean).join(`
30
- `),!1);return}let C=W(o,{apiKey:r.apiKey,packageSpec:r.package,browserProfileName:r.browserProfile,browserProfileSaveChanges:r.saveBrowserProfileChanges});s(r.json?{host:o,protocolVersion:v,protocolDigest:b,text:C}:C,r.json)}),i.command("prompt [name]").description("Print an agent prompt template.").option("--json","Print machine-readable JSON").action((t,r)=>{if(!t){s(r.json?{prompts:_()}:_().join(`
31
- `),r.json);return}let o=Q(t);s(r.json?{name:t,...t==="xray-setup"?{protocolVersion:v,protocolDigest:b}:{},text:o}:o,r.json)});let p=e.command("workflow").description("Run named SEO workflows.");p.command("list").description("List available workflows.").option("--json","Print machine-readable JSON").action(t=>{let r=T();t.json?s({workflows:r},!0):s(r.map(o=>`${o.id} ${o.title}
21
+ `)}function Z(e){return e.replace(/sk_[A-Za-z0-9_-]+/g,"sk_***")}function S(e,n){return new Promise(c=>{let i=te(e,n,{stdio:["ignore","pipe","pipe"]}),p=[],u=[],a=!1,l=r=>{a||(a=!0,c(r))};i.stdout.on("data",r=>p.push(Buffer.from(r))),i.stderr.on("data",r=>u.push(Buffer.from(r))),i.on("error",r=>{l({code:127,stdout:"",stderr:r.message})}),i.on("close",r=>{l({code:typeof r=="number"?r:1,stdout:Buffer.concat(p).toString("utf8"),stderr:Buffer.concat(u).toString("utf8")})})})}function ve(e){return e.daily?"daily":e.monthly?"monthly":"weekly"}function re(e){let n=String(e.apiKey??process.env.MCP_SCRAPER_API_KEY??"").trim();if(!n)throw new Error("MCP_SCRAPER_API_KEY is required. Pass --api-key or set the environment variable.");return{apiUrl:String(e.apiUrl??process.env.MCP_SCRAPER_API_URL??"https://mcpscraper.dev").replace(/\/$/,""),apiKey:n}}function ee(e){let n=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",c=process.platform==="win32"?["/c","start","",e]:[e];try{return te(n,c,{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}async function m(e,n,c,i){let{apiUrl:p,apiKey:u}=re(c),a=await fetch(`${p}${e}`,{method:n,headers:{"Content-Type":"application/json","x-api-key":u},body:i==null?void 0:JSON.stringify(i)}),l=await a.json().catch(()=>({}));if(!a.ok){let r=typeof l.error=="string"?l.error:`API request failed with ${a.status}`;throw new Error(r)}return l}function be(e){return e.option("--keyword <keyword>","Agent packet keyword").option("--domain <domain>","Target domain").option("--url <url>","Target page URL").option("--location <location>","Target location").option("--max-results <n>","Maximum SERP or Maps results").option("--max-questions <n>","Maximum PAA questions").option("--extract-top <n>","Top result pages or citation pages to extract").option("--depth <n>","PAA expansion depth").option("--no-serp","Skip SERP evidence for agent packet").option("--no-paa","Skip PAA evidence for agent packet").option("--query <query>","Business category or workflow query").option("--state <state>","US state").option("--min-pop <n>","Minimum city population").option("--max-cities <n>","Maximum selected cities").option("--per-city <n>","Maps results per city").option("--concurrency <n>","City search concurrency").option("--proxy-mode <mode>","Proxy mode: location, configured, or none").option("--hydrate-top <n>","Profiles to hydrate per city for competitive audits").option("--reviews <n>","Review cards to collect per hydrated profile").option("--no-return-partial","Fail instead of writing partial artifacts")}function Se(){let e=new he;e.name("mcp-scraper-cli").description("Human CLI for MCP Scraper setup, workflows, reports, and agent-ready SEO artifacts.").version(P),e.command("doctor").description("Check setup, API key, output directory, package version, and recommended MCP config.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--output-dir <path>","Output directory to test").option("--json","Print machine-readable JSON").action(async r=>{let t=await Y({apiKey:r.apiKey,apiUrl:r.apiUrl,outputDir:r.outputDir});s(r.json?t:F(t),r.json),t.ok||(process.exitCode=1)});let n=e.command("billing").description("Inspect billing and start checkout flows."),c=n.command("concurrency").description("Manage MCP Scraper concurrency packs.");c.command("info").description("Show current concurrency limit and concurrency-pack price.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async r=>{let t=await m("/billing/credits","POST",r,{});if(r.json)s(t.concurrency,!0);else{let o=t.concurrency?.upgrade;s([`Current limit: ${t.concurrency?.current_limit??"unknown"} concurrent operations`,`Extra slots: ${t.concurrency?.current_extra_slots??"unknown"}`,`Concurrency pack: ${o?.price_label??"$5/month for 2 extra browsers"}`,`Upgrade command: ${o?.terminal_command??"mcp-scraper-cli billing concurrency checkout"}`].join(`
22
+ `),!1)}}),c.command("checkout").description("Create or update a hosted Stripe checkout for concurrency packs (two browsers per pack).").argument("[quantity]","Desired pack quantity","1").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").option("--no-open","Print the checkout URL without opening a browser").action(async(r,t)=>{let o=await m("/billing/concurrency/terminal-checkout","POST",t,{quantity:Number(r)});if(t.json){s(o,!0);return}let d=!!o.checkout_url&&t.open!==!1&&ee(o.checkout_url);s([`Concurrency pack: ${o.price?.price_label??"$5/month for 2 extra browsers"}`,o.updated?"Concurrency pack quantity updated.":d?`Opened checkout: ${o.checkout_url}`:`Checkout URL: ${o.checkout_url}`,o.next_step??"Complete checkout, then retry the MCP request."].join(`
23
+ `),!1)}),n.command("subscribe <tier>").description("Subscribe to a plan (starter | growth | scale) via a hosted Stripe checkout link.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").option("--no-open","Print the checkout URL without opening a browser").action(async(r,t)=>{let o=await m("/billing/subscribe/terminal-checkout","POST",t,{tier:String(r).toLowerCase()});if(t.json){s(o,!0);return}if(o.updated){s(o.message??`Switched to ${o.tier}.`,!1);return}let d=t.open!==!1&&!!o.checkout_url&&ee(o.checkout_url),C=o.billing_interval==="year"?"year":"month",h=o.amount_usd??o.monthly_usd,k=o.credits_per_interval??o.credits_per_month,g=C==="year"?"/yr":"/mo",w=C==="year"?"credits/yr":"credits/mo",y=[o.credits_never_expire?"credits never expire":null,o.includes_memory?"includes Memory Pro":null,o.intro].filter(Boolean).join(" \xB7 ");s([`Plan: ${o.label??r} \u2014 $${h}${g} \xB7 ${k?.toLocaleString()} ${w} \xB7 ${o.concurrency} concurrency${y?` \xB7 ${y}`:""}`,d?`Opened checkout: ${o.checkout_url}`:`Checkout URL: ${o.checkout_url}`,o.next_step??"Complete payment in the browser."].join(`
24
+ `),!1)});let i=e.command("agent").description("Generate AI-agent install configs and workflow prompts.");i.command("install <host>").description("Print or apply install/config instructions for Codex, Claude, Cursor, Windsurf, Cline, or a handoff-only MCP host.").option("--api-key <key>","API key to place in generated config").option("--package <spec>","npm package spec","mcp-scraper@latest").option("--browser-profile <name>","Default saved browser profile for browser_open sessions.").option("--save-browser-profile-changes","Persist cookies and browser storage back to the named browser profile when sessions close").option("--apply","Apply the config to this client when supported. For Claude Code, this upserts the user-scope mcp-scraper server.").option("--json","Print machine-readable JSON").action(async(r,t)=>{let o=I(r),d=t.apiKey??process.env.MCP_SCRAPER_API_KEY;if(t.apply){if(o!=="claude")throw new Error('--apply is currently supported for Claude Code only. Use host "claude" or "claude-code".');if(!d?.trim())throw new Error("MCP_SCRAPER_API_KEY is required for --apply. Set it in the environment or pass --api-key.");let h={apiKey:d,packageSpec:t.package,browserProfileName:t.browserProfile,browserProfileSaveChanges:t.saveBrowserProfileChanges},k=await S("claude",B()),g=k.code===0?J(k.stdout):null,w=await S("claude",q()),y=await S("claude",G(h));if(y.code!==0){let oe=Z([y.stderr,y.stdout].filter(Boolean).join(`
25
+ `).trim()),R;throw w.code!==0?R="No existing entry was removed, so your configuration is unchanged.":g?R=(await S("claude",O(g))).code===0?"Your previous mcp-scraper entry was restored; nothing was lost.":`WARNING: the previous mcp-scraper entry was removed and could NOT be restored. Re-add it with:
26
+ claude `+O(g).join(" "):R="WARNING: the previous mcp-scraper entry was removed and could not be captured for rollback. Re-add it manually.",new Error(["Claude Code MCP registration failed.",oe||"No error output returned.",R,"Make sure Claude Code is installed and the `claude` command is on PATH."].join(`
27
+ `))}let E=await S("claude",["mcp","list"]),M={host:"claude",applied:!0,replacedExisting:w.code===0,command:"npx",args:A(h),nextStep:"Fully exit Claude Code, start a new Claude terminal, then run: claude mcp list",list:Z([E.stdout,E.stderr].filter(Boolean).join(`
28
+ `).trim())};if(t.json){s(M,!0);return}s(["Applied Claude Code MCP config: mcp-scraper",w.code===0?"Replaced existing mcp-scraper entry.":"No existing mcp-scraper entry found; added a new one.","Command: npx "+A(h).join(" "),t.browserProfile?`Browser profile: ${t.browserProfile}`:"","","Next step: fully exit Claude Code, start a new Claude terminal, then run:"," claude mcp list","",M.list?`Current Claude MCP list:
29
+ ${M.list}`:""].filter(Boolean).join(`
30
+ `),!1);return}let C=W(o,{apiKey:t.apiKey,packageSpec:t.package,browserProfileName:t.browserProfile,browserProfileSaveChanges:t.saveBrowserProfileChanges});s(t.json?{host:o,protocolVersion:v,protocolDigest:b,text:C}:C,t.json)}),i.command("prompt [name]").description("Print an agent prompt template.").option("--json","Print machine-readable JSON").action((r,t)=>{if(!r){s(t.json?{prompts:x()}:x().join(`
31
+ `),t.json);return}let o=V(r);s(t.json?{name:r,...r==="xray-setup"?{protocolVersion:v,protocolDigest:b}:{},text:o}:o,t.json)});let p=e.command("workflow").description("Run named SEO workflows.");p.command("list").description("List available workflows.").option("--json","Print machine-readable JSON").action(r=>{let t=T();r.json?s({workflows:t},!0):s(t.map(o=>`${o.id} ${o.title}
32
32
  ${o.description}`).join(`
33
- `),!1)}),p.command("run <id>").description("Run a workflow and save local artifacts.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON").option("--keyword <keyword>","Agent packet keyword").option("--domain <domain>","Target domain").option("--url <url>","Target page URL").option("--location <location>","Target location").option("--max-results <n>","Maximum SERP or Maps results").option("--max-questions <n>","Maximum PAA questions").option("--extract-top <n>","Top result pages or citation pages to extract").option("--depth <n>","PAA expansion depth").option("--no-serp","Skip SERP evidence for agent packet").option("--no-paa","Skip PAA evidence for agent packet").option("--query <query>","Business category or workflow query").option("--state <state>","US state").option("--min-pop <n>","Minimum city population").option("--max-cities <n>","Maximum selected cities").option("--per-city <n>","Maps results per city").option("--concurrency <n>","City search concurrency").option("--proxy-mode <mode>","Proxy mode: location, configured, or none").option("--hydrate-top <n>","Profiles to hydrate per city for competitive audits").option("--reviews <n>","Review cards to collect per hydrated profile").option("--no-return-partial","Fail instead of writing partial artifacts").action(async(t,r)=>{let o=await U(t,ee(t,r),{apiKey:r.apiKey,apiUrl:r.apiUrl,outputDir:r.outputDir});r.json?s(o,!0):s([`${o.title}: ${o.status}`,o.summary,o.reportPath?`Report: ${o.reportPath}`:"",o.warnings.length?`Warnings:
33
+ `),!1)}),p.command("run <id>").description("Run a workflow and save local artifacts.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON").option("--keyword <keyword>","Agent packet keyword").option("--domain <domain>","Target domain").option("--url <url>","Target page URL").option("--location <location>","Target location").option("--max-results <n>","Maximum SERP or Maps results").option("--max-questions <n>","Maximum PAA questions").option("--extract-top <n>","Top result pages or citation pages to extract").option("--depth <n>","PAA expansion depth").option("--no-serp","Skip SERP evidence for agent packet").option("--no-paa","Skip PAA evidence for agent packet").option("--query <query>","Business category or workflow query").option("--state <state>","US state").option("--min-pop <n>","Minimum city population").option("--max-cities <n>","Maximum selected cities").option("--per-city <n>","Maps results per city").option("--concurrency <n>","City search concurrency").option("--proxy-mode <mode>","Proxy mode: location, configured, or none").option("--hydrate-top <n>","Profiles to hydrate per city for competitive audits").option("--reviews <n>","Review cards to collect per hydrated profile").option("--no-return-partial","Fail instead of writing partial artifacts").action(async(r,t)=>{let o=await U(r,Q(r,t),{apiKey:t.apiKey,apiUrl:t.apiUrl,outputDir:t.outputDir});t.json?s(o,!0):s([`${o.title}: ${o.status}`,o.summary,o.reportPath?`Report: ${o.reportPath}`:"",o.warnings.length?`Warnings:
34
34
  ${o.warnings.map(d=>`- ${d}`).join(`
35
35
  `)}`:""].filter(Boolean).join(`
36
- `),!1)});let u=e.command("report").description("List and open local workflow reports.");u.command("list").description("List recent workflow reports.").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON").action(async t=>{let r=await N(t.outputDir);s(t.json?{reports:r}:r.map(o=>`${o.startedAt} ${o.workflow} ${o.status} ${o.reportPath??o.manifestPath}`).join(`
37
- `),t.json)}),u.command("path [id]").description("Print a report path. Defaults to last.").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON").action(async(t="last",r)=>{let o=await M(t,r.outputDir);if(!o?.reportPath)throw new Error(`No report found for "${t}"`);s(r.json?{path:o.reportPath,run:o}:o.reportPath,r.json)}),u.command("open [id]").description("Open a report. Defaults to last.").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON instead of opening").action(async(t="last",r)=>{if(r.json){let d=await M(t,r.outputDir);if(!d?.reportPath)throw new Error(`No report found for "${t}"`);s({path:d.reportPath,run:d},!0);return}let o=await L(t,r.outputDir);s(`Opened: ${o}`,!1)});let a=e.command("schedule").description("Create and manage hosted workflow schedules.");be(a.command("create <workflowId>").description("Create a recurring hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--name <name>","Schedule name").option("--daily","Run daily").option("--weekly","Run weekly").option("--monthly","Run monthly").option("--timezone <tz>","Schedule timezone","UTC").option("--webhook <url>","HTTPS webhook URL").option("--next-run-at <iso>","First run time as an ISO timestamp").option("--json","Print machine-readable JSON")).action(async(t,r)=>{let o=await m("/workflows/schedules","POST",r,{workflowId:t,name:r.name,input:ee(t,r),cadence:ve(r),timezone:r.timezone,webhookUrl:r.webhook,nextRunAt:r.nextRunAt});s(o,r.json)}),a.command("list").description("List hosted workflow schedules.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async t=>{let r=await m("/workflows/schedules","GET",t);t.json?s(r,!0):s(r.schedules.map(o=>`${o.id} ${o.status} ${o.workflow_id} ${o.next_run_at??""}`).join(`
38
- `),!1)}),a.command("pause <id>").description("Pause a hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(t,r)=>s(await m(`/workflows/schedules/${t}`,"PATCH",r,{status:"paused"}),r.json)),a.command("resume <id>").description("Resume a hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(t,r)=>s(await m(`/workflows/schedules/${t}`,"PATCH",r,{status:"active"}),r.json)),a.command("delete <id>").description("Delete a hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(t,r)=>s(await m(`/workflows/schedules/${t}`,"DELETE",r),r.json)),a.command("run <id>").description("Run a hosted workflow schedule now.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(t,r)=>s(await m(`/workflows/schedules/${t}/run`,"POST",r,{}),r.json));let l=e.command("runs").description("Inspect and download hosted workflow runs.");return l.command("list").description("List hosted workflow runs.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async t=>{let r=await m("/workflows/runs","GET",t);t.json?s(r,!0):s(r.runs.map(o=>`${o.id} ${o.status} ${o.workflow_id} ${o.queued_at}`).join(`
39
- `),!1)}),l.command("status <id>").description("Show a hosted workflow run.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(t,r)=>s(await m(`/workflows/runs/${t}`,"GET",r),r.json)),l.command("download <id>").description("Download hosted workflow run artifacts.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--output-dir <path>","Download directory").option("--json","Print machine-readable JSON").action(async(t,r)=>{let o=await m(`/workflows/runs/${t}`,"GET",r),{apiUrl:d,apiKey:C}=oe(r),h=Z(r.outputDir??E(),"workflow-downloads",t);await ke(h,{recursive:!0});let k=[];for(let g of o.run.artifacts??[]){let w=await fetch(`${d}/workflows/runs/${t}/artifacts/${g.id}`,{headers:{"x-api-key":C}});if(!w.ok)throw new Error(`Failed to download ${g.label}: HTTP ${w.status}`);let y=Z(h,Ce(g.path));await Pe(y,Buffer.from(await w.arrayBuffer())),k.push(y)}s(r.json?{runId:t,files:k}:k.join(`
40
- `),r.json)}),e}async function ie(e=process.argv){await Se().parseAsync(e)}ie().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});
36
+ `),!1)});let u=e.command("report").description("List and open local workflow reports.");u.command("list").description("List recent workflow reports.").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON").action(async r=>{let t=await N(r.outputDir);s(r.json?{reports:t}:t.map(o=>`${o.startedAt} ${o.workflow} ${o.status} ${o.reportPath??o.manifestPath}`).join(`
37
+ `),r.json)}),u.command("path [id]").description("Print a report path. Defaults to last.").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON").action(async(r="last",t)=>{let o=await j(r,t.outputDir);if(!o?.reportPath)throw new Error(`No report found for "${r}"`);s(t.json?{path:o.reportPath,run:o}:o.reportPath,t.json)}),u.command("open [id]").description("Open a report. Defaults to last.").option("--output-dir <path>","Workflow output directory").option("--json","Print machine-readable JSON instead of opening").action(async(r="last",t)=>{if(t.json){let d=await j(r,t.outputDir);if(!d?.reportPath)throw new Error(`No report found for "${r}"`);s({path:d.reportPath,run:d},!0);return}let o=await L(r,t.outputDir);s(`Opened: ${o}`,!1)});let a=e.command("schedule").description("Create and manage hosted workflow schedules.");be(a.command("create <workflowId>").description("Create a recurring hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--name <name>","Schedule name").option("--daily","Run daily").option("--weekly","Run weekly").option("--monthly","Run monthly").option("--timezone <tz>","Schedule timezone","UTC").option("--webhook <url>","HTTPS webhook URL").option("--next-run-at <iso>","First run time as an ISO timestamp").option("--json","Print machine-readable JSON")).action(async(r,t)=>{let o=await m("/workflows/schedules","POST",t,{workflowId:r,name:t.name,input:Q(r,t),cadence:ve(t),timezone:t.timezone,webhookUrl:t.webhook,nextRunAt:t.nextRunAt});s(o,t.json)}),a.command("list").description("List hosted workflow schedules.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async r=>{let t=await m("/workflows/schedules","GET",r);r.json?s(t,!0):s(t.schedules.map(o=>`${o.id} ${o.status} ${o.workflow_id} ${o.next_run_at??""}`).join(`
38
+ `),!1)}),a.command("pause <id>").description("Pause a hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(r,t)=>s(await m(`/workflows/schedules/${r}`,"PATCH",t,{status:"paused"}),t.json)),a.command("resume <id>").description("Resume a hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(r,t)=>s(await m(`/workflows/schedules/${r}`,"PATCH",t,{status:"active"}),t.json)),a.command("delete <id>").description("Delete a hosted workflow schedule.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(r,t)=>s(await m(`/workflows/schedules/${r}`,"DELETE",t),t.json)),a.command("run <id>").description("Run a hosted workflow schedule now.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(r,t)=>s(await m(`/workflows/schedules/${r}/run`,"POST",t,{}),t.json));let l=e.command("runs").description("Inspect and download hosted workflow runs.");return l.command("list").description("List hosted workflow runs.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async r=>{let t=await m("/workflows/runs","GET",r);r.json?s(t,!0):s(t.runs.map(o=>`${o.id} ${o.status} ${o.workflow_id} ${o.queued_at}`).join(`
39
+ `),!1)}),l.command("status <id>").description("Show a hosted workflow run.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--json","Print machine-readable JSON").action(async(r,t)=>s(await m(`/workflows/runs/${r}`,"GET",t),t.json)),l.command("download <id>").description("Download hosted workflow run artifacts.").option("--api-key <key>","MCP Scraper API key").option("--api-url <url>","MCP Scraper API URL","https://mcpscraper.dev").option("--output-dir <path>","Download directory").option("--json","Print machine-readable JSON").action(async(r,t)=>{let o=await m(`/workflows/runs/${r}`,"GET",t),{apiUrl:d,apiKey:C}=re(t),h=X(t.outputDir??_(),"workflow-downloads",r);await ke(h,{recursive:!0});let k=[];for(let g of o.run.artifacts??[]){let w=await fetch(`${d}/workflows/runs/${r}/artifacts/${g.id}`,{headers:{"x-api-key":C}});if(!w.ok)throw new Error(`Failed to download ${g.label}: HTTP ${w.status}`);let y=X(h,Ce(g.path));await Pe(y,Buffer.from(await w.arrayBuffer())),k.push(y)}s(t.json?{runId:r,files:k}:k.join(`
40
+ `),t.json)}),e}async function ne(e=process.argv){await Se().parseAsync(e)}ne().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{a as e}from"../chunk-O4LUUIYG.js";import"../chunk-GLTECY3U.js";import"../chunk-WO46LTBW.js";import"../chunk-W2BVJ7S2.js";import"../chunk-KFOFU3LA.js";import"../chunk-LMWU7VPZ.js";import"../chunk-TMB56NCA.js";import"../chunk-HUV2WTRW.js";import"../chunk-YGBTTW5D.js";import"../chunk-EDQ57S4P.js";import"../chunk-GPSPSJBJ.js";import"../chunk-VREXSMC6.js";import"../chunk-6DTXIZY2.js";import"../chunk-K3D2S36Q.js";import"../chunk-WO3N5FH2.js";import"../chunk-HE45FFBU.js";import"../chunk-VXOXB3IB.js";import"../chunk-7YOXSMHG.js";var _=["harvest_paa","search_serp","extract_url","diff_page","map_site_urls","map_wayback_snapshots","extract_site","analyze_site_similarity","audit_site","check_site_export","site_export_read","site_export_image","archive_read","youtube_harvest","youtube_transcribe","facebook_page_intel","facebook_ad_search","reddit_thread","reddit_trending","video_frame_analysis","video_frame_analysis_status","facebook_ad_transcribe","google_ads_search","google_ads_page_intel","google_ads_transcribe","facebook_video_transcribe","instagram_profile_content","instagram_media_download","maps_place_intel","maps_search","trustpilot_reviews","g2_reviews","capture_serp_snapshot","capture_serp_page_snapshots"];e({toolsets:new Set(["paa","serp"]),allowedToolNames:_});
2
+ import{a as e}from"../chunk-LFRAA3MY.js";import"../chunk-ARSQZCTJ.js";import"../chunk-QER2ELZP.js";import"../chunk-OB6E6UTD.js";import"../chunk-7POHNTVW.js";import"../chunk-MVMWPKRC.js";import"../chunk-63VWBMYR.js";import"../chunk-3F7AFM53.js";import"../chunk-RDHGQ2WZ.js";import"../chunk-2IATUJO6.js";import"../chunk-QWE7VKIX.js";import"../chunk-ZPQNY5TI.js";import"../chunk-CNSVZTTT.js";import"../chunk-5BKSKLHX.js";import"../chunk-7SR7MEU5.js";import"../chunk-HUNSEGUN.js";import"../chunk-SEZETSB3.js";import"../chunk-SDQW7IJN.js";import"../chunk-B4ZKPLSP.js";import"../chunk-VVSHZV5S.js";import"../chunk-C7MGJUTU.js";import"../chunk-4ZVO6GX4.js";import"../chunk-WNUXMPVX.js";import"../chunk-VAC3SQ5T.js";import"../chunk-WP3QS4CJ.js";import"../chunk-24YDQ3EO.js";import"../chunk-OLYPLX5C.js";import"../chunk-B3JRL45T.js";import"../chunk-OILOHQNL.js";import"../chunk-7UHDOIUE.js";import"../chunk-HNNYXMSI.js";var _=["harvest_paa","search_serp","extract_url","diff_page","map_site_urls","map_wayback_snapshots","extract_site","analyze_site_similarity","audit_site","check_site_export","site_export_read","site_export_image","archive_read","youtube_harvest","youtube_transcribe","facebook_page_intel","facebook_ad_search","reddit_thread","reddit_trending","video_frame_analysis","video_frame_analysis_status","facebook_ad_transcribe","google_ads_search","google_ads_page_intel","google_ads_transcribe","facebook_video_transcribe","instagram_profile_content","instagram_media_download","maps_place_intel","maps_search","trustpilot_reviews","g2_reviews","capture_serp_snapshot","capture_serp_page_snapshots"];e({toolsets:new Set(["paa","serp"]),allowedToolNames:_});
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{a as s}from"../chunk-K3D2S36Q.js";import{a as e}from"../chunk-VXOXB3IB.js";var r=process.argv.includes("--no-color")||process.env.NO_COLOR!==void 0||process.env.FORCE_COLOR==="0"||!process.stdout.isTTY,n=process.argv.includes("--help")||process.argv.includes("-h");n&&(process.stdout.write(["Usage: mcp-scraper-install [--no-color]","","Prints the branded MCP Scraper terminal install card and copyable install commands.","mcp-scraper prints the same card in a human terminal and runs as the MCP stdio server in clients.",""].join(`
2
+ import{a as s}from"../chunk-VAC3SQ5T.js";import"../chunk-WP3QS4CJ.js";import{a as e}from"../chunk-7UHDOIUE.js";var r=process.argv.includes("--no-color")||process.env.NO_COLOR!==void 0||process.env.FORCE_COLOR==="0"||!process.stdout.isTTY,n=process.argv.includes("--help")||process.argv.includes("-h");n&&(process.stdout.write(["Usage: mcp-scraper-install [--no-color]","","Prints the branded MCP Scraper terminal install card and copyable install commands.","mcp-scraper prints the same card in a human terminal and runs as the MCP stdio server in clients.",""].join(`
3
3
  `)),process.exit(0));process.stdout.write(s({version:e,color:!r,apiKeyConfigured:!!process.env.MCP_SCRAPER_API_KEY?.trim()}));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{a as r}from"../chunk-O4LUUIYG.js";import"../chunk-GLTECY3U.js";import"../chunk-WO46LTBW.js";import"../chunk-W2BVJ7S2.js";import"../chunk-KFOFU3LA.js";import"../chunk-LMWU7VPZ.js";import"../chunk-TMB56NCA.js";import"../chunk-HUV2WTRW.js";import"../chunk-YGBTTW5D.js";import"../chunk-EDQ57S4P.js";import"../chunk-GPSPSJBJ.js";import"../chunk-VREXSMC6.js";import"../chunk-6DTXIZY2.js";import"../chunk-K3D2S36Q.js";import"../chunk-WO3N5FH2.js";import"../chunk-HE45FFBU.js";import"../chunk-VXOXB3IB.js";import"../chunk-7YOXSMHG.js";r();
2
+ import{a as r}from"../chunk-LFRAA3MY.js";import"../chunk-ARSQZCTJ.js";import"../chunk-QER2ELZP.js";import"../chunk-OB6E6UTD.js";import"../chunk-7POHNTVW.js";import"../chunk-MVMWPKRC.js";import"../chunk-63VWBMYR.js";import"../chunk-3F7AFM53.js";import"../chunk-RDHGQ2WZ.js";import"../chunk-2IATUJO6.js";import"../chunk-QWE7VKIX.js";import"../chunk-ZPQNY5TI.js";import"../chunk-CNSVZTTT.js";import"../chunk-5BKSKLHX.js";import"../chunk-7SR7MEU5.js";import"../chunk-HUNSEGUN.js";import"../chunk-SEZETSB3.js";import"../chunk-SDQW7IJN.js";import"../chunk-B4ZKPLSP.js";import"../chunk-VVSHZV5S.js";import"../chunk-C7MGJUTU.js";import"../chunk-4ZVO6GX4.js";import"../chunk-WNUXMPVX.js";import"../chunk-VAC3SQ5T.js";import"../chunk-WP3QS4CJ.js";import"../chunk-24YDQ3EO.js";import"../chunk-OLYPLX5C.js";import"../chunk-B3JRL45T.js";import"../chunk-OILOHQNL.js";import"../chunk-7UHDOIUE.js";import"../chunk-HNNYXMSI.js";r();
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{z as t}from"../chunk-MC4W6TZT.js";import"../chunk-M5QHXNFZ.js";import{b as r}from"../chunk-GPSPSJBJ.js";import"../chunk-VREXSMC6.js";import"../chunk-6DTXIZY2.js";import"../chunk-XPEJB4BZ.js";import"../chunk-7YOXSMHG.js";import{Command as s,Option as a}from"commander";var i=new s;i.name("paa-harvest").description("Recursively extract Google People Also Ask questions").requiredOption("-q, --query <query>","Seed query").option("-l, --location <location>",'Location name (e.g. "austin" or "Austin,Texas,United States")').option("--gl <gl>","Google country code","us").option("--hl <hl>","Google language code","en").option("-d, --depth <depth>","BFS depth (1-30)","3").option("-m, --max-questions <n>","Max questions to harvest","100").option("-o, --output <dir>","Output directory","./paa-output").option("-f, --format <format>","Output format: json, csv, or both","both").option("--headless","Run browser in headless mode",!1).option("--profile <dir>","Persistent browser profile directory").option("--proxy <url>","Proxy server URL").option("--browser-api-key <key>","Browser service API key (or set BROWSER_SERVICE_API_KEY env var)").addOption(new a("--\u006b\u0065\u0072\u006e\u0065\u006c-api-key <key>").hideHelp()).action(async e=>{try{let o=await t({query:e.query,location:e.location,gl:e.gl,hl:e.hl,depth:parseInt(e.depth,10),maxQuestions:parseInt(e.maxQuestions,10),outputDir:e.output,format:e.format,headless:e.headless,profileDir:e.profile,proxy:e.proxy,\u006b\u0065\u0072\u006e\u0065\u006cApiKey:e.browserApiKey??e.\u006b\u0065\u0072\u006e\u0065\u006cApiKey??r()});console.log(JSON.stringify({totalQuestions:o.totalQuestions,outputDir:o.stats.seed}))}catch(o){console.error(o instanceof Error?o.message:String(o)),process.exit(1)}});async function n(){await i.parseAsync()}n();
2
+ import{J as t}from"../chunk-JAV5V3JT.js";import"../chunk-M5QHXNFZ.js";import{d as r}from"../chunk-B4ZKPLSP.js";import"../chunk-VVSHZV5S.js";import"../chunk-C7MGJUTU.js";import"../chunk-4ZVO6GX4.js";import"../chunk-WNUXMPVX.js";import"../chunk-6N5EC7ZO.js";import"../chunk-HNNYXMSI.js";import{Command as s,Option as a}from"commander";var i=new s;i.name("paa-harvest").description("Recursively extract Google People Also Ask questions").requiredOption("-q, --query <query>","Seed query").option("-l, --location <location>",'Location name (e.g. "austin" or "Austin,Texas,United States")').option("--gl <gl>","Google country code","us").option("--hl <hl>","Google language code","en").option("-d, --depth <depth>","BFS depth (1-30)","3").option("-m, --max-questions <n>","Max questions to harvest","100").option("-o, --output <dir>","Output directory","./paa-output").option("-f, --format <format>","Output format: json, csv, or both","both").option("--headless","Run browser in headless mode",!1).option("--profile <dir>","Persistent browser profile directory").option("--proxy <url>","Proxy server URL").option("--browser-api-key <key>","Browser service API key (or set BROWSER_SERVICE_API_KEY env var)").addOption(new a("--\u006b\u0065\u0072\u006e\u0065\u006c-api-key <key>").hideHelp()).action(async e=>{try{let o=await t({query:e.query,location:e.location,gl:e.gl,hl:e.hl,depth:parseInt(e.depth,10),maxQuestions:parseInt(e.maxQuestions,10),outputDir:e.output,format:e.format,headless:e.headless,profileDir:e.profile,proxy:e.proxy,\u006b\u0065\u0072\u006e\u0065\u006cApiKey:e.browserApiKey??e.\u006b\u0065\u0072\u006e\u0065\u006cApiKey??r()});console.log(JSON.stringify({totalQuestions:o.totalQuestions,outputDir:o.stats.seed}))}catch(o){console.error(o instanceof Error?o.message:String(o)),process.exit(1)}});async function n(){await i.parseAsync()}n();
@@ -0,0 +1 @@
1
+ import{n as a,o as b,p as c,q as d,r as e,s as f,t as g}from"./chunk-SDQW7IJN.js";export{g as cleanupPrivateBlobObjects,b as consumePrivateBlob,c as getPrivateBlob,a as metadataBlobId,d as putPrivateBlobFile,e as putPrivateBlobMetadata,f as signPrivateBlob};
@@ -0,0 +1 @@
1
+ import{j as b,k as c}from"./chunk-FRXKMOXI.js";import"./chunk-U4WIJZCX.js";import{E as a}from"./chunk-6SNOUSSZ.js";import"./chunk-KXJCGV3H.js";import"./chunk-JMZIDHYQ.js";import"./chunk-M5QHXNFZ.js";import"./chunk-CNSVZTTT.js";import"./chunk-5BKSKLHX.js";import"./chunk-7SR7MEU5.js";import"./chunk-HUNSEGUN.js";import"./chunk-SEZETSB3.js";import"./chunk-SDQW7IJN.js";import"./chunk-C7MGJUTU.js";import"./chunk-4ZVO6GX4.js";import"./chunk-WNUXMPVX.js";import"./chunk-OLYPLX5C.js";import"./chunk-B3JRL45T.js";import"./chunk-OILOHQNL.js";import"./chunk-HNNYXMSI.js";export{b as MAX_ANALYZED_LINK_EDGES,a as SITE_EXTRACT_PAGE_CHUNK,c as assembleExtractArtifacts};
@@ -0,0 +1,5 @@
1
+ import{a as S}from"./chunk-OLYPLX5C.js";import{a as P}from"./chunk-B3JRL45T.js";import{createHash as N}from"crypto";var k="3.0.0",x="analytics_setup_site",U="Install X-Ray",C="Install X-Ray and tell me what drives customers",m=`Call ${x} with { "startUrl": "<site URL>", "goal": "${C}" }; on every later call, reuse the returned setupId and revision.`,_=JSON.stringify({mcpServers:{"mcp-scraper":{command:"npx",args:["-y","--package","mcp-scraper@latest","mcp-scraper"],env:{MCP_SCRAPER_API_KEY:"${env:MCP_SCRAPER_API_KEY}"}}}},null,2),H=JSON.stringify({mcpServers:{"mcp-scraper":{url:"https://mcpscraper.dev/mcp"}}},null,2),I={schema:"https://mcpscraper.dev/schemas/xray-install-contract-v1.json",protocolVersion:k,product:"Thorbit X-Ray",triggerPhrases:[U,"Install X-Ray on this site and tell me what drives customers"],canonicalUrls:{markdown:"https://mcpscraper.dev/agent-install.md",json:"https://mcpscraper.dev/.well-known/xray-install.json",hostedMcp:"https://mcpscraper.dev/mcp"},facade:{tool:x,initialCall:{startUrl:"<site URL>",goal:C},continuationRule:"Treat setupId plus revision as the durable continuation handle. Reuse both after approval, runtime publication, verification, retry, or return."},topologies:{local_stdio:{label:"Local npm MCP process",authority:"The client starts MCP Scraper locally. After the one-time Pixel insertion, X-Ray setup publishes only owner-confirmed runtime configuration.",secretHandling:"Set MCP_SCRAPER_API_KEY in the local environment or client secret field. Never paste it into chat or commit it."},hosted_remote:{label:"Hosted authenticated MCP",authority:"The hosted MCP inspects approved public pages and publishes owner-confirmed X-Ray runtime configuration. It cannot read or write a caller-local path.",secretHandling:"Use the MCP OAuth/connection flow. Never put a credential in a URL, prompt, or config shown in chat."}},hosts:[{id:"codex",label:"Codex",supportedTopologies:["local_stdio","hosted_remote"],mcpConfigMode:"toml",workspaceRead:!0,workspaceWrite:!0,restartRequired:!0,exactConfig:`[mcp_servers.mcp-scraper]
2
+ command = "npx"
3
+ args = ["-y", "--package", "mcp-scraper@latest", "mcp-scraper"]
4
+ env_vars = ["MCP_SCRAPER_API_KEY"]`,continuation:m,rollback:"Remove the mcp_servers.mcp-scraper table, then restart Codex. Pixel removal is a separate owner-controlled site action."},{id:"claude-code",label:"Claude Code",supportedTopologies:["local_stdio","hosted_remote"],mcpConfigMode:"command",workspaceRead:!0,workspaceWrite:!0,restartRequired:!0,exactConfig:'claude mcp add mcp-scraper --scope user --env MCP_SCRAPER_API_KEY="$MCP_SCRAPER_API_KEY" -- npx -y --package mcp-scraper@latest mcp-scraper',continuation:m,rollback:"Run: claude mcp remove mcp-scraper -s user. Pixel removal is a separate owner-controlled site action."},{id:"claude-desktop",label:"Claude Desktop",supportedTopologies:["local_stdio"],mcpConfigMode:"desktop_extension",workspaceRead:!1,workspaceWrite:!1,restartRequired:!0,exactConfig:"Install https://mcpscraper.dev/downloads/mcp-scraper.mcpb and enter the API key only in Claude Desktop's protected configuration field.",continuation:m,rollback:"Remove the MCP Scraper extension in Claude Desktop. Pixel removal is a separate owner-controlled site action."},{id:"cursor",label:"Cursor",supportedTopologies:["local_stdio","hosted_remote"],mcpConfigMode:"json",workspaceRead:!0,workspaceWrite:!0,restartRequired:!0,exactConfig:_,continuation:m,rollback:"Remove mcp-scraper from .cursor/mcp.json (or the user MCP settings), then restart Cursor."},{id:"windsurf",label:"Windsurf",supportedTopologies:["local_stdio"],mcpConfigMode:"json",workspaceRead:!0,workspaceWrite:!0,restartRequired:!0,exactConfig:_,continuation:m,rollback:"Remove mcp-scraper from the Windsurf MCP configuration, then restart Windsurf."},{id:"cline",label:"Cline",supportedTopologies:["local_stdio"],mcpConfigMode:"json",workspaceRead:!0,workspaceWrite:!0,restartRequired:!0,exactConfig:_,continuation:m,rollback:"Remove mcp-scraper from Cline MCP Servers, then restart the extension host."},{id:"user-action-only",label:"User-action-only MCP host",supportedTopologies:["hosted_remote"],mcpConfigMode:"handoff_only",workspaceRead:!1,workspaceWrite:!1,restartRequired:!1,exactConfig:H,continuation:m,rollback:"Disconnect MCP Scraper from the host. Pixel removal remains an owner-controlled site action."}],tagBootstrap:{requirement:"one_global_pixel_once",ongoingConfiguration:"xray_runtime_manifest",sourceAccessRequiredAfterInstall:!1,supportedInsertionPaths:["manual","tag_manager","cms_plugin","coding_host"]},workflow:["Resolve Thorbit X-Ray entitlement and the target site without consuming MCP Scraper Credits.","Inspect the whole approved public site; enumerate distinct renderers, routes, and Pixel coverage.","Infer the business model and customer outcome, then propose one privacy-safe measurement plan.","If the Pixel is absent, return the canonical one-time tag and exact uncovered surfaces; do not claim ongoing control there.","Publish an approved measurement revision as a digest-bound runtime manifest; source or repository access is not required.","For SaaS, verify the measurement foundation: global tag coverage, ad-source capture, lifecycle events, currency and timezone, consent, and observed outcome events.","Optionally publish a structured post-purchase survey after approved disclosure evidence; reported influence remains separate from deterministic touches.","Compare observed attribution with customer-reported influence only with explicit weights totaling 1,000,000 micros and a named methodology version.","Keep view-through reporting unavailable until event-level provider evidence can join to an eligible X-Ray subject; aggregate impressions never qualify.","Verify route coverage, consent, PageViews, manifest convergence, bounded rule matches, and persisted definition/version evidence.","Require a separate exact-revision approval before any canary or real-visitor experiment activation.","Return first customer-journey answers when sufficient, or exact evidence-readiness thresholds when not.","On \u201Cimprove\u201D, propose one highest-value uncovered outcome, require approval when needed, and reverify."],safetyRules:["Never invent or hand-author tracking code; use the canonical product-authored Pixel snippet returned by analytics_setup_site.","Never ask for or print API keys, OAuth tokens, signed URLs, cookies, or other credentials.","Never call ad platforms directly or silently activate events, definitions, funnels, or delivery destinations.","Never treat a CTA click as a customer, sale, or revenue event without verified outcome evidence.","Never claim full-domain installation while an in-scope route or renderer is missing, excluded without approval, or unverifiable.","Never treat event-name presence as verification; require persisted definition ID and version evidence.","Never accept survey invitation tokens or exchange codes through MCP inputs or prompts.","Never treat customer-reported influence as identity, CRM mutation authority, ad-activation evidence, or a deterministic touch."],terminalStates:["complete","tag_required","input_required","approval_required","runtime_convergence_required","not_ready","error"]};function E(t){return Array.isArray(t)?`[${t.map(E).join(",")}]`:t&&typeof t=="object"?`{${Object.entries(t).sort(([e],[r])=>e.localeCompare(r)).map(([e,r])=>`${JSON.stringify(e)}:${E(r)}`).join(",")}}`:JSON.stringify(t)}function j(){return E(I)}var O=`sha256:${N("sha256").update(j(),"utf8").digest("hex")}`,L={...I,protocolDigest:O};function de(t){let e=L.hosts.find(r=>r.id===t);if(!e)throw new Error(`Unknown X-Ray host: ${t}`);return e}function me(){return["# Install X-Ray","",`Protocol: ${k} (${O})`,"",`Use ${x} as the single conversational setup facade. Ask for the target site URL if it is missing, then call it with the goal \u201C${C}\u201D.`,"","Continue the same setup with its setupId and revision. Confirm one global Pixel across all public rendering surfaces; present one measurement plan; publish it as a digest-bound runtime manifest after exact-revision approval; verify manifest and rule versions; then return first customer-journey answers or explicit readiness thresholds. For SaaS, verify the provider-neutral measurement foundation. Browser events never prove paid revenue. GitHub or repository access is not required after the tag is installed.","","Do not request pasted credentials, invent tracking code, silently activate measurement, call ad platforms directly, or claim customers/revenue from CTA clicks."].join(`
5
+ `)}function fe(t=L){return JSON.parse(JSON.stringify(t))}var q=20,D=20,W=10,z=20,w=280,Y=/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,F=[".png",".jpg",".jpeg",".gif",".svg",".webp",".ico","@2x","sentry","wix.com","wixpress","example.","godaddy","squarespace","schema.org","u003e","core-js"],G=/\b(?:website|site|web)\s+(?:by|built|design(?:ed)?|develop(?:ed)?|powered)\b|\b(?:developer|webmaster)\b/i,J=/(?:^|\/)(?:about|bio|contact|contact-us|meet|our-team|people|profile|staff|team)(?:\/|$)/i,V={"facebook.com":"facebook","fb.com":"facebook","instagram.com":"instagram","linkedin.com":"linkedin","twitter.com":"twitter","x.com":"x","youtube.com":"youtube","youtu.be":"youtube","tiktok.com":"tiktok","pinterest.com":"pinterest"},K=/(?:\/shar(?:e|er)|\/intent\/|\/dialog\/|\/plugins\/|\/share\?|\/developers?\/|platform\.)/i;function R(t){return(t??"").replace(/\s+/g," ").trim()}function b(t,e){try{let r=new URL(t,e);return!["http:","https:"].includes(r.protocol)||r.username||r.password?null:r}catch{return null}}function g(t){let e=t;try{e=decodeURIComponent(t)}catch{}let r=e.split("?")[0].trim().toLowerCase().replace(/[),.;:]+$/,"");return r.length>254||r.split("@").length!==2||!/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9.-]+\.[a-z]{2,}$/i.test(r)||F.some(n=>r.includes(n))?null:r}function f(t){let e=t.closest("p,li,address,section,article,div").first();return R(e.length?e.text():t.text()).slice(0,w)}function X(t,e,r,n){let s=g(e);!s||G.test(n)||t.has(s)||t.set(s,{value:s,sourceUrl:r,context:R(n).slice(0,w),sourceType:"first_party_page"})}function B(t){t.hash="";for(let e of[...t.searchParams.keys()])/^(?:utm_|fbclid|gclid)/i.test(e)&&t.searchParams.delete(e);return t.href}function Z(t){let e=t.toLowerCase().replace(/^www\./,"");return Object.entries(V).find(([n])=>e===n||e.endsWith(`.${n}`))?.[1]??null}function Q(t,e){let r=b(e);if(!r)throw new Error("pageUrl must be a public HTTP(S) URL");let{$:n}=S(t),s=new Map,p=new Map,o=new Map,c=new Map;return n('a[href^="mailto:" i]').each((u,a)=>{let i=n(a);X(s,(i.attr("href")??"").slice(7),r.href,f(i))}),n("body").find("*").addBack().contents().filter((u,a)=>a.type==="text").each((u,a)=>{let i=n(a.parent??a),l=String(a.data??"");for(let d of l.match(Y)??[])X(s,d,r.href,f(i))}),n("a[href]").each((u,a)=>{let i=n(a),l=b(i.attr("href")??"",r.href);if(!l)return;let d=R(`${i.text()} ${f(i)}`).slice(0,w),T=Z(l.hostname);if(T&&!K.test(`${l.pathname}${l.search}`)){let v=B(l);p.has(v)||p.set(v,{url:v,network:T,sourceUrl:r.href,context:d});return}if(l.origin!==r.origin||!J.test(l.pathname))return;l.hash="";let y=l.href;c.has(y)||c.set(y,{url:y,sourceUrl:r.href,context:d})}),n("form").each((u,a)=>{let i=n(a),l=b(i.attr("action")||r.href,r.href);if(!l||l.origin!==r.origin)return;l.hash="";let d=l.href;o.has(d)||o.set(d,{url:d,sourceUrl:r.href,context:f(i)})}),{emails:[...s.values()].slice(0,q),socialProfiles:[...p.values()].slice(0,D),contactForms:[...o.values()].slice(0,W),candidatePageUrls:[...c.values()].slice(0,z)}}var ee=new Set(["admin","contact","customerservice","hello","help","info","inquiries","mail","office","reception","sales","service","support","team"]),te=/(?:^|\/)(?:about|bio|contact|contact-us|meet|our-team|people|profile|staff|team)(?:\/|$)/i,re=/\b(?:our team|meet the team|staff|agents?|brokers?|realtors?|people)\b/i,oe=/\b(?:brokerage|company|corporate|front desk|main office|office|organization|reception)\b/i;function M(t){return t.toLowerCase().replace(/[^a-z0-9]+/g," ").trim().split(/\s+/).filter(Boolean)}function A(t,e){try{let r=new URL(t,e);return!["http:","https:"].includes(r.protocol)||r.username||r.password?null:r}catch{return null}}function ne(t,e){let r=t.value.split("@")[0].toLowerCase().replace(/[^a-z0-9]+/g," "),n=M(e.name).filter(a=>a.length>1),s=A(t.sourceUrl)?.pathname.toLowerCase()??"",p=`${t.context} ${s}`.toLowerCase(),o=ee.has(r.replace(/\s+/g,"")),c=n.filter(a=>new RegExp(`(?:^|[^a-z0-9])${a}(?:$|[^a-z0-9])`,"i").test(`${r} ${p}`)).length,u=e.entityType==="person"&&n.length>=2&&c>=Math.min(2,n.length)&&(r.includes(n[0])||s.includes(n.join("-"))||s.includes(n.join("/")));return!o&&u?{scope:"person",reasons:["named_person_page_context"]}:o||oe.test(p)?{scope:"office",reasons:o?["generic_mailbox_prefix"]:["organization_page_context"]}:re.test(p)?{scope:"team",reasons:["team_page_context"]}:e.entityType==="business"&&M(e.organization||e.name).filter(i=>i.length>2).some(i=>p.includes(i))?{scope:"team",reasons:["business_context"]}:{scope:"unknown",reasons:["public_occurrence_without_identity_evidence"]}}function ve(t,e){let r=t.map(o=>({...o,...ne(o,e)})),n=e.entityType==="person"?["person","team"]:["person","team","office"],s=["provided","first_party_page","structured_data","serp_snippet"],p=["person","team","office"];return r.filter(o=>n.includes(o.scope)).sort((o,c)=>s.indexOf(o.sourceType)-s.indexOf(c.sourceType)||p.indexOf(o.scope)-p.indexOf(c.scope))[0]??null}function _e(t,e,r){if(!Number.isInteger(r)||r<1||r>4)throw new Error("maxPages must be an integer from 1 to 4");let n=A(t);if(!n)return[];n.hash="";let s=new Set([n.href]),p=e.map(o=>typeof o=="string"?o:o.url).map(o=>A(o,n.href)).filter(o=>!!(o&&o.origin===n.origin&&te.test(o.pathname)));for(let o of p)o.hash="",s.add(o.href);for(let o of["/contact","/contact-us"])s.add(new URL(o,n.origin).href);return[...s].slice(0,r)}var se=/[A-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,ie=/\b(?:website|site|web)\s+(?:by|built|design(?:ed)?|develop(?:ed)?|powered)\b|\b(?:developer|webmaster)\b/i,ae=280,ce=10;function h(t){return t.toLowerCase().replace(/^www\./,"").replace(/\.$/,"")}function le(t){let e=h(t);return e==="localhost"||e.endsWith(".localhost")||e.endsWith(".local")||!e.includes(".")&&!e.includes(":")}function $(t){try{let e=new URL(t.trim());return!["http:","https:"].includes(e.protocol)||e.username||e.password||le(e.hostname)||P(e.hostname)?null:e}catch{return null}}function pe(t,e){return`${t} ${e??""}`.replace(/\s+/g," ").trim().slice(0,ae)}function we(t){let e=$(t);return e?(e.hostname=h(e.hostname),`email for ${e.protocol}//${e.host}/`):null}function Re({websiteUrl:t,results:e}){let r=$(t);if(!r)return[];let n=h(r.hostname),s=new Map,p=e.map((o,c)=>({result:o,index:c})).sort((o,c)=>{let u=Number.isFinite(o.result.position)?o.result.position:Number.MAX_SAFE_INTEGER,a=Number.isFinite(c.result.position)?c.result.position:Number.MAX_SAFE_INTEGER;return u-a||o.index-c.index});for(let{result:o}of p){let c=`${o.title} ${o.snippet??""}`,u=pe(o.title,o.snippet);for(let a of c.match(se)??[]){let i=g(a);!i||ie.test(c)||h(i.slice(i.lastIndexOf("@")+1))!==n||s.has(i)||s.set(i,{value:i,sourceUrl:o.url,sourceTitle:o.title,sourcePosition:o.position,context:u,sourceType:"serp_snippet"})}}return[...s.values()].slice(0,ce)}export{Q as a,ne as b,ve as c,_e as d,we as e,Re as f,k as g,x as h,C as i,O as j,L as k,de as l,me as m,fe as n};