mcp-scraper 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/bin/api-server.cjs +15730 -7780
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-stdio-server.cjs +300 -110
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +1 -1
- package/dist/bin/paa-harvest.cjs +1537 -165
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/{chunk-ZBP4RHNW.js → chunk-4743MZHT.js} +298 -106
- package/dist/chunk-4743MZHT.js.map +1 -0
- package/dist/{chunk-LXZDJJXR.js → chunk-D4CJBZBY.js} +426 -29
- package/dist/chunk-D4CJBZBY.js.map +1 -0
- package/dist/chunk-HERFK7W6.js +2781 -0
- package/dist/chunk-HERFK7W6.js.map +1 -0
- package/dist/chunk-Y74EXABN.js +295 -0
- package/dist/chunk-Y74EXABN.js.map +1 -0
- package/dist/{db-IOYMX64U.js → db-YWCNHBLH.js} +36 -4
- package/dist/index.cjs +1660 -237
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +169 -2
- package/dist/index.d.ts +169 -2
- package/dist/index.js +120 -69
- package/dist/index.js.map +1 -1
- package/dist/server-N7Q6H4OR.js +11612 -0
- package/dist/server-N7Q6H4OR.js.map +1 -0
- package/dist/{worker-3ECJHPRE.js → worker-D4D2YQTA.js} +44 -9
- package/dist/worker-D4D2YQTA.js.map +1 -0
- package/package.json +17 -5
- package/dist/chunk-4API3ZCT.js +0 -1387
- package/dist/chunk-4API3ZCT.js.map +0 -1
- package/dist/chunk-LXZDJJXR.js.map +0 -1
- package/dist/chunk-ZBP4RHNW.js.map +0 -1
- package/dist/server-63DR2HE5.js +0 -6062
- package/dist/server-63DR2HE5.js.map +0 -1
- package/dist/worker-3ECJHPRE.js.map +0 -1
- /package/dist/{db-IOYMX64U.js.map → db-YWCNHBLH.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/blog/registry.ts","../src/blog/template.ts","../src/blog/og.ts","../src/api/server.ts","../src/api/url-utils.ts","../src/api/kpo-extractor.ts","../src/api/kernel-fetch.ts","../src/lib/screenshot.ts","../src/lib/branding-extractor.ts","../src/lib/media-extractor.ts","../src/api/site-mapper.ts","../src/api/site-extractor.ts","../src/inngest/client.ts","../src/lib/llm-parse-with-retry.ts","../src/lib/http-client.ts","../src/services/site-architecture-auditor/site-audit-service.ts","../src/services/site-architecture-auditor/schemas.ts","../src/lib/site-architecture-auditor/rules.ts","../src/services/site-architecture-auditor/prompts/ingest-validate.ts","../src/services/site-architecture-auditor/prompts/enrich-competitor.ts","../src/services/site-architecture-auditor/prompts/build-graph-per-site.ts","../src/services/site-architecture-auditor/prompts/metrics-classify.ts","../src/services/site-architecture-auditor/prompts/compare-recommend.ts","../src/services/site-architecture-auditor/prompts/score-synthesize.ts","../src/services/site-architecture-auditor/site-audit-repository.ts","../src/services/site-architecture-auditor/python/graph-runner.ts","../src/services/site-architecture-auditor/factory.ts","../src/services/site-architecture-auditor/phases.ts","../src/inngest/functions/site-audit.ts","../src/api/site-audit-routes.ts","../src/api/site-audit-middleware.ts","../src/api/youtube-routes.ts","../src/youtube/youtube-harvest.ts","../src/youtube/schemas.ts","../src/youtube/YouTubeExtractor.ts","../src/youtube/MP3Downloader.ts","../src/youtube/YTProgressReporter.ts","../src/youtube/CaptionFetcher.ts","../src/api/api-auth.ts","../src/api/server-schemas.ts","../src/api/screenshot-routes.ts","../src/api/facebook-ad-routes.ts","../src/extractor/FacebookAdExtractor.ts","../src/api/maps-routes.ts","../src/extractor/MapsNavigator.ts","../src/extractor/MapsReviewCollector.ts","../src/extractor/MapsExtractor.ts","../src/api/serp-intelligence-routes.ts","../src/serp-intelligence/page-snapshot-extractor.ts","../src/serp-intelligence/schemas.ts","../src/serp-intelligence/serp-capture-service.ts","../src/mcp/mcp-routes.ts","../src/api/stripe-routes.ts","../src/api/site-audit-worker.ts","../src/api/billing-schemas.ts","../src/api/credit-operations.ts","../src/api/session.ts"],"sourcesContent":["import type { BlogPost } from './types.js'\n\nexport const posts: BlogPost[] = [\n {\n slug: 'ai-development-workflows',\n title: 'AI Development Workflows: A Complete Guide',\n titleDisplay: 'AI Development',\n titleDisplayItalic: 'Workflows.',\n description: 'What they are, how they\\'re built, which tools actually matter, and the mistakes most teams make before they figure it out.',\n publishedAt: '2026-05-23',\n author: 'MCP Scraper',\n authorInitials: 'M',\n tags: ['AI', 'workflows', 'development', 'automation'],\n category: 'AI Workflows',\n badge: '49 questions answered',\n fieldGuideLabel: 'Field guide',\n deck: 'What they are, how they\\'re built, which tools actually matter, and the mistakes most teams make before they figure it out.',\n readTimeMinutes: 12,\n stats: [\n { value: '5', label: 'sections' },\n { value: '49', label: 'questions' },\n { value: '4', label: 'core stages' },\n { value: '0', label: 'fluff' },\n ],\n ctaHeading: 'Build faster with',\n ctaHeadingItalic: 'real data.',\n ctaBody: 'MCP Scraper gives your AI workflows the web intelligence they need — SERP data, People Also Ask harvests, page extraction, YouTube transcripts, and more. All via API or MCP server.',\n sections: [\n {\n id: 's1',\n num: '01',\n title: 'What is an AI',\n titleItalic: 'Workflow?',\n deck: 'The definition most people skip, and why skipping it costs them three months of rework.',\n callout: {\n eyebrow: 'Quick take',\n heading: 'An AI workflow is not an AI tool.',\n body: 'A tool does one thing. A workflow connects data, model, and action into a loop that runs without you. <strong>Most teams build tools. Winners build workflows.</strong>',\n },\n cards: [\n {\n id: 'q1-1', num: '1.1',\n question: 'What is an example of an AI workflow?',\n answer: 'An AI workflow is a connected sequence where an AI model handles one or more steps in a larger process. A concrete example: a model reads an incoming invoice, extracts line items, cross-references them against a purchase order in your database, flags discrepancies, and routes it to the right approver — all automatically. <strong>The human only touches the exceptions.</strong> That\\'s the leverage. Single-step automations (just a prompt, just a classification) are AI tools. Workflows string those steps together with state and decision logic.',\n },\n {\n id: 'q1-2', num: '1.2',\n question: 'What is the basic workflow of AI?',\n answer: 'The core components of any AI workflow are: <strong>agents</strong> that perform tasks or make decisions, <strong>data pipelines</strong> that feed those agents, <strong>tool integrations</strong> that let agents take action in external systems, and <strong>feedback loops</strong> that improve outputs over time. Strip away any one of those and you have a prototype, not a workflow. The feedback loop is where most teams cut corners — and it\\'s the one that compounds.',\n },\n {\n id: 'q1-3', num: '1.3',\n question: 'What are the four stages of an AI workflow?',\n answer: 'The four stages are: <strong>(1) Data input</strong> — structured or unstructured data enters the system. <strong>(2) Processing and analysis</strong> — the model interprets, classifies, or extracts. <strong>(3) Decision-making</strong> — based on the model\\'s output, the workflow branches. <strong>(4) Output with feedback</strong> — an action is taken and the result is logged so future runs improve. Most implementations nail stages 1–3 and forget 4. That\\'s why they plateau.',\n },\n {\n id: 'q1-4', num: '1.4',\n question: 'What are the four types of workflows?',\n answer: '<strong>Sequential</strong> — tasks run in a fixed order. Good for predictable processes. <strong>Parallel</strong> — multiple tasks run simultaneously. Good for speed. <strong>State machine</strong> — waits for an event to transition. Good for long-running or human-in-the-loop processes. <strong>Rules-driven</strong> — conditional logic branches based on data values. Good for compliance or tiered routing. Most real AI workflows combine two or three of these.',\n },\n {\n id: 'q1-5', num: '1.5',\n question: 'What is the AI project cycle?',\n answer: 'The AI Project Cycle runs: <strong>problem definition → data collection → model selection → evaluation → deployment → monitoring</strong>. Deployment and monitoring take longer than most teams budget — usually 60% of total project time. Skipping proper problem definition is where 85% of AI projects fail before they start.',\n },\n ],\n },\n {\n id: 's2',\n num: '02',\n title: 'Stages of',\n titleItalic: 'Development.',\n deck: 'How AI systems mature from prototype to production — and the adoption curve most organizations get stuck on.',\n cards: [\n {\n id: 'q2-1', num: '2.1',\n question: 'What are the 5 stages of AI adoption?',\n answer: 'Five stages: <strong>Aware</strong> (experimenting with prompts), <strong>Active</strong> (running pilots), <strong>Operational</strong> (AI is a production dependency), <strong>Systemic</strong> (AI shapes how teams are structured), and <strong>Transformational</strong> (the business model itself changes). Most teams stall at Operational — they have working AI but it hasn\\'t changed how decisions get made.',\n },\n {\n id: 'q2-2', num: '2.2',\n question: 'What are the 5 layers of AI development?',\n answer: 'Infrastructure → Data → Model development and operations → Application → Cross-layer governance. The cross-layer governance piece is the one that gets ignored until there\\'s an incident. When you have AI making decisions in production, you need an audit trail, rollback capability, and ownership assignments at every layer — before something goes wrong, not after.',\n },\n {\n id: 'q2-3', num: '2.3',\n question: 'What are the 8 stages of a workflow?',\n answer: 'Creation → Initiation → Execution → Review → Approval → Documentation → Archival → Iteration. The Iteration stage is where AI adds disproportionate value — a workflow that can learn from its own execution history will outperform a static one within weeks.',\n },\n {\n id: 'q2-4', num: '2.4',\n question: 'What are the 4 types of AI?',\n answer: '<strong>Reactive</strong> (no memory), <strong>Limited memory</strong> (uses recent context — most modern LLMs), <strong>Theory of mind</strong> (not yet achieved), <strong>Self-aware</strong> (theoretical). Every AI in production today is limited memory. The \"agentic AI\" hype is largely about making limited-memory systems behave more like theory-of-mind ones through tool use and persistent context.',\n },\n {\n id: 'q2-5', num: '2.5',\n question: 'What are the 4 pillars of AI?',\n answer: 'Across frameworks, the consistent pillars are: <strong>Data</strong> (quality and volume), <strong>Compute</strong> (infrastructure and cost), <strong>Algorithms</strong> (model architecture), and <strong>People</strong> (domain expertise to supervise and improve the system). Of these, People is the longest-lead bottleneck. You can rent compute and buy data. You can\\'t rapidly acquire practitioners who know both the domain and the models.',\n },\n ],\n },\n {\n id: 's3',\n num: '03',\n title: 'Tools &',\n titleItalic: 'Platforms.',\n deck: 'The honest rundown on what\\'s actually useful versus what\\'s just well-funded.',\n cards: [\n {\n id: 'q3-1', num: '3.1',\n question: 'What is the best AI workflow tool?',\n answer: 'It depends on where you sit on the complexity curve. <strong>No-code</strong>: Zapier AI, Make, monday.com. <strong>Low-code</strong>: n8n, Pipedream. <strong>Code-first</strong>: LangChain, LlamaIndex, custom Claude/GPT API integrations. No-code gets you to 80% quickly and hits a wall. Code-first has no ceiling but requires engineering time. Most production teams end up hybrid.',\n },\n {\n id: 'q3-2', num: '3.2',\n question: 'Can ChatGPT create workflows?',\n answer: 'Yes — ChatGPT can design, describe, and write the code for workflows. It can also be a step inside a workflow via the API. The distinction matters: using ChatGPT to <em>build</em> a workflow is a productivity tool. Using the API as a <em>node</em> in a running workflow is an architectural decision. The latter is where teams underestimate latency and cost at scale.',\n },\n {\n id: 'q3-3', num: '3.3',\n question: 'Which AI tool is most popular?',\n answer: 'ChatGPT remains the most widely recognized AI tool. But popularity in a consumer context doesn\\'t translate to best-in-class for workflows. <strong>Claude 3.5 Sonnet</strong> is widely considered superior for nuanced writing, coding, and reasoning tasks as of mid-2026. Gemini leads on multimodal and Google Workspace integration. Match the model to the task, not the brand recognition.',\n },\n {\n id: 'q3-4', num: '3.4',\n question: 'What are the 7 components of AI?',\n answer: 'For an AI agent architecture: <strong>goal definition, perception/input, memory, reasoning/planning, tool use, action execution, and output/feedback</strong>. This maps directly to workflow design. Goal = trigger condition. Perception = data ingestion. Memory = context + retrieval. Reasoning = the model call. Tool use = API integrations. Action = write to DB, send message. Feedback = log outcome for evaluation.',\n },\n ],\n },\n {\n id: 's4',\n num: '04',\n title: 'Building',\n titleItalic: 'Workflows.',\n deck: 'The practical how — from blank canvas to something running in production.',\n callout: {\n eyebrow: 'Before you build',\n heading: 'Map the failure modes first.',\n body: 'Draw the workflow. Then ask: what happens when the model returns garbage? What happens when the API is down? <strong>Every branch that leads to silent failure needs a fallback before you ship.</strong>',\n },\n cards: [\n {\n id: 'q4-1', num: '4.1',\n question: 'How do you generate a workflow using AI?',\n answer: 'Connect a data source (email, form, webhook) → define what the AI model does with that data (classify, extract, generate) → wire the output to an action (update a record, send a message, trigger another step). The hard part is writing the prompt that\\'s robust to edge cases — that takes iteration and logging, not just a clever initial draft.',\n },\n {\n id: 'q4-2', num: '4.2',\n question: 'How do you develop workflows?',\n answer: 'Define a clear, measurable goal. Map chronological tasks. Assign ownership at each step (human or AI). Select tools. Build the happy path first, then stress-test with edge cases. The most common mistake is building the automation before establishing the baseline metric — if you don\\'t know your current error rate, you can\\'t prove the AI improved it.',\n },\n {\n id: 'q4-3', num: '4.3',\n question: 'What are the 4 C\\'s of AI compliance?',\n answer: 'In the context of responsible AI workflow design: <strong>Compliance</strong> (meets regulatory requirements), <strong>Confidence</strong> (can you quantify the model\\'s certainty), <strong>Consistency</strong> (same behavior on similar inputs), and <strong>Clarity</strong> (can you explain the output). These aren\\'t theoretical — they\\'re the questions an auditor asks when a workflow makes a wrong decision at scale.',\n },\n {\n id: 'q4-4', num: '4.4',\n question: 'What is L1 L2 L3 in AI workflows?',\n answer: '<strong>L1</strong> handles routine, rule-based tasks. <strong>L2</strong> handles exceptions with AI-assisted decision-making, escalating to humans when confidence is low. <strong>L3</strong> handles complex, judgment-intensive tasks where AI augments human expertise. Deploy L1 broadly (high ROI, low risk), L2 selectively, and L3 sparingly — not because L3 isn\\'t valuable but because it requires the most oversight.',\n },\n ],\n },\n {\n id: 's5',\n num: '05',\n title: 'Challenges &',\n titleItalic: 'Best Practices.',\n deck: 'Why 85% of AI projects fail — and what the 15% do differently.',\n cards: [\n {\n id: 'q5-1', num: '5.1',\n question: 'What is the biggest problem with AI?',\n answer: 'In production workflows, the biggest problem is <strong>lack of transparency</strong> in how models make decisions. When a workflow produces a wrong output, you need to know which step failed and why. Without logging and explainability tooling built in from day one, debugging becomes archaeology. Second biggest: data quality. Models are amplifiers — they amplify good data into great outputs and bad data into confidently wrong ones.',\n },\n {\n id: 'q5-2', num: '5.2',\n question: 'Why do 85% of AI projects fail?',\n answer: 'Top reasons: vague problem definition (no measurable success condition), poor data quality, underestimating deployment and monitoring cost, building for the demo rather than the edge case, and lack of domain expertise on the team. Most projects \"fail\" by not reaching production, not by producing wrong results. Getting to production is an organizational problem more than a technical one.',\n },\n {\n id: 'q5-3', num: '5.3',\n question: 'What skills are needed to work in AI workflows?',\n answer: 'Core: prompt engineering, API integration, basic Python or JavaScript, data cleaning fundamentals. Differentiating: systems thinking (understanding how components fail), domain expertise, evaluation methodology (how do you score outputs?), and cost modeling (how do you prevent runaway API spend?). The actual bottleneck in most organizations is people who can scope, build, and evaluate a workflow end to end.',\n },\n {\n id: 'q5-4', num: '5.4',\n question: 'What do humans have that AI can never have?',\n answer: 'Accountability. A model can produce an output; only a human can own the consequence of acting on it. This is the non-technical moat for human workers in AI-augmented workflows. Design your workflows with explicit human ownership of outcomes, not just human review of outputs.',\n },\n ],\n },\n ],\n },\n {\n slug: 'who-hallucinates-more-chatgpt-or-claude',\n title: 'Who Hallucinates More: ChatGPT or Claude?',\n titleDisplay: 'Who Hallucinates More:',\n titleDisplayItalic: 'ChatGPT or Claude?',\n description: 'Five benchmarks, two competing verdicts, and the one variable every comparison article gets wrong. Know which model to trust before the answer matters.',\n publishedAt: '2026-05-23',\n author: 'MCP Scraper',\n authorInitials: 'M',\n tags: ['AI hallucination', 'ChatGPT', 'Claude', 'LLM accuracy', 'AI benchmarks'],\n category: 'AI Accuracy',\n badge: '32 questions answered',\n fieldGuideLabel: 'Field guide',\n deck: 'Five benchmarks give five different winners — and every \"Claude wins\" article was benchmarked on a model that is no longer the default. Here is what the current data actually says, and what to do with it.',\n readTimeMinutes: 14,\n stats: [\n { value: '5', label: 'sections' },\n { value: '32', label: 'questions answered' },\n { value: '5', label: 'benchmarks compared' },\n { value: '0', label: 'fluff' },\n ],\n ctaHeading: 'Verify before you ship with',\n ctaHeadingItalic: 'live data.',\n ctaBody: 'MCP Scraper gives you real-time SERP intelligence, PAA harvests, and page extraction so your AI workflows are grounded in current sources — not cached claims from articles written about models that no longer exist.',\n sections: [\n {\n id: 'what-is-hallucination',\n num: '01',\n title: 'What You\\'re Actually Asking',\n titleItalic: 'About.',\n deck: 'Before comparing rates, you need to know what the word \"hallucination\" means — and it turns out no benchmark, no article, and no AI company uses the same definition. A 3% rate and a 15% rate can describe the same model on the same day.',\n callout: {\n eyebrow: 'Terminology',\n heading: 'Hallucination and confabulation are not the same thing — and the distinction explains why Claude and ChatGPT get different labels.',\n body: 'Confabulation is the specific pattern of plausibly gap-filling missing knowledge with invented detail — the brain (or model) connecting dots that were never there. Hallucination is the broader term covering any confident false output. <strong>Claude\\'s uncertainty-admission training was designed to interrupt confabulation specifically.</strong> ChatGPT\\'s RLHF was tuned on human preference, which tends to reward confident, complete-sounding answers even when the model is uncertain. The same root behavior gets opposite training signals in each system.',\n },\n cards: [\n {\n id: 'q1-1',\n num: '1.1',\n question: 'what is AI hallucination',\n answer: '<strong>AI hallucination is when a language model produces confident, fluent output that is factually wrong — a citation that doesn\\'t exist, a date that never happened, a quote no one said.</strong> The term is borrowed loosely from psychiatry, where hallucination means perceiving something that isn\\'t there. In practice, LLM hallucinations look less like delusions and more like plausible-sounding autocomplete: the model generates the statistically likely continuation of a sentence, not a grounded fact lookup. The critical word is \"confident\" — hallucinations are dangerous not because models are wrong, but because they are wrong without signaling any uncertainty. A practitioner\\'s real concern is not hallucination frequency but hallucination detectability: a model that hallucinates rarely but never hedges is far more dangerous in production than one that hallucinates often and flags it.',\n },\n {\n id: 'q1-2',\n num: '1.2',\n question: 'why do AI chatbots hallucinate',\n answer: '<strong>AI chatbots hallucinate because they are trained to predict the most plausible next token, not to retrieve verified facts from a ground-truth database.</strong> The architecture is fundamentally generative — the model produces text that fits the statistical patterns in its training corpus, and sometimes those patterns lead it to fill gaps with invented specifics. Three compounding factors make hallucination worse: sparse coverage of a topic in training data (the model extrapolates), conflicting information in the corpus (the model blends), and RLHF reward signals that favor fluent, complete-sounding outputs over hedged ones (the model stops saying \"I\\'m not sure\"). The reason ChatGPT and Claude hallucinate at different rates on different tasks is not architecture alone — it is which of these three failure modes each system\\'s training most aggressively corrects for. If your task exposes sparse training coverage (niche domain knowledge, recent events), neither model can save you without grounded retrieval.',\n },\n {\n id: 'q1-3',\n num: '1.3',\n question: 'what is confabulation in AI',\n answer: '<strong>Confabulation in AI is the specific pattern where a model fills a knowledge gap with invented-but-plausible detail rather than refusing or hedging</strong> — the model \"connects the dots\" that were never actually there. The clinical term comes from neurology, where patients with certain memory disorders produce false memories that feel entirely real to them. In LLMs, confabulation is the mechanism behind the most dangerous class of hallucinations: not random nonsense but well-constructed fabrications — a fake paper with a real author\\'s name, a plausible-sounding legal citation, a drug dosage derived by averaging nearby real figures. The distinction matters for tooling: hallucination detectors that look for low confidence scores will often miss confabulation, because the model\\'s internal confidence on a confabulated output can be high. Grounding against primary sources — not just asking the model to self-check — is the only reliable counter.',\n },\n {\n id: 'q1-4',\n num: '1.4',\n question: 'what is the difference between AI hallucination and confabulation',\n answer: '<strong>Hallucination is the broad category; confabulation is the specific failure mode where the model invents plausible gap-fills rather than flagging its own uncertainty.</strong> All confabulation is hallucination, but not all hallucination is confabulation — a model that confidently states a wrong date is hallucinating, but it isn\\'t necessarily confabulating if the error traces to a corrupted training example rather than a gap-bridging inference. The distinction changes what interventions work: suppressing confabulation requires training models to recognize the edges of their own knowledge and refuse at those boundaries (which is what Constitutional AI\\'s self-critique loop does for Claude). Suppressing hallucination more broadly requires grounding — retrieval-augmented generation, citation enforcement, source verification. Practitioners who use the words interchangeably will apply the wrong fix.',\n },\n {\n id: 'q1-5',\n num: '1.5',\n question: 'are AI hallucinations the same as lying',\n answer: '<strong>No — hallucination is a failure of knowledge, not a failure of intent, which means the usual remedies for dishonesty (adversarial red-teaming, filtering, policy enforcement) don\\'t reduce it.</strong> A lying agent knows the truth and conceals it; a hallucinating model has no ground-truth representation to conceal — it generates the output that fits the learned distribution, whether that output is accurate or not. This distinction is not just philosophical. Treating hallucination as lying leads organizations to apply trust-and-safety interventions (content moderation, output filtering) rather than epistemic interventions (grounding, uncertainty calibration, retrieval). The more practically damaging confusion is the reverse: treating hallucination as a fixable \"bad behavior\" that fine-tuning will eventually eliminate, rather than as a structural property of generative models that requires architectural solutions.',\n },\n {\n id: 'q1-6',\n num: '1.6',\n question: 'why does ChatGPT make things up',\n answer: '<strong>ChatGPT makes things up because its RLHF training consistently rewarded fluent, complete-sounding answers — and human raters often cannot tell in the moment whether a specific claim is true.</strong> When the model encounters a query at the edge of its training knowledge, it faces two options: produce a hedged, incomplete answer (which RLHF raters historically penalized as unhelpful) or produce a fluent, confident-sounding answer that fills the gap (which raters often rewarded as useful). Over millions of training examples, that signal compounds: the model learns that confident gap-filling is the preferred behavior. OpenAI\\'s release notes for GPT-5.5 Instant specifically cite \"reduces hallucination in sensitive areas such as law, medicine, and finance\" as a named improvement — which is an implicit acknowledgment that prior versions were not calibrated to refuse when uncertain. The fix is not better knowledge; it is better uncertainty signaling.',\n source: 'https://techcrunch.com/2026/05/05/openai-releases-gpt-5-5-instant-a-new-default-model-for-chatgpt/',\n },\n ],\n },\n {\n id: 'the-verdict-depends',\n num: '02',\n title: 'The Verdict',\n titleItalic: 'Depends.',\n deck: 'One proprietary test shows Claude hallucinating more than ChatGPT (15% vs. 12%). A different benchmark run on the same models the same year shows Claude with the lowest contradiction rate of five providers. Both studies are real. Neither is lying. The winner changes when the measurement changes — and no competitor article tells you which measurement matches your actual task.',\n callout: {\n eyebrow: 'Deposition',\n heading: 'Every \"Claude wins\" verdict was written against a different product than the one you\\'re using today.',\n body: 'GPT-5.5 Instant became the default ChatGPT in May 2026. Claude Opus 4.7 is the current frontier Claude. <strong>The top SERP articles comparing hallucination rates were benchmarked primarily on GPT-4 Turbo and Claude 3 variants.</strong> The benchmark scores you are reading describe models that are no longer the default. This is not a minor caveat — task-type inversion, refusal-rate confounds, and methodology differences all compound when the model version gap is also wrong. The deposition question is not \"which model wins?\" It is: \"Which benchmark, on which task type, on which model version, measured how?\"',\n },\n cards: [\n {\n id: 'q2-1',\n num: '2.1',\n question: 'who hallucinates more ChatGPT or Claude',\n answer: '<strong>Neither model consistently hallucinates more — the winner changes based on the task type, benchmark methodology, and which model version is being measured.</strong> On BullshitBench v2, Claude Sonnet 4.6 hits a 3% hallucination rate with a 91% detection rate, while OpenAI GPT models are \"stuck in the 55–65% range\" for detection. On Vectara\\'s harder enterprise dataset (February 2026), GPT-4.1 scores 5.6% versus Claude Sonnet 4.6 at 10.6% — a reversal. On AA-Omniscience, Claude Opus 4.1 achieves 0% hallucination (via refusal), while GPT-5.5 reaches 86% error on the same benchmark. The honest answer for practitioners: Claude tends to outperform on tasks requiring uncertainty calibration and open-recall; ChatGPT tends to outperform on grounded tasks with source material present. Your use case determines the verdict.',\n source: 'https://medium.com/@anyapi.ai/llm-hallucination-index-2026-why-claude-4-6-7b2d13ed9f0c',\n },\n {\n id: 'q2-2',\n num: '2.2',\n question: 'does Claude hallucinate less than ChatGPT',\n answer: '<strong>Claude hallucinates less than ChatGPT on open-recall and uncertainty-calibration benchmarks, but GPT models can outperform Claude on grounded generation tasks where source material is provided.</strong> On the Vectara HHEM original dataset (April 2025), GPT-5 scores 1.4% versus Claude-3.7-Sonnet at 4.4% — ChatGPT wins. On BullshitBench v2, Claude Sonnet 4.6 scores 3% with a 91% detection rate — Claude wins. On AA-Omniscience, Claude Opus 4.1 achieves 0% hallucination via confident refusal — Claude wins decisively. The most useful reframe: Claude tends to hallucinate less on tasks where \"I don\\'t know\" is an acceptable output; ChatGPT can score lower on structured summarization tasks where the source material bounds the answer space.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q2-3',\n num: '2.3',\n question: 'which AI is more accurate ChatGPT or Claude',\n answer: '<strong>GPT models score higher on grounded factual accuracy when source material is present; Claude scores higher on calibration — knowing when not to answer.</strong> On FACTS Overall Scores (grounded generation), GPT-5 scores 61.8 versus Claude Opus 4.5 at 51.3. On AA-Omniscience, Claude Opus 4.1 achieves 0% hallucination while GPT-5.5 reaches 86% error. These are not contradictions — they measure different things. FACTS rewards producing correct answers given a source; AA-Omniscience rewards refusing answers when knowledge is uncertain. Accuracy in a production system means both: getting the answer right when you have the source, and refusing when you don\\'t. No single model currently dominates both dimensions simultaneously.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q2-4',\n num: '2.4',\n question: 'what is the hallucination rate of ChatGPT in 2026',\n answer: '<strong>ChatGPT\\'s hallucination rate in 2026 ranges from 1.4% on Vectara\\'s original RAG benchmark to 86% on AA-Omniscience\\'s domain-knowledge open-recall test — the same model, different methodologies.</strong> On BullshitBench v2, OpenAI GPT models are \"stuck in the 55–65% range\" for hallucination detection. GPT-5 with thinking mode achieves 1.6% on HealthBench (medical domain). O3 hits 51% hallucination on SimpleQA; o4-mini reaches 79% on PersonQA. The number you see in any article reflects the benchmark used, not a universal accuracy property. The most applicable figure depends on your task: if you are doing RAG summarization, Vectara\\'s 1.4% is relevant; if you are asking ChatGPT to recall domain-specific facts without source material, the AA-Omniscience figure is the honest baseline.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q2-5',\n num: '2.5',\n question: 'what is the hallucination rate of Claude in 2026',\n answer: '<strong>Claude\\'s hallucination rate in 2026 spans from 0% (Claude Opus 4.1 on AA-Omniscience, via refusal) to 58% (Claude Opus 4.5 on the same benchmark when not configured to refuse) — a range that makes any single number misleading.</strong> On BullshitBench v2, Claude Sonnet 4.6 hits 3% with a 91% detection rate, making it the strongest performer in that benchmark class. On Vectara\\'s enterprise dataset (February 2026), Claude Sonnet 4.6 scores 10.6% and Claude Opus 4.6 scores 12.2%. The spread is explained by task type: Claude\\'s Constitutional AI training produces strong refusal behavior on uncertain factual questions, which collapses the hallucination rate on benchmarks that reward \"I don\\'t know\" responses and inflates it on benchmarks that penalize non-answers.',\n source: 'https://medium.com/@anyapi.ai/llm-hallucination-index-2026-why-claude-4-6-7b2d13ed9f0c',\n },\n {\n id: 'q2-6',\n num: '2.6',\n question: 'which AI has the lowest hallucination rate in 2026',\n answer: '<strong>Gemini-2.0-Flash-001 holds the lowest published Vectara HHEM score at 0.7% on the original dataset — but that benchmark measures factual consistency in RAG summarization, not open-ended recall.</strong> On open-recall benchmarks, Claude Opus 4.1 achieves 0% on AA-Omniscience by refusing uncertain questions, while o3-mini-high scores 0.8% on Vectara. The \"lowest hallucination rate\" title changes with every benchmark and model release cycle; the more useful question is which model has the lowest hallucination rate on your specific task class. For enterprise RAG pipelines with provided source material, GPT-4.1 at 5.6% on the harder Vectara dataset is currently competitive. For open-domain factual recall with uncertainty, Claude\\'s refusal behavior produces the lowest confirmed error rate.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q2-7',\n num: '2.7',\n question: 'how is AI hallucination measured',\n answer: '<strong>AI hallucination is measured by comparing model outputs against a verified ground-truth set and scoring the proportion of confident claims that are factually wrong — but the ground-truth set, task type, and scoring rules vary so widely across benchmarks that the resulting numbers are rarely comparable.</strong> Three methodology families dominate: RAG consistency tests (Vectara HHEM measures whether a summary stays faithful to the source document), factual recall tests (SimpleQA, PersonQA ask the model open questions with known correct answers), and calibration tests (AA-Omniscience scores how often a model produces wrong answers on questions it should refuse). The same model can score in the top tier on one family and bottom tier on another. Before citing a hallucination rate, the practitioner question is: what task type does this benchmark represent, and does that match what I\\'m actually asking the model to do?',\n source: 'https://chatgptguide.ai/ai-hallucination-rates-report-gpt-claude-gemini/',\n },\n ],\n },\n {\n id: 'why-claude-behaves-differently',\n num: '03',\n title: 'Why Claude Behaves Differently',\n titleItalic: '(And Why That\\'s Complicated.)',\n deck: 'Constitutional AI was built to interrupt confabulation at the output layer — not to make Claude more knowledgeable, but to make it refuse when it isn\\'t. That design makes Claude\\'s hallucination rate look better on open-recall benchmarks and worse on grounded tasks where refusing an answer is the wrong move. The \"safer model\" label hides a trade-off every competitor article misses.',\n callout: {\n eyebrow: 'Architecture',\n heading: 'Claude\\'s 0% hallucination score on AA-Omniscience is achieved by refusing to answer — GPT-5.5 attempts the same questions and scores 86% error.',\n body: 'These are not equivalent failure modes. <strong>Claude\\'s refusal behavior is a deliberate uncertainty-admission signal trained by Constitutional AI\\'s self-critique loop.</strong> GPT-5.5\\'s 86% error rate on that benchmark reflects RLHF training that rewards confident, complete-sounding output even under epistemic uncertainty. A practitioner choosing between them for a task where refusal is unacceptable — a legal brief, a diagnostic intake form, a real-time research summary — needs to know that \"lower hallucination rate\" may mean \"higher refusal rate,\" not \"more accurate answers.\"',\n },\n cards: [\n {\n id: 'q3-1',\n num: '3.1',\n question: 'what is Constitutional AI and does it reduce hallucinations',\n answer: '<strong>Constitutional AI is Anthropic\\'s training methodology where Claude critiques and revises its own outputs against a set of principles — and yes, it reduces a specific class of hallucination: confabulation driven by overconfidence.</strong> The core mechanism is a self-critique loop: at training time, Claude is prompted to evaluate its own responses against a constitution of principles (including honesty norms) and revise outputs that violate them. Over millions of examples, this trains the model to flag uncertainty rather than elaborate plausibly over it. What it does not do is give Claude better knowledge — it makes the model more likely to output \"I\\'m not sure\" or refuse at the boundary of its knowledge. The result is measurably lower hallucination rates on open-recall benchmarks and, as a side effect, higher refusal rates on tasks where humans expect confident answers. The \"safer model\" framing is accurate but incomplete: Constitutional AI reduces dangerous confabulation, not all incorrect outputs.',\n },\n {\n id: 'q3-2',\n num: '3.2',\n question: 'why does Claude hallucinate less than ChatGPT',\n answer: '<strong>Claude hallucinates less than ChatGPT on uncertainty-sensitive tasks because Constitutional AI\\'s self-critique training penalized overconfident outputs at the architectural level — not because Claude has better underlying knowledge.</strong> ChatGPT\\'s RLHF training was tuned on human preference ratings, and human raters consistently prefer confident, complete-sounding answers to hedged, partial ones — even when the hedged answer is more accurate. That preference signal, applied at scale, teaches the model to fill gaps confidently. Constitutional AI\\'s revision loop applies a different signal: outputs that violate honesty norms (overconfident claims under uncertainty) are scored negatively by the model itself and revised. On benchmarks like AA-Omniscience, this difference is dramatic: Claude Opus 4.1 achieves 0% hallucination by refusing uncertain questions; GPT-5.5 attempts the same questions and produces 86% error. The practical implication is that Claude\\'s advantage narrows or reverses when the task context provides source material that bounds the answer — because grounding reduces the need for uncertainty calibration.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q3-3',\n num: '3.3',\n question: 'why does Claude say I don\\'t know more than ChatGPT',\n answer: '<strong>Claude says \"I don\\'t know\" more than ChatGPT because uncertainty admission was a first-class design goal in Constitutional AI, not an afterthought in RLHF fine-tuning.</strong> Anthropic explicitly trained Claude to identify the edges of its own knowledge and signal them rather than bridge them. The constitutional principle \"prefer accurate uncertainty estimates over confident wrong answers\" was applied via the self-critique loop — meaning at training time, Claude learned to score its own overconfident outputs negatively. ChatGPT\\'s training did the opposite: human raters penalized partial or hedged answers as unhelpful, pushing the model toward confident completeness. For practitioners, the implication is that Claude\\'s \"I don\\'t know\" is a calibration signal worth respecting — it correlates with genuine knowledge boundaries. ChatGPT\\'s confident answers do not carry the same calibration signal and require independent verification more often.',\n },\n {\n id: 'q3-4',\n num: '3.4',\n question: 'why does Claude admit uncertainty more than ChatGPT',\n answer: '<strong>Claude admits uncertainty more than ChatGPT because its training reward function directly penalized overconfidence, while ChatGPT\\'s reward function indirectly penalized uncertainty by preferring fluent, complete-sounding outputs.</strong> These are mirror-image training problems with mirror-image results. At the architectural level, both models have the same epistemic limitation: they cannot know what they do not know. What differs is how each handles that edge. Claude\\'s Constitutional AI self-critique loop was designed to surface that edge and output it. ChatGPT\\'s RLHF fine-tuning learned to smooth over it. The consequence for production use is that when Claude expresses uncertainty, it is more likely to be a genuine signal. When ChatGPT expresses certainty, it is less likely to be a reliable signal than the confident phrasing suggests. This asymmetry is the single most important behavioral difference between the two systems for high-stakes use cases.',\n },\n {\n id: 'q3-5',\n num: '3.5',\n question: 'does Claude refuse to answer questions it doesn\\'t know',\n answer: '<strong>Yes — Claude is trained to refuse or heavily hedge questions at the boundary of its knowledge, and this behavior is measurable: on AA-Omniscience, Claude Opus 4.1 achieves 0% hallucination by refusing uncertain domain-knowledge questions rather than attempting them.</strong> This refusal behavior is not a safety filter applied after generation — it is a trained output preference baked into the model via Constitutional AI\\'s self-critique loop. The practical consequence is two-sided: Claude produces fewer confident wrong answers than ChatGPT, but it also produces more non-answers on questions where an attempt — even an imperfect one — would be useful. For tasks where a partial answer is better than no answer (brainstorming, hypothesis generation, exploratory research), ChatGPT\\'s higher attempt rate is a feature. For tasks where a wrong answer causes real harm (legal, medical, compliance), Claude\\'s refusal behavior is the more defensible default.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n ],\n },\n {\n id: 'real-world-consequences',\n num: '04',\n title: 'When Getting It Wrong',\n titleItalic: 'Has Consequences.',\n deck: 'ChatGPT has already fabricated legal citations in federal court, hallucinated drug dosages, and invented academic papers that passed first-pass review. The more unsettling risk is newer: when ChatGPT, Claude, and Gemini all hallucinate the same false claim, cross-checking them doesn\\'t give you three independent sources. It gives you the same error three times.',\n callout: {\n eyebrow: 'False Consensus Risk',\n heading: 'If three major LLMs hallucinate the same lie about your business, it can become the new truth — and no benchmark measures this.',\n body: 'The correlated hallucination problem emerges from shared training data, overlapping RLHF pipelines, and convergent fine-tuning on the same web corpus. <strong>When Claude, ChatGPT, and Gemini all reproduce the same unsupported claim, a practitioner who cross-checks across models gets false triangulation rather than independent verification.</strong> This risk is entirely absent from every current competitor article on hallucination rates — and it is most acute for entities (companies, people, products) that appear in training data in ways the entity cannot audit or correct.',\n },\n cards: [\n {\n id: 'q4-1',\n num: '4.1',\n question: 'what are examples of ChatGPT hallucinations',\n answer: '<strong>The most documented ChatGPT hallucinations include fabricated legal case citations presented to federal courts, invented academic papers with real author names, and confident wrong drug dosages in medical queries.</strong> The legal citation failures are the best-documented: in the Mata v. Avianca case, a New York attorney submitted a brief citing multiple cases that ChatGPT invented wholesale — cases that did not exist anywhere in the legal record. Academic hallucinations are structurally similar: ChatGPT generates plausible-sounding paper titles, journal names, and DOIs that pass casual verification because all the component elements (author names, journal names, topic keywords) are real — only the assembled paper is fabricated. The pattern in all major cases is the same: ChatGPT produces hallucinations that are specifically designed, by the training distribution, to pass the first verification step a non-expert would apply.',\n },\n {\n id: 'q4-2',\n num: '4.2',\n question: 'has ChatGPT hallucinated in court',\n answer: '<strong>Yes — the most consequential documented case is Mata v. Avianca, where an attorney used ChatGPT to research case law and submitted a brief citing multiple cases that did not exist, resulting in federal court sanctions.</strong> The cases ChatGPT generated were plausible: they had realistic docket numbers, party names consistent with real aviation litigation, and summaries that read as coherent legal precedent. None of them could be located by opposing counsel or the court because they were entirely fabricated. The attorney was sanctioned for failing to verify the citations. What makes this case significant beyond its notoriety is the mechanism: ChatGPT did not produce random nonsense — it confabulated, generating outputs that fit the expected form of real legal citations so closely that a trained attorney did not catch them on first read. This is the confabulation failure mode operating at the level of maximum real-world cost.',\n },\n {\n id: 'q4-3',\n num: '4.3',\n question: 'did ChatGPT make up fake legal cases',\n answer: '<strong>Yes — in the Mata v. Avianca case, ChatGPT generated at least six fake legal cases that were submitted to a federal court as real precedent, making this the first major documented instance of LLM hallucination causing legal sanctions against a practicing attorney.</strong> The fabricated cases had realistic-looking citations: Varghese v. China Southern Airlines, Shaboon v. Egyptair, Zicherman v. Korean Air Lines, and others — all plausible-sounding aviation negligence precedents. When the court asked for copies of the actual decisions, the attorney could not produce them because they did not exist. The episode became a landmark not just for AI liability but for the broader question of what \"verification\" means when a model\\'s hallucinations are structurally indistinguishable from real citations to a non-expert reader. Neither Claude nor any other model has a documented comparable case — but the mechanism exists in all models that generate legal text without grounding.',\n },\n {\n id: 'q4-4',\n num: '4.4',\n question: 'what happened in the Mata v. Avianca ChatGPT hallucination case',\n answer: '<strong>In Mata v. Avianca, a personal injury lawsuit filed in the Southern District of New York, attorney Steven Schwartz used ChatGPT to research aviation negligence precedents and submitted a court brief citing six cases that ChatGPT had fabricated.</strong> When opposing counsel could not locate the cited cases, the court ordered Schwartz to produce the actual decisions. He could not — the cases existed only in ChatGPT\\'s output. The court sanctioned Schwartz and his firm. Schwartz\\'s defense was that he was unfamiliar with ChatGPT\\'s tendency to generate false information; the court found that reliance on an AI tool without verification constituted professional negligence. The case is now the canonical example cited in AI liability discussions because it makes concrete the abstract warning that LLM hallucinations have real-world costs — and because the mechanism was confabulation, not noise: the fake cases were structurally indistinguishable from real ones.',\n },\n {\n id: 'q4-5',\n num: '4.5',\n question: 'can ChatGPT hallucinate medical information',\n answer: '<strong>Yes — ChatGPT can hallucinate medical information, and the risk is highest in exactly the scenarios where clinicians are most likely to use it: rare conditions, drug-drug interactions, and off-label dosages that are underrepresented in training data.</strong> GPT-4o scores 15.8% hallucination on HealthBench, a medical domain benchmark; GPT-5 with thinking mode reduces this to 1.6%, but the improvement is conditional on the thinking mode being enabled and the query being within the benchmark\\'s scope. The practical risk for medical use is not just the headline hallucination rate — it is the confabulation pattern: ChatGPT generating specific-sounding dosages or protocol details that are plausible but wrong, in the confident register that clinical notes require. The consensus across medical AI research is that no current LLM should be used as a primary information source for treatment decisions without RAG grounding against validated clinical databases.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q4-6',\n num: '4.6',\n question: 'does ChatGPT hallucinate more on academic citations',\n answer: '<strong>ChatGPT hallucinates on academic citations at a notably higher rate than on general-text tasks because academic citations combine several conditions that maximize confabulation risk: sparse training coverage of specific papers, high structural regularity (author, title, journal, year, DOI), and user verification behavior that rarely extends beyond checking the format.</strong> The model has learned that citations follow predictable patterns. When asked for a citation it does not have in training, it generates a citation that fits those patterns — assembling a plausible author name, a realistic journal, and a plausible year around a fabricated paper. Dedicated academic search integrations (like ChatGPT\\'s search tool when enabled) significantly reduce citation hallucination by grounding against live databases. Without grounding, treating any LLM-generated academic citation as provisional and verifying it against Google Scholar, CrossRef, or a DOI resolver is not optional — it is the baseline standard of care.',\n },\n ],\n },\n {\n id: 'choose-and-verify',\n num: '05',\n title: 'Choose a Model.',\n titleItalic: 'Verify the Answer.',\n deck: 'The right model for your task depends on whether wrong answers or missing answers cost you more. The right verification method depends on whether you need real-time source grounding, prompt-level controls, or live SERP intelligence to know whether the benchmark you\\'re relying on has already been superseded. Static articles can\\'t give you that. Here\\'s what can.',\n callout: {\n eyebrow: 'MCP Scraper',\n heading: 'Every hallucination benchmark is already measuring a different model than the one you\\'re running today.',\n body: 'GPT-5.5 Instant and Claude Opus 4.7 are the current defaults as of May 2026. <strong>The top SERP articles were benchmarked on GPT-4 Turbo and Claude 3 variants.</strong> Live PAA intelligence from MCP Scraper shows which benchmark claims are currently circulating in the SERP, which task-specific questions are going unanswered (legal, medical, enterprise, scientific writing), and whether the competitive landscape shifted while the static comparison articles were being written. A practitioner who needs the current answer — not a cached verdict — needs a live source, not another article that will be wrong in six months.',\n },\n cards: [\n {\n id: 'q5-1',\n num: '5.1',\n question: 'how do you stop ChatGPT from hallucinating',\n answer: '<strong>You cannot stop ChatGPT from hallucinating entirely, but the four techniques that reliably reduce it are: retrieval-augmented generation with verified sources, chain-of-thought prompting with explicit uncertainty flagging, citation enforcement in the system prompt, and output verification against primary sources before use.</strong> RAG is the highest-leverage intervention: grounding responses against a controlled, verified document set eliminates the knowledge-gap confabulation that produces most dangerous hallucinations. Chain-of-thought prompting (\"explain your reasoning step by step, and flag any step where you are less than certain\") forces the model to surface uncertainty it would otherwise paper over. Adding \"if you are unsure, say so explicitly rather than guessing\" to system prompts has measurable effect on calibration. None of these eliminate the problem — they reduce it. For high-stakes outputs (legal, medical, financial), human verification against primary sources remains the standard. The tools help; they do not replace verification.',\n },\n {\n id: 'q5-2',\n num: '5.2',\n question: 'how to reduce AI hallucinations with prompt engineering',\n answer: '<strong>The prompt engineering techniques with the strongest documented effect on hallucination reduction are: explicit uncertainty instructions, step-by-step reasoning requirements, role-scoping, and source-citation enforcement — applied together, not independently.</strong> Explicit uncertainty instructions (\"say \\'I don\\'t know\\' rather than guessing\") improve calibration on both Claude and ChatGPT because both models are capable of signaling uncertainty; they need permission to do it. Step-by-step reasoning forces the model to commit to intermediate claims that can be checked, catching confabulation earlier in the chain. Role-scoping (\"you are a fact-checker; do not include any claim you cannot source\") narrows the output distribution toward the verified. Source citation enforcement (\"provide a source URL for every factual claim\") creates a verification trail. The compounding insight most engineers miss: these techniques are not additive linearly — models trained with uncertainty signals (like Claude) show larger improvements from uncertainty prompts than models trained against them (like older ChatGPT versions).',\n },\n {\n id: 'q5-3',\n num: '5.3',\n question: 'does asking ChatGPT to cite sources reduce hallucinations',\n answer: '<strong>Asking ChatGPT to cite sources reduces the frequency of unverifiable claims in the output, but it does not eliminate fabricated citations — and a fabricated citation with a plausible URL is harder to catch than a claim with no citation at all.</strong> The mechanism is real: source-citation prompting shifts the model toward outputs where citation is possible, which correlates with better-grounded claims. But ChatGPT can and does generate plausible-looking DOIs, arXiv IDs, and URLs that resolve to nothing. The citation requirement creates a false confidence layer — the output looks verified when it isn\\'t. The correct workflow is source-citation prompting plus independent verification of every cited source before use. For enterprise pipelines, automated link-checking (do all cited URLs actually resolve?) is the minimum; content verification (does the cited source actually say what the LLM claims it says?) is the standard that eliminates the fabricated-citation failure mode.',\n },\n {\n id: 'q5-4',\n num: '5.4',\n question: 'what is retrieval-augmented generation and does it stop hallucinations',\n answer: '<strong>Retrieval-augmented generation (RAG) is an architecture that grounds LLM outputs by retrieving relevant documents from a verified source set and providing them as context — and it is currently the most effective single intervention for reducing hallucination in production systems.</strong> Instead of asking a model to recall facts from training, a RAG system retrieves the relevant passage from a controlled document store and asks the model to summarize or synthesize it. On Vectara\\'s HHEM benchmark, which specifically tests this summarization-from-source behavior, even older model versions achieve hallucination rates below 5% — because the knowledge gap confabulation mechanism is eliminated when the answer exists in the provided context. What RAG does not stop: hallucinations that occur when the retrieved document does not contain the answer and the model interpolates anyway, and hallucinations in the retrieval step itself (if a semantic search retrieves the wrong document). RAG reduces hallucination dramatically in bounded domains; it is not a universal cure for open-domain queries.',\n },\n {\n id: 'q5-5',\n num: '5.5',\n question: 'which AI is more trustworthy ChatGPT or Claude',\n answer: '<strong>Claude is more trustworthy for tasks where calibrated uncertainty is the primary requirement; ChatGPT is more trustworthy for tasks where producing an answer — even an imperfect one — is the primary requirement.</strong> Trust is not a single dimension. On calibration trust (does the model\\'s expressed confidence correlate with its actual accuracy?), Claude leads: Constitutional AI\\'s uncertainty training produces hedges that track genuine knowledge gaps. On coverage trust (will the model attempt the question rather than refuse?), ChatGPT leads: RLHF training produces higher attempt rates on hard questions. The user perception data reflects calibration trust: 62% of verified ChatGPT (GPT-4/4o) users report \"occasional confident inaccuracies\" versus 24% of Claude 3.5 users. For practitioners, the framework is: trust Claude more when a wrong answer is worse than no answer; trust ChatGPT more when a partial answer is better than a refusal.',\n source: 'https://chatgptguide.ai/ai-hallucination-rates-report-gpt-claude-gemini/',\n },\n {\n id: 'q5-6',\n num: '5.6',\n question: 'which AI is safer to use for high-stakes tasks',\n answer: '<strong>For high-stakes tasks where a wrong answer has irreversible consequences, Claude\\'s refusal-calibrated behavior makes it the safer default — but safe use of either model requires human verification against primary sources, not model selection alone.</strong> Claude\\'s design advantage in high-stakes contexts is the refusal signal: when Claude says it is uncertain, that signal has been trained to track real knowledge boundaries. ChatGPT\\'s confident outputs in the same situations are less reliably calibrated. However, \"safer model\" does not mean \"reliable without verification\" — Claude Opus 4.5 scores 58% hallucination on AA-Omniscience when not configured to refuse, and Claude Opus 4.7 scores 36%. The practical standard for high-stakes work is: use Claude for the calibration signal, ground with RAG against verified sources, and treat any AI-generated factual claim in a legal, medical, or financial context as provisional until independently confirmed.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q5-7',\n num: '5.7',\n question: 'which AI hallucinates less for enterprise use',\n answer: '<strong>For enterprise RAG pipelines where source documents are provided, GPT-4.1 currently outperforms Claude on Vectara\\'s harder enterprise dataset (5.6% vs. Claude Sonnet 4.6\\'s 10.6%) — but for enterprise use cases requiring open-domain knowledge retrieval or strict uncertainty signaling, Claude\\'s refusal calibration produces fewer dangerous confident errors.</strong> The enterprise use case is split along the same task-type boundary that governs all hallucination comparisons: grounded generation with provided source material favors GPT models; open-domain recall with uncertainty requirements favors Claude. For enterprise deployments at the highest risk level (legal, medical, compliance), the architecture recommendation from available benchmark data is: pair Claude with a RAG pipeline, use Claude\\'s uncertainty signal as a flag for human review, and verify any output where the model expresses high confidence without a cited source. The combination outperforms either model used alone.',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n {\n id: 'q5-8',\n num: '5.8',\n question: 'can you trust ChatGPT for medical or legal advice',\n answer: '<strong>No — neither ChatGPT nor any current LLM should be trusted as a primary source for medical or legal advice without independent verification against authoritative primary sources, and the documented failure cases make the risk concrete.</strong> In the Mata v. Avianca case, ChatGPT fabricated legal citations that passed initial attorney review, resulting in federal court sanctions — the most expensive hallucination failure mode documented in legal practice. On HealthBench, GPT-4o hallucinates medical information at a 15.8% rate; GPT-5 with thinking mode reduces this to 1.6%, but that reduction depends on task-specific configurations unavailable in standard ChatGPT use. For legal research, both ChatGPT and Claude should be used as research accelerators — identifying potentially relevant cases and concepts — with every specific citation independently verified against Westlaw, LexisNexis, or primary court documents before any professional use. The standard of care is not \"use the safer model\"; it is \"verify every claim.\"',\n source: 'https://suprmind.ai/hub/ai-hallucination-rates-and-benchmarks/',\n },\n ],\n },\n ],\n },\n {\n slug: 'claude-code',\n title: 'Claude Code: The Evaluation Guide Every Tutorial Skips',\n titleDisplay: 'Claude Code',\n titleDisplayItalic: 'Evaluated.',\n description: 'The cost math, head-to-head comparisons, and trust answers that every Claude Code tutorial skips — so you can decide before you adopt.',\n publishedAt: '2026-05-24',\n author: 'MCP Scraper',\n authorInitials: 'M',\n tags: ['Claude Code', 'AI coding', 'developer tools', 'Anthropic', 'AI agents'],\n category: 'Developer Tools',\n badge: '45 questions answered',\n fieldGuideLabel: 'Field guide',\n deck: 'Every article about Claude Code tells you it\\'s an AI coding agent you just need to learn to use. This one tells you how to evaluate whether it\\'s worth adopting — with the cost math, the head-to-head comparisons, and the trust answers that every tutorial skips.',\n readTimeMinutes: 18,\n stats: [\n { value: '6', label: 'sections' },\n { value: '45', label: 'questions' },\n { value: '22', label: 'gap questions covered' },\n { value: '0', label: 'fluff' },\n ],\n ctaHeading: 'Scrape smarter with',\n ctaHeadingItalic: 'real web data.',\n ctaBody: 'MCP Scraper gives your Claude Code agents the live web intelligence they need — SERP data, People Also Ask harvests, competitor page extraction, and structured data feeds — without rate limits or browser fingerprinting.',\n sections: [\n {\n id: 'what-is-claude-code',\n num: '01',\n title: 'What Claude Code',\n titleItalic: 'Actually Is',\n deck: 'Most people who call Claude Code an \"AI coding assistant\" are describing a category it has already outgrown — it is closer to a junior engineer that runs in your terminal than a smarter autocomplete.',\n callout: {\n eyebrow: 'Built-in definition',\n heading: 'Claude Code operates on your full codebase, not just the file you have open.',\n body: 'Unlike Copilot or Cursor\\'s inline suggestions, Claude Code reads your entire project tree, executes shell commands, runs tests, and commits — making it an <strong>agentic system</strong>, not an autocomplete extension.',\n },\n cards: [\n {\n id: 'q1-1', num: '1.1',\n question: 'What exactly is Claude Code?',\n answer: '<strong>Claude Code is an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools</strong> — available in your terminal, IDE, desktop app, and browser. It is not a chat interface you paste code into; it operates on your local filesystem, executes shell commands with your authorization, and can spawn parallel sub-agents on separate subtasks. Unlike autocomplete tools that react to the file you have open, Claude Code takes a goal as input and works through the steps to achieve it across however many files that requires. The distinction matters for evaluation: you are not buying a smarter tab-completion, you are buying an agent that can misunderstand goals, degrade in long sessions, and occasionally do exactly what you said instead of what you meant.',\n },\n {\n id: 'q1-2', num: '1.2',\n question: 'How does Claude Code work?',\n answer: '<strong>You describe a goal in natural language; Claude Code reads relevant files, writes or edits code, runs shell commands, and iterates until the task is complete — operating on your local filesystem throughout.</strong> The model (Claude Sonnet 4.6 by default, or Opus 4.7 for harder problems) holds up to 1M tokens of context, allowing it to reason across an entire medium-sized codebase in a single session. It runs a loop: read context, plan, execute, observe output, adjust — stopping when the goal is met or when it needs clarification. The practical implication is that prompt quality matters more than most tutorials admit: vague goals produce vague results, and Claude Code will complete a vague goal confidently.',\n },\n {\n id: 'q1-3', num: '1.3',\n question: 'What can you do using Claude Code?',\n answer: '<strong>Write and refactor code, run and fix tests, review pull requests, create scripts, set up CI/CD pipelines, manage files, and spawn parallel agents on subtasks</strong> — across any language or framework. Beyond code editing, Claude Code integrates with GitHub Actions and GitLab CI/CD natively, can trigger pull requests from a Slack mention, connects to external tools via Model Context Protocol (MCP), and can be scheduled as a routine that runs on Anthropic-managed infrastructure while your computer is off. The surface area is broad enough that the more useful question is what it cannot do reliably — and that list appears in Section 06.',\n },\n {\n id: 'q1-4', num: '1.4',\n question: 'What is Claude Code best for?',\n answer: '<strong>Multi-file refactors, greenfield scaffolding, complex debugging cycles, and any task that requires reading a large codebase to understand context before making changes</strong> are where Claude Code\\'s 1M-token context window creates a genuine capability gap over file-local autocomplete tools. It is also well-suited for tasks that require coordination across multiple steps — setting up a test suite, migrating an API client, or auditing a codebase for a specific pattern — where the work is too spread out for a single prompt in a chat interface. Where it underperforms: novel algorithmic design, highly domain-specific regulatory code, and real-time systems — detailed in Section 06.',\n },\n {\n id: 'q1-5', num: '1.5',\n question: 'Why is Claude Code so good at coding?',\n answer: '<strong>It scores 80.8% on SWE-bench Verified</strong> — the leading benchmark for autonomous software engineering — and uses models with a 1M-token context window, allowing it to hold an entire medium-sized codebase in memory at once. The SWE-bench score means it solves roughly 4 in 5 real-world GitHub issues autonomously; competing tools score lower on the same benchmark. Context window size is the second structural advantage: autocomplete tools reason over hundreds of tokens; Claude Code reasons over millions, which is the difference between fixing a function and fixing a system. The 20% failure rate on SWE-bench is the equally important number — Section 06 covers what failure looks like in practice.',\n },\n {\n id: 'q1-6', num: '1.6',\n question: 'Why is everyone obsessed with Claude Code?',\n answer: '<strong>Claude Code is the first widely-adopted agentic coding tool that works at the project level rather than the file level</strong>, and it frequently completes tasks developers expected to take hours in under ten minutes — without requiring IDE changes or workflow restructuring. It runs in a terminal, which means it drops into any existing development environment without replacing the editor you already use. The combination of project-level context, shell execution, and a 1M-token window crossed a threshold where developers found themselves assigning real work to it rather than using it as a drafting aid — and that shift in how people use it, not any single feature, is what generated the adoption curve.',\n },\n {\n id: 'q1-7', num: '1.7',\n question: 'How can Claude Code be so good?',\n answer: '<strong>The underlying models — Claude Sonnet 4.6 and Opus 4.7 — were trained with a 1M-token context window and ranked first on SWE-bench</strong>; paired with full filesystem access and shell execution, the capability gap over autocomplete tools is structural, not marginal. Most autocomplete tools use models optimized for next-token prediction in a small window; Claude Code uses models optimized for multi-step reasoning across large contexts — a different training objective that produces qualitatively different behavior. The important caveat for practitioners: benchmark performance reflects average case; your specific codebase, stack, and task distribution may diverge significantly from the benchmark distribution.',\n },\n ],\n },\n {\n id: 'setup-and-surfaces',\n num: '02',\n title: 'Setup, Surfaces, and',\n titleItalic: 'Who Can Use It',\n deck: 'You do not need to be a developer to start a session with Claude Code — but the gap between \"starting a session\" and \"getting reliable results\" is wider than any installation guide will tell you.',\n cards: [\n {\n id: 'q2-1', num: '2.1',\n question: 'How do you start using Claude Code?',\n answer: '<strong>Install via one curl command (`curl -fsSL https://claude.ai/install.sh | bash`), or use Homebrew (`brew install --cask claude-code`) on Mac or WinGet (`winget install Anthropic.ClaudeCode`) on Windows</strong>; authenticate with a Claude Pro subscription or an Anthropic API key, then run `claude` in your project directory. The tool runs on macOS (Intel and Apple Silicon), Windows (x64 and ARM64), Linux, and WSL. A desktop application for macOS and Windows was also released on April 14, 2026, which adds a GUI launcher and Git worktree support for isolated parallel sessions. The fastest path to a first working session is the API key route — no subscription required, first session costs cents.',\n },\n {\n id: 'q2-2', num: '2.2',\n question: 'Can Claude Code start from scratch?',\n answer: '<strong>Yes — give it an empty directory and a description and it will scaffold the project structure, create files, initialize git, and write initial code without any existing codebase to read.</strong> This is one of the use cases where Claude Code\\'s agentic loop is most visible: it plans the file structure, writes each file, runs an initial build to check for errors, and iterates — the same way a developer would approach a greenfield project. The caveat is that greenfield outputs still require review: Claude Code will make architectural decisions based on its training data, and those decisions may not match your team\\'s standards, your target infrastructure, or your preferred dependencies.',\n },\n {\n id: 'q2-3', num: '2.3',\n question: 'Do you need to be a programmer to use Claude Code?',\n answer: '<strong>Not for well-defined, bounded tasks</strong> — product managers and researchers have used it to run competitive analyses, clean data, and build simple automation — but verifying outputs and debugging failures still benefits from technical fluency. The gap that non-programmers run into is not starting Claude Code; it is recognizing when its output is wrong. Claude Code will generate syntactically valid code that does the wrong thing, use a deprecated API without flagging it, or misunderstand a requirement in a way that only becomes visible when the code runs. Knowing what \"correct\" looks like is a prerequisite for using any agentic coding tool reliably, and that knowledge does not come from the tool itself.',\n },\n {\n id: 'q2-4', num: '2.4',\n question: 'Can beginners use Claude Code?',\n answer: '<strong>Yes, and beginners can complete real tasks in a first session</strong> — the friction of setup is low, the natural language interface is accessible, and for tasks with clear success criteria (write a Python script that does X, convert this CSV to JSON), the output is often directly usable. The challenge for beginners is the review step: knowing whether a diff is correct requires enough understanding of the code to evaluate it. The practitioner pattern that works for non-expert users is to use Claude Code for tasks where the output is directly testable — write a test, run it, see if it passes — rather than for tasks where correctness requires reading and understanding the generated logic.',\n },\n {\n id: 'q2-5', num: '2.5',\n question: 'Can I use Claude Code without coding knowledge?',\n answer: '<strong>For bounded, testable tasks — yes; for open-ended engineering work — no.</strong> The limiting factor is not the tool\\'s interface but your ability to recognize when Claude has misunderstood the goal, which requires knowing what \"correct\" looks like for the specific task. Users without coding knowledge have successfully used Claude Code for data processing, file organization, simple script automation, and research tasks where the output is text or structured data they can evaluate directly. The failure mode is adopting it for work where correctness requires code comprehension — and then merging Claude\\'s output without understanding it.',\n },\n {\n id: 'q2-6', num: '2.6',\n question: 'Can you use Claude Code for personal use?',\n answer: '<strong>Yes — there is no restriction to commercial or professional use</strong>; the Pro plan at $20/month or an Anthropic API key with pay-as-you-go billing both cover personal projects without restriction. Personal use cases that work well include automating repetitive file tasks, building personal utilities, learning a new language or framework by having Claude scaffold examples and explain them, and running competitive analysis scripts. The API key path is often more economical for personal use: if you spend under a few dollars per day on average, pay-as-you-go will cost less than the $20/month Pro subscription.',\n },\n {\n id: 'q2-7', num: '2.7',\n question: 'Can you use Claude Code just to chat?',\n answer: '<strong>Technically yes, but it is an expensive and poorly-optimized path for conversation only</strong> — Claude Code is built for filesystem-aware, command-executing sessions, and for general conversation, Claude.ai is a better fit and may be cheaper depending on your plan. Running a chat-only session inside Claude Code consumes the same token budget as a coding session, which means you are burning rate-limit capacity on messages that would cost less (or nothing, on a free Claude.ai tier) in the standard interface. The one exception: if you are in the middle of a coding session and need to think through an architectural question without switching contexts, the terminal is a reasonable place to have that conversation.',\n },\n {\n id: 'q2-8', num: '2.8',\n question: 'Can you prompt Claude Code?',\n answer: '<strong>Yes — you interact with Claude Code entirely through natural language prompts in the terminal</strong>, and you can store persistent instructions in a CLAUDE.md file in your project root so preferences, coding standards, and architectural decisions carry across sessions without re-stating them. The CLAUDE.md file is the most underused feature for teams: it allows you to encode your stack\\'s conventions, preferred libraries, test patterns, and style rules once, so every Claude Code session starts with that context loaded. Practitioners who skip CLAUDE.md setup spend significantly more tokens re-explaining context that should be a project-level constant.',\n },\n ],\n },\n {\n id: 'cost-math-and-evaluation',\n num: '03',\n title: 'The Real Cost Math',\n titleItalic: 'Before You Commit',\n deck: 'The subscription price is not the most important number — the ratio between what you pay at Max 5x versus what the same usage costs on raw API tokens is 18-to-1, and almost no review article publishes it.',\n callout: {\n eyebrow: 'Evaluation layer — what every tutorial skips',\n heading: 'You can run Claude Code today without a paid subscription.',\n body: 'An Anthropic API key unlocks full Claude Code functionality on a <strong>pay-as-you-go basis</strong> — no $20/month Pro plan required. The average developer spends about <strong>$6 per day</strong> at API rates; for light users, this path is cheaper than a monthly plan and removes the cost-before-commit barrier entirely.',\n },\n cards: [\n {\n id: 'q3-1', num: '3.1',\n question: 'How to use Claude Code for free?',\n answer: '<strong>There is no free Claude Code plan</strong>, but you can use it without a subscription by providing an Anthropic API key and paying per token — and for light users, this often costs less per month than the $20/month Pro subscription. The Free plan on Claude.ai does not include Claude Code access. The API key path requires a funded Anthropic account but no minimum spend: a few evaluation sessions will cost a few dollars, not $20. The distinction matters: \"no free plan\" and \"no way to try it without committing $20\" are different conditions, and almost every article conflates them.',\n },\n {\n id: 'q3-2', num: '3.2',\n question: 'Can I run Claude Code locally for free?',\n answer: '<strong>Not for free with Claude models</strong>, but Claude Code can be configured to run against local Ollama-compatible models, which eliminates API costs entirely — at the cost of model quality relative to Claude Sonnet 4.6. Running Claude Code against a local Ollama model means all inference stays on your machine: no API call, no token spend, no data leaving your network. The trade-off is that local open-source models perform significantly below Claude Sonnet 4.6 on SWE-bench and similar coding benchmarks, so the output quality for complex tasks is not comparable. For privacy-sensitive experimentation or cost-zero prototyping, local Ollama is a legitimate path; for production coding work, the quality gap is real.',\n },\n {\n id: 'q3-3', num: '3.3',\n question: 'Can I try Claude Code without paying?',\n answer: '<strong>The API key path requires a funded Anthropic account, but there is no minimum spend</strong> — you can run several evaluation sessions for a few dollars without committing to any monthly plan. Create an Anthropic account, add a small credit balance ($5–$10 is enough for meaningful evaluation), generate an API key, and authenticate Claude Code with it. At Sonnet 4.6 rates ($3/MTok input, $15/MTok output), a few hours of coding sessions will cost well under $10. This is the evaluation path that no ranking article describes explicitly, which is why most searchers believe the choice is binary: $20/month Pro or nothing.',\n },\n {\n id: 'q3-4', num: '3.4',\n question: 'Is Claude Code free now?',\n answer: '<strong>No — the Free plan does not include Claude Code access</strong>; the tool requires Pro ($20/month), Max ($100–$200/month), Team Premium ($100–$125/seat/month), or an Anthropic API key with pay-as-you-go billing. As of 2026-05-24, Anthropic has not announced a free tier for Claude Code. The \"free\" path that does exist is the API key route for low-volume users who spend less monthly than the Pro subscription would cost — which is free of subscription commitment but not free of per-token cost. If you are searching this question because you saw \"free\" mentioned somewhere, it likely refers to the absence of a required subscription for the API key path, not zero-cost access.',\n },\n {\n id: 'q3-5', num: '3.5',\n question: 'How much is Claude Code per month?',\n answer: '<strong>Pro: $20/month (or $17/month billed annually); Max 5x: $100/month; Max 20x: $200/month; Team Premium: $125/seat/month (or $100/seat/month annually), minimum 5 seats, Claude Code included; API key: pay-as-you-go, average approximately $6/developer/day.</strong> Team Standard ($25/seat/month) does not include Claude Code. The Max plans exist because Pro has a usage ceiling — approximately 44,000 tokens per 5-hour window — that active power users hit daily; Max 5x roughly doubles that to 88,000 tokens, and Max 20x reaches approximately 220,000 tokens per window. For developers who would otherwise pay API rates at those volumes, the Max plan is dramatically cheaper than the alternative.',\n },\n {\n id: 'q3-6', num: '3.6',\n question: 'Is it worth it to pay for Claude for coding?',\n answer: '<strong>At Pro ($20/month), the break-even is roughly one hour of professional developer time saved per month</strong> — for developers using it daily on real tasks, the ratio is heavily favorable. The harder question is whether you need Max-tier throughput: if you hit the Pro usage ceiling regularly, you are spending time waiting for windows to reset instead of working, and the $80/month step-up to Max 5x pays for itself quickly. For occasional users — a few sessions per week on bounded tasks — the API key path at average $6/developer/day will cost less than $20/month and provides the same capability without the subscription commitment.',\n },\n {\n id: 'q3-7', num: '3.7',\n question: 'Is Claude Code actually worth it?',\n answer: '<strong>For developers running multi-file tasks daily, yes — the Max plan is approximately 18x cheaper than equivalent API usage at full capacity</strong>; for occasional users, the API key path is more economical and the subscription adds no value. The 18x figure comes from the projected cost of purchasing the same token volume directly at Sonnet 4.6 rates ($3/MTok input): at Max 20x throughput sustained, the API equivalent would run approximately $3,650/month versus $200/month for the Max 20x plan. The honest framing for an evaluation decision: start with the API key path, measure your actual daily spend for two weeks, then decide whether the Pro or Max subscription saves money relative to your real usage pattern.',\n },\n {\n id: 'q3-8', num: '3.8',\n question: 'How expensive is it to use Claude Code?',\n answer: '<strong>Light use on an API key: under $2/day; moderate use on Pro: $20/month flat; heavy agentic use on Max 5x: $100/month for approximately 88,000 tokens per 5-hour window</strong>, versus approximately $3,650/month if you paid API rates for the same volume. Ninety percent of API-path users spend under $12/day. Prompt caching reduces costs further for long sessions with repeated context: Sonnet 4.6 cache reads cost $0.30/MTok versus $3/MTok for fresh input — a 90% discount on context that is already in the cache. The Batch API adds a 50% discount across all token prices for non-real-time workloads. Heavy users who ignore caching and batching pay 2–3x more than necessary.',\n },\n {\n id: 'q3-9', num: '3.9',\n question: 'Is Claude Code no longer pro?',\n answer: '<strong>Claude Code remains available on the Pro plan</strong> — the Max plans (5x and 20x) are higher-throughput tiers added for power users, not replacements for Pro. Pro was not removed or downgraded; it retains the same model access (Sonnet 4.6 and Opus 4.7) as Max, with tighter usage limits per 5-hour window (approximately 44,000 tokens). The confusion likely stems from Anthropic\\'s introduction of the Max tier, which is marketed heavily to power users — but Pro is still the primary entry point for individual developers who do not consistently hit usage ceilings.',\n },\n ],\n },\n {\n id: 'comparison-and-switching',\n num: '04',\n title: 'Claude Code vs. the Tools',\n titleItalic: 'You Already Use',\n deck: 'The three tools most developers compare against Claude Code — Cursor, GitHub Copilot, and ChatGPT — answer different questions than Claude Code does, and picking the wrong framing makes the comparison meaningless.',\n callout: {\n eyebrow: 'Decision-stage question the SERP ignores',\n heading: 'Most professional teams use Claude Code alongside Cursor or Copilot, not instead of them.',\n body: 'The most common production stack is <strong>Cursor for inline editing</strong> (72% autocomplete acceptance rate with Supermaven) <strong>+ Claude Code for complex multi-file tasks</strong> in the terminal — or Copilot in the IDE + Claude Code for architectural work. Picking one and dropping the other is a false choice.',\n },\n cards: [\n {\n id: 'q4-1', num: '4.1',\n question: 'Why are people leaving ChatGPT and going to Claude?',\n answer: '<strong>Claude\\'s models score higher on coding benchmarks (80.8% SWE-bench Verified) and have a substantially longer context window (1M tokens versus 128k for GPT-4o)</strong>, and Claude Code offers deeper filesystem integration than ChatGPT Codex or the GPT-4 API. For developers specifically, the context window difference is the most consequential: 1M tokens allows Claude Code to reason over an entire codebase; 128k limits competing tools to a subset of files. The SWE-bench gap is real but less dramatic than marketing implies — both tools fail a meaningful percentage of tasks, and the right comparison is not benchmark scores but how each tool behaves on your specific workload.',\n },\n {\n id: 'q4-2', num: '4.2',\n question: 'Is ChatGPT or Claude better?',\n answer: '<strong>For agentic coding tasks, Claude Code leads on SWE-bench Verified at 80.8%</strong>; for general conversation, document analysis, and multimodal tasks, the gap between the two is smaller and depends on the specific benchmark. Neither is universally better: GPT-4o has advantages in certain multimodal contexts; Claude Sonnet 4.6 leads on long-context coding tasks. The evaluation question for a developer is not \"which is better overall\" but \"which handles my workload better\" — the two tools have different context window sizes, different pricing structures, and different agentic execution models, and those differences matter more than aggregate benchmark rankings.',\n },\n {\n id: 'q4-3', num: '4.3',\n question: 'Why are people switching to Claude?',\n answer: '<strong>The three primary reasons developers cite: longer context window (1M versus 128k), stronger agentic task performance on SWE-bench, and Claude Code\\'s full-filesystem terminal approach versus chat-based alternatives.</strong> A secondary factor is Constitutional AI training, which produces a model that more often says \"I don\\'t know\" or flags uncertainty rather than generating confidently wrong output — a meaningful difference for code review workflows where false confidence is costly. Developers who switched from ChatGPT-based workflows most commonly cite hitting GPT-4o\\'s context limit on large codebase tasks as the triggering event.',\n },\n {\n id: 'q4-4', num: '4.4',\n question: 'Is Copilot cheaper than ChatGPT?',\n answer: '<strong>GitHub Copilot Pro at $10/month is the lowest-priced individual plan in this comparison</strong>: ChatGPT Plus is $20/month, Claude Pro is $20/month, and Cursor Pro is $20/month. Copilot also offers a team plan at $19/seat/month and enterprise at $39/seat/month. The price comparison is misleading without capability context: Copilot at $10/month provides IDE-integrated autocomplete and code chat; it does not include a standalone agentic coding tool equivalent to Claude Code. Developers who need both inline autocomplete (Copilot\\'s strength) and multi-file agentic task completion (Claude Code\\'s strength) are looking at $10 + $20 = $30/month minimum, not a choice between them.',\n },\n {\n id: 'q4-5', num: '4.5',\n question: 'Who hallucinates more — ChatGPT or Claude?',\n answer: '<strong>Both hallucinate; the more useful comparison for coding tasks is SWE-bench score</strong>, which measures how often a model actually solves a real-world issue correctly rather than generating plausible-looking wrong code — and Claude Code leads at 80.8%. Claude\\'s Constitutional AI training is designed to produce more calibrated uncertainty: the model is more likely to say it does not know something than to confabulate a confident but wrong answer. In practice, both tools will generate syntactically valid code that fails tests, fabricate library method names, and misread logic — the difference is in frequency and in how they signal uncertainty. For any coding output from either tool, running tests and reviewing diffs is non-optional.',\n },\n {\n id: 'q4-6', num: '4.6',\n question: 'What is Claude Code and Ollama?',\n answer: '<strong>Ollama runs open-source language models locally on your machine; Claude Code can be configured to use Ollama-compatible models as its backend, eliminating API costs and keeping all data entirely local.</strong> This configuration is relevant for two use cases: cost-zero experimentation without API spend, and air-gapped or high-security environments where sending code context to an external API is not permissible. The trade-off is model quality: local open-source models perform significantly below Claude Sonnet 4.6 on SWE-bench and complex coding tasks. The Ollama path is worth knowing because most articles presenting Claude Code as requiring a paid subscription omit it entirely — for teams with strong privacy requirements or zero budget, it is the only viable evaluation path.',\n },\n ],\n },\n {\n id: 'safety-trust-and-privacy',\n num: '05',\n title: 'Safety, Data Handling, and',\n titleItalic: 'What Claude Code Can Actually See',\n deck: 'The most common trust concerns about Claude Code — screenshot access, code ownership, data leakage — have clear factual answers, and none of the ranking articles provide them.',\n callout: {\n eyebrow: 'Enterprise and privacy-sensitive teams',\n heading: 'Claude Code does not store your code on Anthropic\\'s servers between sessions.',\n body: 'Your files run locally on your machine; only the <strong>conversation context</strong> (your prompts and Claude\\'s responses) is sent to Anthropic\\'s API. The Enterprise plan adds <strong>HIPAA-ready data handling</strong>, audit logs, custom data retention controls, and SCIM provisioning — features that unblock adoption for regulated industries.',\n },\n cards: [\n {\n id: 'q5-1', num: '5.1',\n question: 'Is it safe to use Claude Code?',\n answer: '<strong>For most professional use, yes — code executes locally, file access is local, and only prompt context traverses the API</strong>; sensitive or regulated data (HIPAA, PII) requires the Enterprise plan for compliant data handling. The practical risk surface for most developers is not data leakage but local command execution: Claude Code executes shell commands you authorize, and approving a destructive command without reviewing it produces real damage. For teams in regulated industries, the Enterprise plan adds HIPAA-ready data handling, audit logs, and custom data retention controls — the specific features required for compliant adoption in healthcare, finance, and similar sectors.',\n },\n {\n id: 'q5-2', num: '5.2',\n question: 'Can you get banned from Claude Code?',\n answer: '<strong>Yes — Anthropic\\'s usage policies apply to Claude Code, and automated or agentic misuse can trigger account suspension.</strong> The prohibited uses are the same as those that apply to Claude.ai: generating malware, automating scraping in violation of a site\\'s terms, producing content that violates Anthropic\\'s usage policy, and misrepresenting Claude-generated output as human-authored in contexts where that matters. Agentic tools that execute at scale create higher-velocity policy surface than chat interfaces — a Claude Code routine running unattended can produce policy violations faster than a human would catch them. Review Anthropic\\'s usage policy before deploying Claude Code in automated, unmonitored pipelines.',\n },\n {\n id: 'q5-3', num: '5.3',\n question: 'Does Claude Code leak?',\n answer: '<strong>No evidence of systemic data leakage exists</strong>; prompt context is transmitted to Anthropic\\'s API as part of normal operation but is not persisted beyond the session by default under standard plans. What \"leak\" means technically: your code appears in the prompt context sent to the API for inference; it is not stored, indexed, or accessible to other users. Enterprise plans add explicit custom retention controls and data-use opt-outs for teams who need contractual data handling guarantees rather than policy-level assurances. The meaningful risk for most teams is not leakage to other users but the transmission of proprietary code to an external API at all — a policy question, not a technical vulnerability.',\n },\n {\n id: 'q5-4', num: '5.4',\n question: 'Is Claude Code unsafe?',\n answer: '<strong>The primary risk surface is local command execution, not network security</strong> — Claude Code executes shell commands you authorize on your machine, and approving a destructive command without reviewing it produces damage that is real and often irreversible. Claude Code\\'s design requires your explicit approval before executing commands in most configurations, but users who approve commands quickly without reading the proposed action bypass the primary safety mechanism. The secondary risk is prompt injection in multi-agent configurations — a subtask agent receiving malicious instructions from an external source. Neither risk is exotic; both are manageable with standard review practices.',\n },\n {\n id: 'q5-5', num: '5.5',\n question: 'Can I trust Claude Code?',\n answer: '<strong>Trust is context-dependent: for local development tasks, yes; for regulated data, only under the Enterprise plan; for security-sensitive environments, verify the data handling documentation before adopting.</strong> The relevant trust question for most practitioners is not \"is Anthropic malicious\" but \"does sending my codebase context to an external API comply with my organization\\'s data handling policy\" — and that is a legal and compliance question, not a technical one. Enterprise teams evaluating Claude Code should request Anthropic\\'s data processing agreement and review the HIPAA-ready configuration before making adoption decisions; the documentation exists and is specific.',\n },\n {\n id: 'q5-6', num: '5.6',\n question: 'Does Claude Code take screenshots?',\n answer: '<strong>No — Claude Code does not have screen capture capability.</strong> It reads and writes files on your filesystem and executes terminal commands, but it cannot capture your screen, access your clipboard, read data from applications outside the project directory you opened it in, or observe your browser activity. The question comes up because agentic AI tools are often conflated with general system-access tools; Claude Code\\'s access is specifically scoped to your project directory and the shell commands you authorize it to run. If you are evaluating Claude Code for an environment where screen capture would be a security concern, that concern does not apply to this tool\\'s architecture.',\n },\n {\n id: 'q5-7', num: '5.7',\n question: 'Does ChatGPT own my code?',\n answer: '<strong>OpenAI\\'s standard terms do not claim ownership of output code</strong>, and Anthropic\\'s terms similarly do not claim ownership of code that Claude Code generates — but both companies\\' default terms may use your inputs to improve their models unless you opt out or upgrade to an enterprise plan. For most developers, code ownership is not the risk: the risk is whether code you submit as input context can be used in model training. Anthropic\\'s Enterprise plan includes explicit data-use opt-outs; OpenAI\\'s Enterprise plan does the same. On individual plans for either tool, review the current terms of service for training data opt-out provisions, which have changed multiple times across both platforms.',\n },\n {\n id: 'q5-8', num: '5.8',\n question: 'Why is Claude being blacklisted?',\n answer: '<strong>Some organizations block Claude via firewall because it is an external API service — not because of a known security vulnerability in Claude specifically.</strong> IT departments that treat all AI API services as unauthorized external data connections will block Claude Code, ChatGPT, Copilot, and similar tools under the same policy, regardless of their individual security properties. The blocking is a data governance decision, not a technical finding against Claude. For enterprise teams trying to get Claude Code approved through IT, the relevant artifacts are Anthropic\\'s data processing agreement, the HIPAA-ready Enterprise configuration documentation, and Anthropic\\'s SOC 2 compliance status — not the tool\\'s general reputation.',\n },\n ],\n },\n {\n id: 'limits-and-skepticism',\n num: '06',\n title: 'What Claude Code Gets Wrong',\n titleItalic: '(And What It Cannot Do)',\n deck: 'The performance ceiling that marketing materials never publish: Claude Code hallucinates, degrades in long sessions, and will confidently generate plausible-looking wrong code — knowing the failure modes before you adopt matters more than knowing the benchmark score.',\n cards: [\n {\n id: 'q6-1', num: '6.1',\n question: 'What is Claude not good at?',\n answer: '<strong>Novel algorithmic design requiring mathematical proof, highly domain-specific regulatory code with no training signal, real-time systems where every millisecond matters, and tasks requiring external context it cannot access</strong> — production databases, proprietary internal documentation, undocumented internal APIs — are the consistent failure categories. Claude Code reasons over what is in its context window; anything that must be inferred from systems it cannot read produces hallucinated or superficially correct but functionally wrong output. The most expensive failure mode in practice is not obvious errors but plausible-looking code that passes a surface review and fails in production — which is why running tests is not optional.',\n },\n {\n id: 'q6-2', num: '6.2',\n question: 'Is there a limit to how much you can use Claude Code?',\n answer: '<strong>Yes — rate limits vary by plan: Pro gets approximately 44,000 tokens per 5-hour window; Max 5x approximately 88,000; Max 20x approximately 220,000</strong>; hitting the ceiling pauses access until the window resets. These are approximate figures derived from usage reports; Anthropic does not publish the exact token limits by plan. The practical effect: heavy Pro users who run multiple long agentic sessions in a day will hit the ceiling and wait; Max 5x handles most power-user workflows without interruption. On the API key path, there are no usage windows — you pay per token with no ceiling, which is one reason the API path can be preferable for users who need uninterrupted long sessions.',\n },\n {\n id: 'q6-3', num: '6.3',\n question: 'Is Claude Code getting dumber?',\n answer: '<strong>Claude Code is not being degraded — Anthropic has not reduced model capability</strong>; perceived quality drops in long sessions typically trace to context window saturation, not model downgrade. When a session accumulates enough turns that older context is compressed or dropped to fit within the context window, Claude Code loses access to earlier decisions, file states, and constraints — and the output quality degrades visibly. The fix is to start a new session for major new tasks rather than extending a single session indefinitely. The \"getting dumber\" perception is real; the cause is session management, not model regression.',\n },\n {\n id: 'q6-4', num: '6.4',\n question: 'Does Claude Code hallucinate?',\n answer: '<strong>Yes — it will generate incorrect code, fabricate library method names, and misread file logic</strong>, and the SWE-bench score of 80.8% means it fails roughly 1 in 5 real-world tasks by the benchmark\\'s definition. Hallucination in coding contexts looks different from hallucination in conversation: the output is syntactically valid, the method names look plausible, and the logic structure appears correct — it fails when you run it. Always run tests and review diffs before merging Claude Code output; treating it as authoritative without validation is the most common source of expensive errors. The 80.8% score is the performance ceiling under benchmark conditions; your specific codebase, stack, and task distribution will produce a different empirical failure rate.',\n },\n {\n id: 'q6-5', num: '6.5',\n question: 'Can you run Claude Code without internet?',\n answer: '<strong>Not with Claude models — all inference goes through Anthropic\\'s API, which requires an active internet connection.</strong> The exception is running Claude Code configured against local Ollama-compatible models, which work fully offline with no API call required. For teams in air-gapped environments or with strict egress policies, the Ollama configuration is the only viable path to offline Claude Code use — at the cost of model quality. Anthropic does not currently offer an on-premises deployment option for Claude models equivalent to some enterprise AI vendors; the Enterprise plan provides stronger data handling guarantees but still routes inference through Anthropic\\'s API.',\n },\n {\n id: 'q6-6', num: '6.6',\n question: 'Is Claude Code actually useful?',\n answer: '<strong>For developers running multi-file refactors, complex debugging cycles, or greenfield scaffolding, yes — the SWE-bench benchmark performance and consistent practitioner time-savings reports are aligned</strong>; for users expecting zero-verification autonomous output, no. The honest evaluation frame: Claude Code is a force multiplier for developers who can review its output, not an autonomous agent that eliminates the need for developer judgment. Teams that have adopted it most successfully use it for the high-context, high-effort tasks that benefit most from 1M-token reasoning — not as a replacement for understanding the codebase.',\n },\n {\n id: 'q6-7', num: '6.7',\n question: 'Is Claude Code still the best coding agent?',\n answer: '<strong>As of mid-2026, Claude Code leads on SWE-bench Verified at 80.8% and on context window size at 1M tokens</strong>; Cursor leads on inline autocomplete acceptance rate at 72% with Supermaven; the \"best\" answer depends entirely on whether you optimize for agentic task completion or IDE-native editing speed. The benchmark lead is real but not permanent: SWE-bench scores across competing tools have risen steadily, and the gap between leaders narrows with each model generation. The more durable evaluation criterion than benchmark ranking is which tool handles the specific failure modes that matter most in your codebase — which requires empirical testing, not reading rankings.',\n },\n ],\n },\n ],\n },\n {\n slug: 'vibe-coding',\n title: 'Vibe Coding: The Complete Honest Guide',\n titleDisplay: 'Vibe',\n titleDisplayItalic: 'Coding.',\n description: 'What vibe coding actually is, which free tools work, whether you can get hired, and the data layer no tutorial mentions — 30 questions answered without the hype.',\n publishedAt: '2026-05-24',\n author: 'MCP Scraper',\n authorInitials: 'M',\n tags: ['vibe coding', 'AI', 'development', 'tools', 'careers'],\n category: 'AI Development',\n badge: '30 questions answered',\n fieldGuideLabel: 'Field guide',\n deck: 'Andrej Karpathy coined \"vibe coding\" on February 2, 2025. By November it was Collins Word of the Year. Most of what you\\'ll read about it sells a tool. This is the version that tells you what works, what breaks, what it pays, and what you still have to build yourself after the prototype runs.',\n readTimeMinutes: 14,\n stats: [\n { value: '6', label: 'sections' },\n { value: '30', label: 'questions' },\n { value: '7', label: 'tools compared' },\n { value: '0', label: 'fluff' },\n ],\n ctaHeading: 'Your vibe-coded app needs',\n ctaHeadingItalic: 'real data.',\n ctaBody: 'MCP Scraper gives your AI-generated tools the live web data they need — SERP results, People Also Ask harvests, page extraction, and structured scraping — without writing a single line of custom scraping logic.',\n sections: [\n {\n id: 's1',\n num: '01',\n title: 'What Is Vibe',\n titleItalic: 'Coding?',\n deck: 'The definition everyone links to, the origin story most posts get half-wrong, and the uncomfortable truth no one else in the top results will say.',\n callout: {\n eyebrow: 'Origin story',\n heading: '\"The hottest new programming language is English.\"',\n body: 'Andrej Karpathy said that in 2023. On February 2, 2025, he named the practice: vibe coding. <strong>The tweet reached 4.5 million views in days.</strong> Merriam-Webster added the term on March 8, 2025. Collins named it Word of the Year on November 6, 2025. The definition matters because everyone is now selling their own version of it.',\n },\n cards: [\n {\n id: 'q1-1', num: '1.1',\n question: 'Is vibe coding just having AI code for you?',\n answer: '<strong>Not exactly.</strong> In Karpathy\\'s original framing, vibe coding is a specific mode: you describe what you want, you accept the code without reading it, and you treat bugs as a vibe to ride rather than a problem to debug. Merriam-Webster\\'s listing makes the same point — coders don\\'t need to understand how the code works and must accept that bugs will be present. That is narrower than \"AI-assisted development,\" where you still review, accept, or reject every suggestion the model makes. Most products marketed as \"vibe coding\" today — Cursor, Copilot, Windsurf — are actually closer to AI-assisted development. <em>The distinction matters because the risk profile is completely different the moment you start reading the output.</em>',\n },\n {\n id: 'q1-2', num: '1.2',\n question: 'Is vibe coding difficult?',\n answer: '<strong>For prototypes, no. For anything you have to maintain, yes.</strong> The first working version of a tool — a price tracker, a Slack bot, a small dashboard — comes out of a vibe coding session in under an hour. The difficulty curve spikes the moment you try to extend it, debug a regression, or scale it past the original prompt. According to recent surveys, 66% of developers report spending more time fixing \"almost-right\" AI code than they save generating it. That gap is not in the tutorials. The skill that becomes hard is not writing code — it is reading code you didn\\'t write, figuring out which AI suggestion to trust, and knowing when to throw the prototype away and rebuild it properly.',\n },\n {\n id: 'q1-3', num: '1.3',\n question: 'Is vibe coding a skill?',\n answer: '<strong>Yes — but not the skill most people expect.</strong> The transferable skill is prompt precision: describing system behavior clearly enough that the model produces the right thing on the first try. Add to that systems thinking (understanding how parts of an app talk to each other), and the judgment to spot when AI output is wrong before it ships. None of those require knowing syntax. All of them improve with practice. Vibe coders who succeed treat prompting as engineering — they iterate on prompts the way developers iterate on code, with versioned specs and explicit edge cases. Vibe coders who fail treat prompting as wishful thinking and re-prompt with the same vague description hoping for a better outcome.',\n },\n {\n id: 'q1-4', num: '1.4',\n question: 'Can anyone be a vibe coder?',\n answer: '<strong>For personal tools and throwaway prototypes — yes.</strong> For anything with real users, the practical floor is higher than the marketing suggests. The clearest data point: 25% of Y Combinator\\'s W25 cohort is running codebases that are almost entirely AI-generated, but every one of those teams has a technical founder who understands what the AI is producing. <em>The pattern in successful vibe-coded products is not \"no engineering knowledge\" — it is \"engineering judgment without the syntax overhead.\"</em> If you have never thought about how data flows through a system, you can still ship a working prototype. You will struggle the first time it breaks in front of a real user, and that moment arrives faster than most tutorials let on.',\n },\n {\n id: 'q1-5', num: '1.5',\n question: 'What is the uncomfortable truth about vibe coding?',\n answer: '<strong>AI generates code that looks correct and isn\\'t.</strong> The output reads cleanly, runs locally, and passes a casual review. It also reportedly contains SQL injection paths, leaked API keys in client-side bundles, overly permissive CORS configurations, and authentication logic that fails silently on edge cases. According to recent surveys, trust in AI code accuracy fell from 40% to 29% over the last year — not because the models got worse, but because more developers spent enough time with the output to see what it actually does. The \"lower technical barrier\" framing is honest for Day 0. It is actively misleading for Day 1 and beyond, when the cost of not understanding your own code starts showing up in production.',\n },\n ],\n },\n {\n id: 's2',\n num: '02',\n title: 'Vibe Coding',\n titleItalic: 'Tools.',\n deck: 'Seven tools ranked honestly — including which ones are free, which are worth paying for, and the one question the tool comparison tables never answer.',\n callout: {\n eyebrow: 'Tool warning',\n heading: 'No single tool handles Day 0 and Day 1+.',\n body: 'Every tool is Day 0 optimized. None owns Day 1+. The emerging pattern: tools that let you build fast have weak maintenance stories. <strong>The moment you need to debug, audit, or extend AI-generated code, you\\'re on your own.</strong> Pick your tool knowing this gap exists — and budget separately for the data, deployment, and review layers that none of them include.',\n },\n cards: [\n {\n id: 'q2-1', num: '2.1',\n question: 'What is the best vibe coding platform for beginners?',\n answer: '<strong>Bolt.new and Replit are the two clearest entry points for non-developers.</strong> Both run entirely in the browser, both deploy a working app in one session, both require zero local setup. Bolt.new leans further toward \"type what you want, see it run.\" Replit has a stronger long-term workspace because the project persists with files you can edit later. For developers who already have a code editor and want AI inside it, Cursor (2M+ users, $2B ARR) and Windsurf (1M+ active users) are the strongest options. The honest split: pick browser-based if you have never installed VS Code; pick Cursor or Windsurf if you have.',\n },\n {\n id: 'q2-2', num: '2.2',\n question: 'Are there any free vibe coding tools?',\n answer: '<strong>Yes. A definitive free-tier map.</strong> Bolt.new offers a free plan with daily token limits — enough to ship a small project, not enough to iterate heavily. Replit\\'s free tier supports small public projects and runs in the browser. Aider is fully open-source and free, runs locally against any LLM you connect. Continue.dev is a free VS Code extension. Claude.ai and ChatGPT both offer free-tier chat that can generate complete code you paste into a runner. GitHub Copilot has a free tier for individuals. Cursor has a free tier with limited completions. <em>The only thing that is not free across all of these: heavy daily usage. Every product gates volume, not access.</em>',\n },\n {\n id: 'q2-3', num: '2.3',\n question: 'Which AI for vibe coding?',\n answer: '<strong>Claude and the GPT family are the two strongest code-generation models.</strong> For IDE-integrated use, two products dominate: GitHub Copilot leads on raw scale (20M total users, 4.7M paid subscribers, reportedly 42% of the AI coding assistant market) and Claude Code leads on satisfaction (91% CSAT in recent surveys, the highest of any AI coding tool measured). Developers using Copilot reportedly complete tasks 55% faster, with PR time dropping from 9.6 days to 2.4 days. The pragmatic answer: use Copilot if you live in VS Code and want the largest ecosystem; use Claude Code if you want the model most developers say produces the fewest \"almost-right\" answers.',\n },\n {\n id: 'q2-4', num: '2.4',\n question: 'Which AI agent is best for vibe coding?',\n answer: '<strong>For autonomous multi-step tasks, the leaders are Cursor\\'s Composer, Windsurf\\'s Cascade, and Claude Code.</strong> Cascade handles the most aggressive agentic workflows out of the box — multi-file refactors, end-to-end feature builds — and Windsurf was acquired by Cognition in December 2025 for reportedly ~$250M, which consolidated agentic capability under one roof. Claude Code is the highest-satisfaction agentic tool in developer surveys. <em>The honest tradeoff: more autonomy means less review, which means more time fixing surprises later.</em> Pick the level of autonomy you actually want to audit.',\n },\n {\n id: 'q2-5', num: '2.5',\n question: 'Can you vibe code with ChatGPT?',\n answer: '<strong>Yes, but ChatGPT alone is a copy-paste workflow.</strong> You describe what you want, ChatGPT generates the code, you paste it into a runner — Replit, your local terminal, a CodeSandbox tab. That works, and it is genuinely free at the entry tier. The friction is everything between paste and run: you don\\'t get inline edits, you don\\'t get file-level context, and you re-paste the whole project every time you want a change. A better free path is ChatGPT plus Replit (ChatGPT writes, Replit runs and persists) or ChatGPT inside an IDE through a plugin. ChatGPT is a viable free-tier route into vibe coding. It is not the best long-term home for a real project.',\n },\n {\n id: 'q2-6', num: '2.6',\n question: 'What\\'s the best app for vibe coding?',\n answer: '<strong>\"App\" depends on the device.</strong> On desktop: Cursor is the most powerful for developers, Bolt.new is the fastest for non-coders, and Replit is the most balanced if you want both browser convenience and a real workspace. On mobile: Replit\\'s iOS app is the only environment that supports full project builds from a phone. <em>Claude.ai and ChatGPT also run in mobile browsers and can generate code you deploy elsewhere, but neither is a complete vibe coding environment on its own.</em> The next section covers iPhone-specific workflows in detail — most posts skip this entirely, even though the question shows up clearly in search.',\n },\n ],\n },\n {\n id: 's3',\n num: '03',\n title: 'How to Start',\n titleItalic: 'Vibe Coding.',\n deck: 'A step-by-step path for non-developers, the one thing every tutorial skips (getting real data into your tool), and the iPhone question every other guide ignores.',\n callout: {\n eyebrow: 'Critical gap',\n heading: 'The prototype always works. The live version needs a data layer.',\n body: 'Prompting AI to build a tool is 30 minutes. Getting it live data is where most projects die. Every useful vibe-coded tool eventually needs to read from the real web. Price trackers need prices. Research tools need pages. Lead generators need business data. <strong>That\\'s where MCP Scraper enters the workflow.</strong>',\n },\n cards: [\n {\n id: 'q3-1', num: '3.1',\n question: 'How should I start vibe coding?',\n answer: '<strong>Three steps. In this order.</strong> First, pick a browser-based tool — Bolt.new or Replit — so you skip every \"install this, configure that\" trap that kills momentum on day one. Second, describe one specific thing you want to build, not a general app idea. \"A tool that emails me when the price of these three Amazon products drops\" beats \"an e-commerce price tracker\" because the model can build the first one in one pass. Third, test the output immediately and iterate with precise corrections — name the file, the function, and the exact behavior you want changed. <em>The most common beginner failure is prompting vaguely and then re-prompting with more vagueness, hoping the model figures it out.</em>',\n },\n {\n id: 'q3-2', num: '3.2',\n question: 'Can you learn coding from vibe coding?',\n answer: '<strong>Yes for systems thinking. No for syntax.</strong> Vibe coding teaches you to think in terms of inputs, outputs, state, and failure modes — the conceptual layer that makes a developer effective. It teaches debugging logic, because you spend real time reading errors and asking the model what they mean. It teaches prompt precision, which transfers to spec-writing in any technical role. What it does not teach is syntactic fluency, language-specific idioms, or low-level architecture decisions. If your goal is to be hired as a traditional software engineer at a company that interviews on syntax, vibe coding is an accelerator for concepts and a poor substitute for fundamentals. If your goal is to ship products, the concepts matter more.',\n },\n {\n id: 'q3-3', num: '3.3',\n question: 'Can I vibe code on my iPhone?',\n answer: '<strong>Yes, with real limitations.</strong> Replit has a functional iOS app that supports full project builds, file editing, and deployment from your phone. Claude.ai and ChatGPT both run in Safari and can generate complete code you paste into Replit or another runner. Bolt.new is browser-accessible on mobile, though the desktop layout is the supported experience. No current iOS tool matches the desktop IDE experience for serious projects. For prototyping a small tool, drafting a Slack bot, or sketching the first version of an app idea, your iPhone is a viable vibe coding environment — and it is the only environment most travelers have on day one of an idea.',\n },\n {\n id: 'q3-4', num: '3.4',\n question: 'Does Apple ban vibe coded apps?',\n answer: '<strong>No.</strong> Apple\\'s App Store Review Guidelines do not categorically ban AI-generated code. What they ban — and have always banned — are thin wrappers around web content, spam apps with no original functionality, and apps that violate content or privacy policies. None of those rules are AI-specific. The accountability standard is unchanged: the developer is responsible for the app\\'s behavior, regardless of how the code was produced. <em>A vibe-coded native app that genuinely does something useful for the user, handles data responsibly, and meets the same review bar as any other app will pass review.</em> A vibe-coded app that wraps a website in a webview and adds nothing will not — and would not have passed review in 2018 either.',\n },\n {\n id: 'q3-5', num: '3.5',\n question: 'Is vibe coding good?',\n answer: '<strong>For prototyping and personal tools — unambiguously yes. For production at scale — only with engineering oversight.</strong> The data supports both halves. 84% of developers use or plan to use AI coding tools, with average savings of 3.6 hours per week. 25% of Y Combinator\\'s W25 cohort runs nearly all-AI-generated codebases. At the same time, 66% of developers report spending more time fixing \"almost-right\" AI code than they save generating it, according to recent surveys. The honest read: vibe coding is excellent for the 80% of ideas that never needed production-grade code in the first place, and it is genuinely risky for the 20% that do. The skill is knowing which one you\\'re building.',\n },\n ],\n },\n {\n id: 's4',\n num: '04',\n title: 'The Real',\n titleItalic: 'Limits.',\n deck: 'Security vulnerabilities, the Day 0 vs. Day 1+ cliff, what happens when AI-generated code hits production — and how to protect yourself.',\n callout: {\n eyebrow: 'Industry stat',\n heading: '66% of developers reportedly spend more time fixing AI code than generating it.',\n body: 'This is not an argument against AI coding tools. It is an argument for using them with your eyes open. <strong>The productivity gains are real — if you know when to trust the output and when to audit it.</strong>',\n },\n cards: [\n {\n id: 'q4-1', num: '4.1',\n question: 'Do vibe coders understand their code?',\n answer: '<strong>Most do not, by design.</strong> Karpathy\\'s original framing is explicit: you give in to the vibes, you accept the code without reading it, you let the model handle bugs by reprompting rather than debugging. Merriam-Webster\\'s definition repeats the same idea — practitioners don\\'t need to understand how the code works and must accept bugs will be present. That is fine for personal tools you can throw away. It is a serious liability for anything deployed to real users: you cannot debug a system you do not understand, you cannot catch security issues you never look for, and you cannot tell a user with confidence what your app actually does with their data. The Day 0 advantage becomes the Day 1+ liability.',\n },\n {\n id: 'q4-2', num: '4.2',\n question: 'What are the security risks of vibe coding?',\n answer: '<strong>AI-generated code commonly introduces SQL injection vulnerabilities, insecure API key handling, overly permissive CORS configurations, and authentication logic that fails silently on edge cases.</strong> None of these are visible to a non-developer reviewing the output, because the code looks correct. The mitigation is not \"review your code more carefully\" — that asks vibe coders to do exactly what they came here not to do. The mitigation is an automated security scanner. Snyk, GitHub Advanced Security, and Semgrep each run continuously and flag the common AI-generated mistakes before deploy. <em>Pair every vibe coding session with a scanner that runs on commit, and you eliminate the most common production-breaking class of mistake without learning to read every line.</em>',\n },\n {\n id: 'q4-3', num: '4.3',\n question: 'When should you not vibe code?',\n answer: '<strong>Three hard cases.</strong> First, systems handling personal data under GDPR, HIPAA, PCI-DSS, or similar — AI-generated code needs auditing you cannot DIY, and the regulatory penalty for getting it wrong is higher than the time you saved. Second, financial transaction logic — silent rounding errors, race conditions, and authorization gaps compound quickly and quietly. Third, anything you need to maintain for more than 12 months without a developer — AI-generated codebases become unmaintainable faster than hand-written code because the structure was never designed for change. <em>Vibe code freely when the cost of being wrong is small. Bring in engineering when the cost of being wrong is asymmetric.</em>',\n },\n {\n id: 'q4-4', num: '4.4',\n question: 'What is the Day 1+ problem in vibe coding?',\n answer: '<strong>Every vibe coding tool is optimized for the first working build. None of them is optimized for what comes after.</strong> Day 1+ problems are predictable: adding a second feature without breaking the first, debugging a regression you cannot trace, scaling to enough users that the original architecture cracks, and integrating third-party APIs whose contracts change. The tools do not advertise this gap because Day 0 demos sell better than maintenance demos. One developer-practitioner survey concluded that no single tool today can build and maintain an entire application end-to-end. <em>Plan for the Day 1+ moment before it arrives. Pick a tool with file-level access, version control, and an escape hatch into real code review.</em>',\n },\n {\n id: 'q4-5', num: '4.5',\n question: 'Can a vibe-coded app go viral?',\n answer: '<strong>Yes — with documented examples.</strong> One builder shipped more than ten vibe-coded apps that were used reportedly close to a million times in total before scaling and maintenance pressure forced a pause. Kevin Roose\\'s LunchBox Buddy — a fridge-photo-to-meal-suggestion tool — was cited in the New York Times as an early vibe coding demo. Refetch, an open-source Hacker News alternative, was reportedly built in 15 hours of vibe coding on Appwrite Cloud. The pattern across these cases is consistent: vibe-coded apps scale to early traction with no problem, and the crisis arrives when traffic, data volume, or feature scope exceeds what the original AI-generated architecture was built for. That crisis is solvable. It just doesn\\'t solve itself.',\n },\n ],\n },\n {\n id: 's5',\n num: '05',\n title: 'Career &',\n titleItalic: 'Hiring.',\n deck: 'Six questions no competitor will answer — whether vibe coding is a real job, what it pays, and how to position it when you\\'re applying.',\n callout: {\n eyebrow: 'Hiring signal',\n heading: 'The fastest-growing startups have already decided vibe coding is production-ready.',\n body: '25% of Y Combinator\\'s current cohort runs codebases that are almost entirely AI-generated. IBM cites the stat. Google ignores it. Medium skips it. None asks the obvious next question: what does that mean for the person reading this article? <strong>The job market is catching up.</strong>',\n },\n cards: [\n {\n id: 'q5-1', num: '5.1',\n question: 'Is vibe coding a real job now?',\n answer: '<strong>It is becoming one — fastest at startups, slowest at large enterprises.</strong> Early-stage companies increasingly list \"AI-assisted developer,\" \"AI product builder,\" \"prompt engineer,\" and \"no-code/AI builder\" roles. Freelance platforms like Upwork and Fiverr have active vibe coding service categories with steady project volume. At enterprise scale, formal \"vibe coder\" titles are still rare — but the practice is embedded in 18% of developers\\' day-to-day work according to JetBrains\\' January 2026 survey, and at the YC startups where 25% of codebases are nearly all AI-generated, it is the default daily practice. <em>The title is lagging the work by roughly 18 months. The work is already mainstream.</em>',\n },\n {\n id: 'q5-2', num: '5.2',\n question: 'Do companies hire vibe coders?',\n answer: '<strong>Early-stage startups and solo-founder companies actively do.</strong> Common titles include \"growth engineer,\" \"founding engineer,\" \"AI product builder,\" and \"technical founder in residence.\" The hiring signal is clearest in YC-backed companies and Series A startups where shipping speed matters more than code purity. Traditional enterprise software companies have been slower — their hiring processes are designed to test syntax and system-design fundamentals, which vibe coders often have not formally studied. The market direction is consistent with broader adoption: the AI coding tools market reportedly reached $7.37 billion in 2025, and 84% of developers use or plan to use AI tools. <em>The companies hiring fastest are the ones building fastest.</em>',\n },\n {\n id: 'q5-3', num: '5.3',\n question: 'Do vibe coders get hired?',\n answer: '<strong>If you can show working products — yes. The portfolio matters more than the title.</strong> Demonstrating that you shipped a functional tool used by real people is more compelling to early-stage hiring managers than a CS degree or a coding bootcamp certificate. A live URL with real users beats a GitHub repo with no traction. The friction point is larger companies whose engineering interviews are designed around whiteboard syntax problems vibe coders have not drilled on. The pragmatic path: build five shippable tools, get real users for at least one, document what you built and what you learned, and apply to companies whose hiring is portfolio-driven rather than interview-driven. The first job is the hardest. After the first job, the portfolio compounds.',\n },\n {\n id: 'q5-4', num: '5.4',\n question: 'How much do vibe coders make?',\n answer: '<strong>Ranges vary by context. Approximate market signal as of 2026:</strong> Freelance project rates on Upwork and Fiverr for AI-built tools reportedly range from $500 to $5,000 per project, depending on scope and the client\\'s budget. Full-time \"AI-assisted developer,\" \"AI product builder,\" and \"founding engineer\" roles at startups reportedly range from $80K to $140K base depending on seniority and location, with equity on top. Indie hackers shipping revenue-generating vibe-coded apps have publicly reported product revenue from $1K to $20K per month, with outliers higher. <em>The ceiling scales with what you build, not what you know. Salaried roles cap your upside; products do not.</em> Treat these as ballparks — no single survey aggregates them yet, and the market is moving monthly.',\n },\n {\n id: 'q5-5', num: '5.5',\n question: 'Is vibe coding a real job?',\n answer: '<strong>It depends on which job market you are targeting.</strong> In the startup and indie developer ecosystem — yes, building with AI is a marketable practice with paying roles and growing demand. In regulated industries (healthcare, finance, defense) and large enterprise environments — not yet as a standalone role, because compliance and code-audit requirements still demand engineers who can read every line. The trajectory is clearly toward normalization: AI reportedly writes about 41% of all new code today, 84% of developers are using or planning to use AI tools, and 25% of YC\\'s current cohort runs nearly-all-AI-generated codebases. <em>The current state is early but real. The forward curve points toward \"yes\" being the default answer within two to three years.</em>',\n },\n ],\n },\n {\n id: 's6',\n num: '06',\n title: 'The Future of',\n titleItalic: 'Coding.',\n deck: 'Whether AI will replace coders, what the realistic 2026–2040 arc looks like, and the one skill that becomes more valuable as AI writes more code.',\n callout: {\n eyebrow: 'Key take',\n heading: 'AI replaces code-writing. It doesn\\'t replace problem-solving.',\n body: 'The developers who will struggle are those whose value is in typing code fast. <strong>The developers who will thrive are those whose value is in knowing what to build, how systems should behave, and when AI output is wrong.</strong> Vibe coding is the fastest way to find out which one you are.',\n },\n cards: [\n {\n id: 'q6-1', num: '6.1',\n question: 'Will coders be replaced by AI?',\n answer: '<strong>Rote code-writing is already being replaced.</strong> AI reportedly writes about 41% of all code today, and 84% of developers use or plan to use AI coding tools. The role-level effect is more specific than \"coders are obsolete\": developers who specialize in syntax and straightforward implementation are most exposed; developers who specialize in architecture, system design, and problem framing are least exposed. The practice of writing code is being automated. The judgment of what to build, why to build it, and how to know whether it works is not — and there is no current evidence that it will be soon. <em>\"Coder\" is becoming a smaller part of \"software developer.\" The other parts are growing.</em>',\n },\n {\n id: 'q6-2', num: '6.2',\n question: 'Will AI replace coders by 2040?',\n answer: '<strong>By 2040, AI will likely generate the majority of code by volume.</strong> What survives — and what the labor market will pay for — is the meta-skill: defining problems precisely, evaluating AI output critically, and directing systems toward intended outcomes. The vibe coder of 2026 who develops those meta-skills is better positioned than the traditional developer who ignores them. The career risk over the next 15 years is not AI itself. It is the choice not to adapt to AI. The 25% YC cohort statistic is the leading indicator: the fastest-growing companies are already operating at the model that the rest of the market will reach. Plan accordingly.',\n },\n {\n id: 'q6-3', num: '6.3',\n question: 'What skill becomes most valuable as AI writes more code?',\n answer: '<strong>Prompt precision.</strong> The ability to describe complex system behavior clearly enough that AI produces the right output on the first try is the emerging premium skill. Closely paired with it: the ability to audit AI output for correctness, security, and architectural soundness — not by reading every line, but by knowing which questions to ask and which automated checks to run. Both are learnable by non-developers. Neither requires fluency in any programming language. <em>The developers who treat prompting as engineering — versioned, specified, tested — are already pulling ahead of the developers who treat it as a chat.</em> That gap will widen.',\n },\n {\n id: 'q6-4', num: '6.4',\n question: 'What does a vibe-coded app need to work in the real world?',\n answer: '<strong>Three things — and the middle one is where most vibe coders stall.</strong> First, a deployment target: Vercel, Replit, Railway, Fly.io. Second, real data access — every useful live tool eventually needs to read from the web, and writing custom scrapers, handling JavaScript-rendered pages, and maintaining selectors as sites change is the wall most prototypes hit. Third, a feedback loop with actual users. The data access layer is the most underestimated step in the entire vibe coding workflow. MCP Scraper is the data layer vibe coders reach for when the prototype needs to start consuming real-world inputs — SERP results, People Also Ask trees, page extraction, YouTube transcripts — without writing and maintaining the scraping code themselves.',\n },\n ],\n },\n ],\n },\n {\n slug: 'people-also-ask-seo',\n title: 'People Also Ask SEO: The Complete 2026 Guide',\n titleDisplay: 'People Also Ask',\n titleDisplayItalic: 'SEO.',\n description: 'What PAA boxes are, how Google generates them, which tools harvest them at scale, and why the manual two-step loop breaks the moment you need programmatic data.',\n publishedAt: '2026-05-24',\n author: 'MCP Scraper',\n authorInitials: 'M',\n tags: ['SEO', 'people also ask', 'content strategy', 'PAA', 'keyword research'],\n category: 'SEO',\n badge: '31 questions answered',\n fieldGuideLabel: 'Field guide',\n deck: 'PAA strategy ends where programmatic begins. Every competitor guide shows you how to expand boxes — none shows you how to harvest them at scale via API and pipe the data directly into your content system.',\n readTimeMinutes: 12,\n stats: [\n { value: '85%', label: 'of Google searches show PAA boxes' },\n { value: '13.6%', label: 'CTR for purchase-intent PAA clicks' },\n { value: '31', label: 'questions answered in this guide' },\n { value: '90%', label: 'of AI Overviews include PAA boxes' },\n ],\n ctaHeading: 'Stop copying questions from a SERP accordion.',\n ctaHeadingItalic: 'MCP Scraper delivers structured PAA trees via API — one call, any keyword, directly into your pipeline.',\n ctaBody: 'MCP Scraper is the PAA harvesting API for developers and SEOs who need more than 4 visible questions. Extract hundreds of People Also Ask questions from any query via REST or MCP.',\n sections: [\n {\n id: 's1',\n num: '01',\n title: 'What Is People',\n titleItalic: 'Also Ask?',\n deck: 'PAA mechanics, Google behavior, and what the AI-generation shift means for the feature — the foundational questions most guides leave unanswered.',\n callout: {\n eyebrow: 'Key take',\n heading: 'PAA is not a content placement. It is a real-time map of query intent.',\n body: 'Google surfaces PAA on roughly 85% of searches. Winning a placement is useful. Understanding why Google shows the questions it does — and how 12.6% of answers are now AI-generated — is what separates a tactic from a strategy. <strong>Optimize the placement. Understand the system.</strong>',\n },\n cards: [\n {\n id: 'q1-1', num: '1.1',\n question: 'What do people also ask in SEO?',\n answer: '<strong>People Also Ask (PAA) is a dynamic SERP accordion Google introduced in 2016</strong> that surfaces related questions predicted to follow a searcher\\'s original query. It now appears on roughly <strong>85% of searches</strong> — not a niche placement opportunity but a near-universal feature that functions as Google\\'s real-time map of query intent for every topic. Early testing began in April 2015 as \"Related questions\" before the official July 2016 naming. For SEOs, PAA is two things simultaneously: a visibility placement to win, and a research signal showing which follow-up questions Google believes matter most to your searchers.',\n },\n {\n id: 'q1-2', num: '1.2',\n question: 'How does Google generate people also ask?',\n answer: 'Google generates PAA algorithmically from <strong>co-occurrence patterns in search sessions</strong> — which queries follow which, and what content satisfies them. One significant recent shift: <strong>12.6% of PAA answers are now AI-generated by Google itself</strong>, not pulled from any web page. This means winning a PAA placement no longer guarantees your content is the displayed source. The answer Google shows may be synthesized entirely from its own models, with your page as a citation at best. Understanding this shift changes the strategic goal from \"win the placement\" to \"be the authoritative source Google trusts when it generates the answer.\"',\n },\n {\n id: 'q1-3', num: '1.3',\n question: 'What does it mean when Google says people also searched for?',\n answer: '<strong>\"People also searched for\" is a distinct SERP feature</strong> — it appears after a user clicks a result and returns to the search page, surfacing refinement queries based on what searchers do next. <strong>PAA appears before the click</strong>, predicting follow-up intent from the query itself. They signal different moments in the search session and target different optimization strategies. \"People also searched for\" is a post-click refinement signal; PAA is a pre-click intent prediction. Conflating the two leads to misaligned content strategy — targeting refinement queries when your page needs to answer the predicted follow-ups, or vice versa.',\n },\n {\n id: 'q1-4', num: '1.4',\n question: 'How do Google people also ask work?',\n answer: '<strong>PAA boxes are infinite-scroll</strong>: expanding one question loads 2–4 additional questions, allowing Google to map an entire topic graph from a single seed query. The block appears after the first organic result in more than <strong>58% of SERPs</strong>, making it a top-three SERP element in roughly two-thirds of all searches. Featured answers average <strong>40–50 words</strong>. The infinite-scroll mechanism is the most operationally important detail for SEOs: a single seed keyword can branch into hundreds of related questions, which is why harvesting PAA programmatically returns a fundamentally different volume of data than manually expanding a visible accordion.',\n },\n {\n id: 'q1-5', num: '1.5',\n question: 'How to find people also ask?',\n answer: 'Three methods, in order of scale: <strong>manual SERP expansion</strong> — open Google, type your keyword, click the accordion arrows (free, works at 1–5 keywords); <strong>UI tools like AlsoAsked</strong>, which automate question collection and return visual question trees ($12–$47/mo, works at 5–1,000 keywords); and <strong>programmatic API access via MCP Scraper</strong>, which returns structured PAA data for any keyword without a browser (works at any scale). The right method depends entirely on how many keywords you need to cover. The manual path is not inferior — it is simply volume-limited, and that limit arrives faster than most SEOs expect.',\n },\n {\n id: 'q1-6', num: '1.6',\n question: 'What type of questions can Google not answer?',\n answer: 'PAA avoids four structural categories: <strong>highly time-sensitive breaking news</strong> (where no authoritative answer has stabilized), <strong>deeply personal queries</strong> (medical, legal, financial specifics tied to individual circumstance), <strong>normative controversies</strong> where no consensus source exists, and <strong>queries where the intent is too ambiguous</strong> for any single question formulation to make sense. Understanding these boundaries helps SEOs identify where PAA placements are structurally off the table — not due to competition, but due to feature design. If your topic falls into one of these categories, optimize for other SERP features rather than PAA.',\n },\n ],\n interactiveHtml: `<div class=\"si si-calculator\"><span class=\"si-heading\">How many PAA questions does one keyword unlock?</span><div class=\"si-calc-inputs\"><label class=\"si-label\">Seed keywords<div class=\"si-slider-row\"><input type=\"range\" class=\"si-range\" id=\"sip1-s\" min=\"1\" max=\"50\" value=\"3\"><span class=\"si-range-val\" id=\"sip1-sv\">3</span></div></label><label class=\"si-label\">Expansion depth (click levels)<div class=\"si-slider-row\"><input type=\"range\" class=\"si-range\" id=\"sip1-d\" min=\"1\" max=\"3\" value=\"2\"><span class=\"si-range-val\" id=\"sip1-dv\">2</span></div></label></div><div class=\"si-calc-output\"><div class=\"si-stat\"><span class=\"si-stat-num\" id=\"sip1-m\">12</span><span class=\"si-stat-label\">questions visible manually</span></div><div class=\"si-stat si-stat-accent\"><span class=\"si-stat-num\" id=\"sip1-f\">252</span><span class=\"si-stat-label\">questions in the full PAA tree</span></div></div><script>(function(){var s=document.getElementById('sip1-s'),d=document.getElementById('sip1-d'),sv=document.getElementById('sip1-sv'),dv=document.getElementById('sip1-dv'),mo=document.getElementById('sip1-m'),fo=document.getElementById('sip1-f');function upd(){var ss=+s.value,dd=+d.value;sv.textContent=ss;dv.textContent=dd;mo.textContent=ss*4;fo.textContent=ss*(dd===1?28:dd===2?84:252);}s.addEventListener('input',upd);d.addEventListener('input',upd);})()</script></div>`,\n },\n {\n id: 's2',\n num: '02',\n title: 'PAA as an',\n titleItalic: 'SEO Strategy',\n deck: 'Intent mechanics, keyword discovery, and why PAA is a top-5 SEO strategy because of AI Overviews — not despite them.',\n callout: {\n eyebrow: 'The number that changes the strategy',\n heading: 'Purchase-intent PAA queries drive 13.6% interaction rates. Overall PAA: 3%.',\n body: 'The 4× gap is not a curiosity — it is the entire prioritization framework. <strong>Which questions are worth targeting first is a data problem, and the data problem requires harvesting tools, not intuition.</strong>',\n },\n cards: [\n {\n id: 'q2-1', num: '2.1',\n question: 'What are the 4 types of intent in SEO?',\n answer: 'The four intent types — <strong>informational, navigational, commercial, transactional</strong> — are not equally valuable in PAA strategy. Purchase-intent queries drive a <strong>13.6% PAA interaction rate</strong>, versus 3% for searches overall. That 4× gap means intent classification is not just a content exercise — it is the highest-leverage variable in deciding which PAA questions are worth targeting first. Most SEO teams apply intent classification to keyword strategy but never extend it to PAA prioritization. Applying it there is the arbitrage: harvest PAA for purchase-intent and commercial queries first, and the ROI per question answered separates immediately from the pack.',\n },\n {\n id: 'q2-2', num: '2.2',\n question: 'What are the 3 C\\'s of search intent?',\n answer: 'Content type, content format, and content angle — the <strong>3 C\\'s</strong> — determine structural eligibility for a PAA placement before Google evaluates topical relevance. <strong>Format matters most</strong>: PAA answers average 40–50 words, which means a 2,000-word section cannot win a placement regardless of its quality. PAA requires an extractable, self-contained answer at the right word count. The content angle determines whether your framing matches the specific question Google is showing — a page that answers the general topic but not the exact question in the accordion is not eligible. The 3 C\\'s applied to PAA become a pre-qualification checklist, not an afterthought.',\n },\n {\n id: 'q2-3', num: '2.3',\n question: 'What is the 80/20 rule in SEO?',\n answer: 'Applied to PAA, the 80/20 rule holds in the data: the <strong>13.6% interaction rate for purchase-intent queries versus 3% overall</strong> suggests that a small subset of PAA questions drives disproportionate engagement and commercial value. Identifying that subset — by intent type and query category — is precisely what PAA harvesting tools solve, because the questions that matter most are rarely obvious from the seed keyword alone. A human reviewing a SERP accordion sees 4 questions. A programmatic harvest of the full PAA tree for that seed can surface 200. The 80/20 rule applies to that full dataset, not to the visible 4.',\n },\n {\n id: 'q2-4', num: '2.4',\n question: 'What are the best SEO tools?',\n answer: 'For PAA strategy specifically, a complete stack looks like: <strong>Google Search Console</strong> (free, shows existing PAA appearances for your domain), <strong>AlsoAsked</strong> (UI-based question trees, $12–$47/mo, best for manual research up to 1,000 seeds), <strong>Semrush</strong> (broad SERP feature tracking), and <strong>MCP Scraper</strong> (programmatic PAA API for pipeline integration). The right combination depends on whether your workflow is UI-based or data-pipeline-based. Teams doing content at scale need both tiers — the UI tool for ad-hoc research and the API for systematic collection. The two are complementary, not competing.',\n },\n {\n id: 'q2-5', num: '2.5',\n question: 'Which is the best free SEO tool for beginners?',\n answer: 'For PAA research specifically, <strong>Google Search Console</strong> is the strongest free starting point — it shows which PAA features your site already appears in, at no cost. <strong>AlsoAsked offers free monthly credits</strong> without requiring a registered account. MCP Scraper is not the beginner recommendation; it requires API literacy and suits developers moving into structured data workflows, not first-time SEOs. The honest path for a beginner: start with Search Console to see what you already have, use AlsoAsked free credits to map the questions you\\'re missing, and add the API layer when manual research becomes the rate-limiting step in your workflow.',\n },\n {\n id: 'q2-6', num: '2.6',\n question: 'What are the top 5 SEO strategies?',\n answer: 'PAA optimization earns a top-5 slot not because its direct interaction rate is high (3% overall) but because of its relationship with AI Overviews: <strong>PAA co-appears with AI Overviews in 90% of cases</strong>. Winning a PAA placement now doubles as qualifying content to be sourced by AI Overviews — a compound visibility return that traditional link-building cannot replicate. Optimizing for PAA is, in practice, optimizing for AI Overview sourcing eligibility. The 90% co-appearance figure means these two features share the same content signal. Teams that ignore PAA are leaving the most reliable AI Overview proxy on the table.',\n },\n ],\n interactiveHtml: `<div class=\"si si-decision\"><span class=\"si-heading\">Is your PAA strategy leaving data on the table?</span><div class=\"si-questions\"><div class=\"si-q\"><p class=\"si-q-text\">Do you prioritize PAA questions by intent type — targeting purchase-intent questions before informational ones?</p><div class=\"si-q-opts\"><label class=\"si-opt\"><input type=\"radio\" name=\"sip2-q0\" value=\"1\"> Yes — intent drives my question selection</label><label class=\"si-opt\"><input type=\"radio\" name=\"sip2-q0\" value=\"0\"> No — I target all PAA questions equally</label></div></div><div class=\"si-q\"><p class=\"si-q-text\">Do you refresh PAA-targeted content at least once per quarter?</p><div class=\"si-q-opts\"><label class=\"si-opt\"><input type=\"radio\" name=\"sip2-q1\" value=\"1\"> Yes — freshness is part of my process</label><label class=\"si-opt\"><input type=\"radio\" name=\"sip2-q1\" value=\"0\"> No — I optimize once and move on</label></div></div><div class=\"si-q\"><p class=\"si-q-text\">Is your target keyword list larger than 50 keywords?</p><div class=\"si-q-opts\"><label class=\"si-opt\"><input type=\"radio\" name=\"sip2-q2\" value=\"1\"> Yes — 50 or more keywords</label><label class=\"si-opt\"><input type=\"radio\" name=\"sip2-q2\" value=\"0\"> No — fewer than 50</label></div></div></div><div class=\"si-result\" id=\"sip2-result\" hidden><p class=\"si-result-label\" id=\"sip2-rl\"></p><p class=\"si-result-body\" id=\"sip2-rb\"></p></div><script>(function(){var results=[{label:'Manual research fits your current scale.',body:'Your keyword list is manageable and your process is structured. No tool changes needed yet — the manual path handles your volume.'},{label:'You are at the edge of what manual research supports.',body:'One gap in intent prioritization or freshness cadence is limiting your results. Focusing on purchase-intent PAA first is the highest-ROI next step.'},{label:'Your PAA strategy needs a data layer.',body:'At 50+ keywords with systematic intent filtering and quarterly freshness requirements, programmatic access is no longer optional — manual research is your rate-limiting step.'}];function check(){var score=0,answered=0;for(var i=0;i<3;i++){var r=document.querySelector('input[name=\"sip2-q'+i+'\"]:checked');if(r){score+=+r.value;answered++;}}if(answered<3)return;var res=score>=2?results[2]:score===1?results[1]:results[0];var el=document.getElementById('sip2-result');document.getElementById('sip2-rl').textContent=res.label;document.getElementById('sip2-rb').textContent=res.body;el.hidden=false;}['sip2-q0','sip2-q1','sip2-q2'].forEach(function(n){document.querySelectorAll('input[name=\"'+n+'\"]').forEach(function(r){r.addEventListener('change',check);});});})()</script></div>`,\n },\n {\n id: 's3',\n num: '03',\n title: 'AI and the',\n titleItalic: 'Future of PAA',\n deck: 'Is SEO dead? Will AI replace it? Committed answers backed by data — including why AI Overviews make PAA more important, not less.',\n callout: {\n eyebrow: 'Counter-intuitive finding',\n heading: 'AI Overviews make PAA more important, not less.',\n body: 'PAA co-appears with AI Overviews in 90% of searches. Winning a PAA placement is currently the most reliable proxy for AI Overview sourcing eligibility. <strong>The teams abandoning PAA because of AI are removing themselves from the exact feature that signals AI citation readiness.</strong>',\n },\n cards: [\n {\n id: 'q3-1', num: '3.1',\n question: 'Is SEO dead or evolving in 2026?',\n answer: '<strong>Structurally shifting, not dying</strong> — but the shift is significant. 58.5% of US Google searches already end without a click. AI Overviews reduce position-1 CTR by up to 58%, and searches triggering AI Overviews show an 83% zero-click rate. <strong>PAA co-appears with AI Overviews in 90% of cases.</strong> The game has moved from generating clicks to being cited as an answer source — visibility without click-through is the new baseline. The SEOs who treat this as a crisis are measuring the wrong thing. The SEOs who treat it as a repositioning opportunity are already optimizing for sourcing frequency, not position rank.',\n },\n {\n id: 'q3-2', num: '3.2',\n question: 'Will SEO be replaced by AI?',\n answer: '<strong>AI is not replacing SEO</strong> — it is automating the parts that were manual and amplifying the parts that require data access. The practitioners who win are those using AI for content generation while feeding it with live, structured SEO data: PAA trees, SERP features, intent signals. The ones who lose are optimizing one page at a time while their competitors run data pipelines that update daily. The replacement narrative confuses the tool with the discipline. SEO as structured analysis of how content earns visibility in search systems is not going anywhere. The execution layer is being automated. The strategic layer is becoming more valuable, not less.',\n },\n {\n id: 'q3-3', num: '3.3',\n question: 'What is SEO being replaced by?',\n answer: 'The emerging term is <strong>answer-engine optimization (AEO)</strong> — structuring content so AI systems cite it as a source, not just rank it in blue links. PAA boxes are currently the most reliable signal for what questions AI Overviews will answer from a given domain, because the two features co-appear in <strong>90% of cases</strong>. Winning PAA now is training data sourcing practice for the AI-first SERP. AEO is not a replacement for SEO — it is an extension of it toward the citation layer. The content signals that earn PAA placements and the signals that earn AI Overview citations overlap significantly. PAA optimization is AEO in its most accessible form.',\n },\n {\n id: 'q3-4', num: '3.4',\n question: 'Can ChatGPT write SEO articles?',\n answer: '<strong>ChatGPT can draft content but cannot perform PAA research</strong> — it has no live SERP access, returns no real-time question clusters, and cannot tell you which questions Google is surfacing for your keyword today. PAA data is a live signal that requires querying the SERP, not a language model. Any AI-assisted SEO workflow that skips a live data layer is building content strategy on stale assumptions. The correct architecture is: live PAA data (from a harvesting API) feeding structured inputs to the LLM, with the LLM handling drafting and the data layer handling research. Skipping the data layer produces well-written answers to questions no one is actually asking.',\n },\n {\n id: 'q3-5', num: '3.5',\n question: 'Can ChatGPT do an SEO audit?',\n answer: '<strong>ChatGPT can review content structure</strong>, flag missing headers, and suggest improvements to on-page copy — but it cannot audit live SERP features, check which PAA questions your competitors currently hold, or identify PAA placement gaps across your keyword set. Those tasks require real-time structured data pulled from the SERP itself, not pattern-matching against training data. The distinction is not about writing quality — it is about data access as a category. LLM-based audits are useful for qualitative content review. They are structurally incapable of competitive PAA analysis, which requires live harvesting at query time.',\n },\n {\n id: 'q3-6', num: '3.6',\n question: 'Which AI is best for SEO?',\n answer: '<strong>No single AI model is \"best for SEO\"</strong> — the question frames it wrong. The winning configuration is: a live data API (PAA harvesting, SERP feature extraction) feeding structured inputs to an LLM for content generation and optimization. The AI model handles language; the data layer handles reality. MCP Scraper occupies the data layer slot — it supplies the question data that makes content decisions defensible rather than intuitive. Conflating the two is why AI-assisted SEO often produces content that reads well but targets the wrong questions. The model choice matters far less than whether your workflow has a live data layer at all.',\n },\n ],\n interactiveHtml: `<div class=\"si si-quiz\"><span class=\"si-heading\">Quick check</span><div class=\"si-quiz-q\" data-correct=\"1\"><p class=\"si-quiz-q-text\">PAA co-appears with AI Overviews in what percentage of searches?</p><div class=\"si-quiz-opts\"><button class=\"si-quiz-opt\" data-idx=\"0\">23% of searches</button><button class=\"si-quiz-opt\" data-idx=\"1\">90% of searches</button><button class=\"si-quiz-opt\" data-idx=\"2\">58% of searches</button></div><p class=\"si-quiz-explanation\" hidden>The 90% co-appearance rate is the number that changes everything — winning a PAA placement is currently the most reliable proxy for AI Overview sourcing eligibility. Teams abandoning PAA because of AI are removing themselves from the exact signal that governs AI citations.</p></div><script>(function(){document.querySelectorAll('.si-quiz .si-quiz-q').forEach(function(qBlock){var correct=+qBlock.dataset.correct;var explanation=qBlock.querySelector('.si-quiz-explanation');qBlock.querySelectorAll('.si-quiz-opt').forEach(function(btn){btn.addEventListener('click',function(){if(qBlock.dataset.answered)return;qBlock.dataset.answered='1';var idx=+btn.dataset.idx;qBlock.querySelectorAll('.si-quiz-opt').forEach(function(b,i){b.disabled=true;if(i===correct)b.classList.add('si-quiz-correct');else if(i===idx&&idx!==correct)b.classList.add('si-quiz-wrong');});explanation.hidden=false;});});});})()</script></div>`,\n },\n {\n id: 's4',\n num: '04',\n title: 'PAA Tools',\n titleItalic: 'Compared',\n deck: 'AlsoAsked vs. Semrush vs. MCP Scraper — honest tradeoffs, including where MCP Scraper is and is not the right choice.',\n cards: [\n {\n id: 'q4-1', num: '4.1',\n question: 'How does AlsoAsked work?',\n answer: '<strong>AlsoAsked crawls Google PAA boxes for a given keyword</strong> and builds a branching tree of related questions, which it presents as a visual map, PNG export, or CSV. Bulk upload processes up to 1,000 seed terms in a single job, returning a large question set from recursive PAA expansion. It includes multi-region and multi-language support, API access, and webhook integration across all paid tiers. AlsoAsked is best positioned for UI-based workflows where a researcher is manually reviewing and selecting questions. The limit of the approach is pipeline integration: CSV exports require a human in the loop, and the API, while available on all paid tiers, is designed around the same single-query model as the UI.',\n },\n {\n id: 'q4-2', num: '4.2',\n question: 'Is AlsoAsked free?',\n answer: '<strong>AlsoAsked offers free monthly credits</strong> for non-registered users, with no credit card required. Paid plans start at $12/mo (Basic, 100 credits) and go to $47/mo (Pro, 1,000 credits); the $23/mo Lite tier (300 credits) is listed as most popular. Annual billing saves 20%. <strong>All paid tiers — including Basic — include API access</strong>, so the API is not gated behind a premium plan. The free tier is genuinely useful for occasional PAA research. The paid tiers are priced for practitioners doing regular question harvesting. The $12 Basic plan is a legitimate entry point for SEOs who need more than the free credits allow but are not yet at pipeline scale.',\n },\n {\n id: 'q4-3', num: '4.3',\n question: 'What are the 4 pillars of SEO?',\n answer: 'Technical, on-page, off-page, and content — the <strong>traditional four pillars</strong> — are all affected by PAA strategy. But the content pillar increasingly depends on the technical pillar for data access: a content team that cannot harvest PAA programmatically is relying on manual research that caps out at dozens of keywords. The teams closing content at scale have connected their data layer directly to their publishing pipeline. PAA strategy now bridges two pillars simultaneously. The content question (\"which questions should we answer?\") is answered by the technical infrastructure (\"what does the PAA API return for this seed keyword?\"). That bridge is the competitive gap most content teams have not crossed.',\n },\n {\n id: 'q4-4', num: '4.4',\n question: 'Can a beginner do SEO?',\n answer: 'A beginner can start PAA research immediately — <strong>free tier on AlsoAsked</strong> (no account required), Google Search Console for existing rankings, and manual SERP expansion for small keyword sets. The step-change to programmatic PAA requires basic API literacy, not advanced SEO expertise. <strong>Developers entering content teams are often better positioned</strong> for the API path than experienced SEOs who have never worked with structured data outputs. The entry barrier is not SEO knowledge — it is familiarity with REST APIs and JSON. A developer who has never done SEO can integrate the MCP Scraper API faster than an experienced SEO who has never touched an API endpoint.',\n },\n {\n id: 'q4-5', num: '4.5',\n question: 'Can I do SEO by myself?',\n answer: 'Solo SEOs can run effective PAA strategy — the <strong>manual workflow handles 1–5 target keywords well</strong>. The friction hits at roughly 50 keywords: at that volume, manually expanding PAA trees and logging questions becomes the rate-limiting step, not the content writing. The programmatic API path is the unlock at scale, not a requirement for getting started. The honest threshold: if your keyword list fits on one spreadsheet page and you update it quarterly, manual PAA research is sufficient. If your keyword list is dynamic, multi-locale, or feeds an automated content system, the API path pays for itself in the first week of research time saved.',\n },\n ],\n interactiveHtml: `<div class=\"si si-comparison\"><span class=\"si-heading\">Pick a tool to compare</span><div class=\"si-tabs\" role=\"tablist\"><button class=\"si-tab si-tab-active\" role=\"tab\" aria-selected=\"true\" data-panel=\"sip4-p0\">AlsoAsked</button><button class=\"si-tab\" role=\"tab\" aria-selected=\"false\" data-panel=\"sip4-p1\">Semrush</button><button class=\"si-tab\" role=\"tab\" aria-selected=\"false\" data-panel=\"sip4-p2\">MCP Scraper</button></div><div class=\"si-panels\"><div class=\"si-panel\" id=\"sip4-p0\" role=\"tabpanel\"><p class=\"si-panel-title\">AlsoAsked — UI-based PAA question trees</p><ul class=\"si-panel-pros\"><li>Visual question tree with PNG and CSV export</li><li>Free tier with no account required</li><li>Bulk upload up to 1,000 seeds, multi-region support</li></ul><ul class=\"si-panel-cons\"><li>CSV export requires human review in the loop</li><li>API mirrors the single-query UI model — not designed for pipeline volume</li></ul><p class=\"si-panel-verdict\">Best for UI-based research at up to 1,000 seeds per month. The right tool when a researcher is manually reviewing and selecting questions.</p></div><div class=\"si-panel\" id=\"sip4-p1\" role=\"tabpanel\" hidden><p class=\"si-panel-title\">Semrush — Full SEO suite with PAA tracking</p><ul class=\"si-panel-pros\"><li>PAA alongside rankings, backlinks, and site audit in one platform</li><li>Historical SERP feature data for trend analysis</li></ul><ul class=\"si-panel-cons\"><li>PAA is not its primary strength — depth limited vs. dedicated tools</li><li>Expensive if PAA is your only use case</li><li>No programmatic extraction of full PAA trees</li></ul><p class=\"si-panel-verdict\">Right if you already use Semrush for keyword research and want PAA visibility added. Overkill for PAA-only workflows.</p></div><div class=\"si-panel\" id=\"sip4-p2\" role=\"tabpanel\" hidden><p class=\"si-panel-title\">MCP Scraper — Web dashboard + API + MCP server</p><ul class=\"si-panel-pros\"><li>Dashboard at mcpscraper.dev: run PAA, SERP, Maps, YouTube, and Facebook Ads from one UI</li><li>Same data available via REST API and as MCP tools for Claude, Cursor, and Copilot</li><li>Full PAA tree per seed, results as cards or structured JSON/Markdown export</li></ul><ul class=\"si-panel-cons\"><li>Covers seven surfaces — more than you need if PAA is your only use case</li><li>Credit-based billing: each surface costs credits, not a flat subscription per tool</li></ul><p class=\"si-panel-verdict\">Right when you need PAA alongside SERP data, Maps intelligence, or competitive ad research — and when you want the same data accessible to both your team and your AI agents.</p></div></div><script>(function(){document.querySelectorAll('.si-comparison').forEach(function(comp){comp.querySelectorAll('.si-tab').forEach(function(tab){tab.addEventListener('click',function(){comp.querySelectorAll('.si-tab').forEach(function(t){t.classList.remove('si-tab-active');t.setAttribute('aria-selected','false');});comp.querySelectorAll('.si-panel').forEach(function(p){p.hidden=true;});tab.classList.add('si-tab-active');tab.setAttribute('aria-selected','true');document.getElementById(tab.dataset.panel).hidden=false;});});});})()</script></div>`,\n },\n {\n id: 's5',\n num: '05',\n title: 'Scaling PAA',\n titleItalic: 'Extraction',\n deck: 'The programmatic case — why the manual UI loop breaks at scale, and what a PAA data pipeline actually looks like.',\n callout: {\n eyebrow: 'The scale threshold',\n heading: 'PAA questions shift by location, device, language, and time. Scraping them once is not a strategy.',\n body: 'Google mines search sessions continuously. PAA is a live signal, not a static dataset. <strong>Recurring programmatic harvesting on a schedule is the correct workflow for any live content operation — not a one-time manual pull followed by a spreadsheet filed away.</strong>',\n },\n cards: [\n {\n id: 'q5-1', num: '5.1',\n question: 'What are the 5 important concepts of SEO?',\n answer: 'Applied to PAA at scale, five concepts that drive results: <strong>query intent mapping</strong> (which questions signal purchase-readiness), <strong>zero-volume question discovery</strong> (PAA surfaces questions that keyword tools miss entirely), <strong>PAA tree traversal</strong> (one seed keyword branches into hundreds of related questions), <strong>freshness signaling</strong> (content updated within 90 days appears 4.3× more frequently in PAA features), and <strong>structured data delivery</strong>. The last four require programmatic access. The freshness multiplier is the most underused lever in PAA strategy — most teams optimize the answer once and move on, missing the ongoing recency advantage that recurring updates deliver.',\n },\n {\n id: 'q5-2', num: '5.2',\n question: 'What are the 3 pillars of SEO?',\n answer: 'At programmatic scale, the three operational pillars become <strong>data acquisition, content production, and distribution</strong> — not the traditional crawlability, content, and authority. PAA harvesting via API sits at the data-acquisition layer, upstream of every content and publishing decision. MCP Scraper operates at that layer: it does not write content or build links, it supplies the question data that makes content decisions defensible rather than intuitive. The scope boundary is a trust signal: a tool that claims to do everything does nothing well. The data acquisition layer is the one most content teams have not built yet — and it is the layer that compounds.',\n },\n {\n id: 'q5-3', num: '5.3',\n question: 'What are the 3 C\\'s of SEO?',\n answer: 'Content, code, and credibility — but in a programmatic PAA workflow, <strong>\"content\" starts upstream with machine-readable question data</strong>, not a brainstorming session. The gap between \"which questions should we answer\" and \"draft created\" collapses when PAA API output feeds directly into a content brief template or LLM prompt. The manual research phase that typically takes days becomes a <strong>sub-second API call</strong>. The \"code\" pillar is what enables this — a REST endpoint that accepts a seed keyword and returns a structured PAA tree is not a luxury for large teams; it is the unlock that makes content operations at any scale less dependent on individual research time.',\n },\n {\n id: 'q5-4', num: '5.4',\n question: 'What is the Google 20% rule?',\n answer: 'Google\\'s 20% rule — the practice of giving engineers discretionary time for side projects — produced features including Gmail and Google Maps. <strong>PAA itself emerged from the same underlying logic</strong>: Google continuously mines search session data to predict follow-up intent. That mining is live and ongoing, which is why PAA questions shift by location, device, language, and time — and why scraping them once and filing the results is not a strategy. <em>Recurring harvesting on a schedule is.</em> The operational implication: build a workflow that pulls PAA data for your priority keyword set on a monthly or weekly cadence, and treat the outputs as a live editorial signal, not a one-time research deliverable.',\n },\n ],\n interactiveHtml: `<div class=\"si si-checklist\"><span class=\"si-heading\">PAA pipeline setup</span><div class=\"si-check-progress-row\"><div class=\"si-check-bar-wrap\"><div class=\"si-check-bar\" id=\"sip5-bar\" style=\"width:0%\"></div></div><span class=\"si-check-count\" id=\"sip5-count\">0 / 5 done</span></div><ul class=\"si-check-list\"><li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"sip5-chk\"> Define your seed keyword list — start with your top 50 pages by traffic</label></li><li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"sip5-chk\"> Choose your extraction method: manual for ≤10 keywords, API for 50+</label></li><li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"sip5-chk\"> Classify PAA output by intent type — prioritize purchase-intent questions first</label></li><li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"sip5-chk\"> Set a freshness schedule — monthly for competitive topics, quarterly for stable ones</label></li><li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"sip5-chk\"> Connect PAA output to your content brief template or LLM prompt</label></li></ul><script>(function(){var checks=document.querySelectorAll('.sip5-chk'),bar=document.getElementById('sip5-bar'),count=document.getElementById('sip5-count'),total=checks.length;function upd(){var done=document.querySelectorAll('.sip5-chk:checked').length;bar.style.width=(done/total*100)+'%';count.textContent=done+' / '+total+' done';}checks.forEach(function(c){c.addEventListener('change',upd);});})()</script></div>`,\n },\n {\n id: 's6',\n num: '06',\n title: 'Practical PAA',\n titleItalic: 'Tactics',\n deck: 'Quick wins, freshness mechanics, anti-bot realities, and why the programmatic path is where SEO income separates.',\n cards: [\n {\n id: 'q6-1', num: '6.1',\n question: 'What are basic SEO skills?',\n answer: 'The baseline PAA skill set is: <strong>reading query intent accurately</strong>, <strong>writing concise Q&A answers</strong> (40–50 words is the PAA sweet spot), <strong>implementing FAQ and HowTo schema markup</strong>, and <strong>maintaining content freshness</strong> — pages updated within 90 days appear 4.3× more frequently in PAA features than stale content. The advanced skill is API integration for teams transitioning to programmatic workflows. The basics above are achievable without any tooling beyond Google Search Console, and they compound: intent-matched 40-word answers with correct schema and a quarterly update cadence will outperform longer, less-structured content on PAA placements across nearly every category.',\n },\n {\n id: 'q6-2', num: '6.2',\n question: 'Is SEO a high income skill?',\n answer: 'PAA-driven SEO is splitting into two earning brackets. <strong>Practitioners who optimize content manually</strong> — identifying questions, formatting answers, checking rankings — are doing work that AI tools increasingly replicate, which compresses rates. <strong>Practitioners who build PAA data pipelines</strong> and integrate them into content systems are doing technical-strategic work that remains rare. The programmatic path is where income separates, not because it is harder to learn, but because few people have crossed the data-engineering threshold yet. The \"data-engineering threshold\" is lower than it sounds: REST API literacy, basic JSON handling, and familiarity with content pipeline tooling. The gap is not a skills gap — it is an awareness gap.',\n },\n {\n id: 'q6-3', num: '6.3',\n question: 'Why is Google checking if I\\'m human?',\n answer: 'CAPTCHA and anti-bot verification appear when collecting PAA data without proper infrastructure. <strong>Headless browsers sending high-frequency requests</strong> without realistic session behavior, residential proxy coverage, or rate limiting trigger Google\\'s bot-detection systems. MCP Scraper\\'s API handles the anti-bot layer on the infrastructure side — the caller passes a keyword and receives structured PAA output; the detection friction never reaches the application layer. This is one of the most underestimated friction points in programmatic PAA collection: teams that build their own scrapers spend disproportionate engineering time on detection evasion rather than on the content strategy that the data enables.',\n },\n {\n id: 'q6-4', num: '6.4',\n question: 'Am I being monitored by Google?',\n answer: 'Google monitors search behavior continuously to update PAA questions — <strong>session patterns, query sequences, click data, and location signals</strong> all feed the PAA algorithm in real time. This is why the same keyword returns different PAA questions depending on location, language, device, and time of day. It also explains why a static PAA dataset goes stale: the questions Google surfaces this week may differ meaningfully from last month\\'s harvest. <em>Recurring collection is the correct workflow for any live content operation.</em> The monitoring is a feature, not a surveillance concern — it means PAA is a continuously refreshed intent signal, and the teams harvesting it regularly have a permanently updated editorial dataset that teams relying on one-time research do not.',\n },\n ],\n interactiveHtml: `<div class=\"si si-codegen\"><span class=\"si-heading\">Generate your PAA API request</span><div class=\"si-codegen-inputs\"><label class=\"si-label\">Keyword<input class=\"si-text-input\" id=\"sip6-kw\" value=\"people also ask SEO\" placeholder=\"e.g. best CRM software\"></label><label class=\"si-label\">Max questions<input class=\"si-text-input\" id=\"sip6-mq\" value=\"50\" placeholder=\"50\" type=\"number\" min=\"1\" max=\"200\"></label></div><div class=\"si-codegen-output-wrap\"><pre class=\"si-codegen-pre\"><code id=\"sip6-out\"></code></pre><button class=\"si-copy-btn\" id=\"sip6-copy\">Copy curl</button></div><script>(function(){function getKw(){return document.getElementById('sip6-kw').value||'your keyword';}function getMq(){return+(document.getElementById('sip6-mq').value||50);}function generate(){return JSON.stringify({query:getKw(),maxQuestions:getMq()},null,2);}function upd(){document.getElementById('sip6-out').textContent=generate();}document.querySelectorAll('#sip6-kw,#sip6-mq').forEach(function(i){i.addEventListener('input',upd);});document.getElementById('sip6-copy').addEventListener('click',function(){var body=JSON.stringify({query:getKw(),maxQuestions:getMq()});var cmd=\"curl -X POST https://mcpscraper.dev/harvest/sync -H 'Content-Type: application/json' -H 'x-api-key: YOUR_API_KEY' -d '\"+body+\"'\";navigator.clipboard.writeText(cmd).then(function(){var btn=document.getElementById('sip6-copy');btn.textContent='Copied!';setTimeout(function(){btn.textContent='Copy curl';},1500);});});upd();})()</script></div>`,\n },\n ],\n },\n {\n slug: 'what-is-an-mcp',\n title: 'What Is an MCP? The Honest Developer\\'s Guide',\n titleDisplay: 'What Is',\n titleDisplayItalic: 'an MCP?',\n description: 'MCP explained for developers who need to decide — not just understand. Which platforms adopted it, when to skip it, and how it compares to REST, Zapier, and Copilot.',\n publishedAt: '2026-05-24',\n author: 'MCP Scraper',\n authorInitials: 'M',\n tags: ['MCP', 'model context protocol', 'AI tools', 'developer guide', 'Claude'],\n category: 'AI Development',\n badge: '30 questions answered',\n fieldGuideLabel: 'Field guide',\n deck: 'Every article about what is an MCP tells you it\\'s a universal plug-and-play connector for AI. This one tells you which platforms actually adopted it, when to skip it entirely, and why the USB-C analogy hides the decision your architecture actually requires.',\n readTimeMinutes: 11,\n stats: [\n { value: '6', label: 'sections' },\n { value: '30', label: 'questions answered' },\n { value: '97M', label: 'monthly MCP SDK downloads' },\n { value: '0', label: 'fluff' },\n ],\n ctaHeading: 'Build for the agent era.',\n ctaHeadingItalic: 'MCP Scraper is an MCP-native tool — extract PAA data the way AI agents actually work.',\n ctaBody: 'MCP Scraper gives AI agents the web data they need — PAA questions, SERP results, and page content via REST or MCP. Built for the agent-native stack.',\n sections: [\n {\n id: 's1',\n num: '01',\n title: 'MCP',\n titleItalic: 'Defined',\n deck: 'What MCP actually is, what it does, and the architectural distinction every explainer skips.',\n callout: {\n eyebrow: 'Quick take',\n heading: 'MCP is not USB-C.',\n body: 'USB-C is a hardware connector. MCP is an architectural decision — a protocol that determines whether your AI agent can discover and call tools at runtime or must be hardcoded by a developer. <strong>The analogy hides the trade-off. This section names it.</strong>',\n },\n cards: [\n {\n id: 'q1-1', num: '1.1',\n question: 'What is an MCP in AI?',\n answer: 'MCP — <strong>Model Context Protocol</strong> — is an open standard launched by Anthropic on November 25, 2024, that gives AI agents a single, standardized interface to connect with external tools, data sources, and workflows. Before MCP, every AI model and every external tool required a custom integration written by a developer. MCP eliminates that custom work by defining a shared communication contract that any agent and any tool can implement once and then use interchangeably. <em>An agent calling GitHub, Postgres, and Slack</em> no longer needs three bespoke connectors — it needs one MCP client. The practical implication most explainers miss: MCP is not primarily a developer convenience — it\\'s what makes AI agents autonomous enough to run without a human in the loop on every tool call.',\n },\n {\n id: 'q1-2', num: '1.2',\n question: 'What does the MCP do?',\n answer: 'MCP enables AI agents to <strong>discover, invoke, and receive results from external tools at runtime</strong> — without a developer pre-configuring every possible tool call. The mechanism is a standard called <code>tools/list</code>: an agent sends this request to any MCP server and gets back a live manifest of every capability that server exposes, including names, descriptions, and input schemas. The agent then selects the right tool, calls it, and receives structured output — all without leaving its session. <em>Connecting an agent to a CRM, a code repo, and a web scraper</em> used to mean three separate integrations; MCP makes them all callable through the same protocol. The deeper point: MCP doesn\\'t just save integration work — it makes tool use something the agent decides at runtime rather than something a developer hardcodes at build time.',\n },\n {\n id: 'q1-3', num: '1.3',\n question: 'What is MCP in simple terms?',\n answer: 'MCP is a <strong>shared language that any AI agent and any tool can both speak</strong>. Once a tool publishes an MCP server, every MCP-compatible agent — Claude, ChatGPT, Cursor, GitHub Copilot — can call it without a custom connector. The analogy that actually holds: imagine if every REST API automatically understood every other API\\'s auth, parameter format, and response schema without any glue code. That\\'s what MCP does for AI agents. <em>A Postgres database, a Slack workspace, and a web data service</em> all become callable through the same interface once they expose an MCP server. The distinction that matters in practice: \"simple\" doesn\\'t mean effortless — the tool still has to implement the MCP server; MCP just ensures that implementation only has to happen once.',\n },\n {\n id: 'q1-4', num: '1.4',\n question: 'Is MCP a tool or framework?',\n answer: 'MCP is neither — it\\'s a <strong>protocol</strong>, which means a specification for how two systems communicate, not software you install or a framework you build on top of. Tools implement MCP (a Postgres connector that speaks MCP is a tool). Frameworks integrate MCP clients (LangChain, AutoGen). MCP itself is the rulebook those implementations follow — specifically, a JSON-RPC 2.0 message format plus a defined session lifecycle. IBM\\'s documentation makes this distinction explicitly: MCP is not an agent framework. The practical consequence: asking \"which MCP should I choose?\" is like asking \"which HTTP should I use?\" — the protocol is the same; your choice is which server or client library implements it.',\n },\n {\n id: 'q1-5', num: '1.5',\n question: 'What problems does MCP solve?',\n answer: 'MCP solves the <strong>N×M integration problem</strong>: without it, connecting N AI models to M external tools produces N×M custom integrations, each built and maintained separately. MCP collapses this to M+N — implement the protocol once on each side and every combination works. The secondary problems it solves flow from the same root: inconsistent auth patterns across integrations, brittle hardcoded tool calls that break when APIs change, and the inability for AI agents to discover new tools at runtime. <em>An enterprise with 10 AI models and 50 internal tools</em> faces 500 custom integrations without MCP and 60 protocol implementations with it. What no explainer says plainly: MCP doesn\\'t reduce the number of tools you build — it reduces the number of connectors that break when either side changes.',\n },\n {\n id: 'q1-6', num: '1.6',\n question: 'What is Anthropic?',\n answer: 'Anthropic is the AI safety company that created Claude and launched MCP on November 25, 2024. <strong>In December 2025, Anthropic donated MCP governance to the Agentic AI Foundation under the Linux Foundation</strong> — a move that made MCP formally vendor-neutral and accelerated adoption by removing the perception that the protocol was an Anthropic-controlled standard. Anthropic was founded in 2021 by former OpenAI researchers, including Dario Amodei and Daniela Amodei. Its two primary contributions to AI infrastructure are Claude (a family of large language models) and MCP (the protocol this post covers). The governance transfer is the detail most explainers skip: Anthropic no longer controls MCP — the Linux Foundation body does, which is why OpenAI, Microsoft, and Google were willing to adopt it.',\n },\n ],\n },\n {\n id: 's2',\n num: '02',\n title: 'MCP vs',\n titleItalic: 'Everything',\n deck: 'MCP vs REST, HTTP, Zapier, Copilot, and LLMs — the comparisons that actually determine whether MCP belongs in your stack.',\n callout: {\n eyebrow: 'Key distinction',\n heading: 'REST serves developers. MCP serves AI agents.',\n body: 'That one sentence determines whether MCP belongs in your architecture. If the caller is a developer writing code, use REST. If the caller is an agent making runtime decisions, MCP earns its overhead. <strong>The mistake is treating them as competing choices rather than different interface layers.</strong>',\n },\n cards: [\n {\n id: 'q2-1', num: '2.1',\n question: 'What is an MCP vs API?',\n answer: 'REST APIs serve developers; <strong>MCP serves AI agents</strong> — and that distinction determines which belongs in your system. A REST API is a stateless HTTP endpoint with static documentation a human reads and then hardcodes calls against. MCP is JSON-RPC 2.0 over a persistent session, with a live tool manifest the agent reads at runtime and uses to decide what to call. The critical difference: with REST, a developer writes the integration once and it breaks when the API changes; with MCP, the agent re-discovers the tool manifest on every session and adapts. MCP doesn\\'t replace REST — MCP servers use REST internally. What changes is the interface layer above: REST is for humans integrating systems; MCP is for agents choosing tools.',\n },\n {\n id: 'q2-2', num: '2.2',\n question: 'Why MCP instead of REST API?',\n answer: 'Use MCP instead of REST when the consumer of the API is an AI agent making runtime decisions — not a developer writing hardcoded calls. <strong>MCP\\'s <code>tools/list</code> endpoint lets an agent discover what a server can do without any human-written glue code</strong>. With REST, someone has to read the OpenAPI spec and write the integration; with MCP, the agent reads the live capability manifest and writes the call itself. The answer changes if you control both ends: if you own the API and the code calling it, REST is simpler and faster. MCP earns its overhead when the caller is an autonomous agent that needs to self-direct across a changing tool landscape — the moment you want the agent to decide which tool to use, not just execute the one you told it to.',\n },\n {\n id: 'q2-3', num: '2.3',\n question: 'Why use MCP instead of HTTP?',\n answer: 'Raw HTTP has no standard for <strong>tool discovery, session state, or AI-native authentication</strong> — MCP adds all three on top of HTTP. An agent calling raw HTTP endpoints has to know the URL, method, parameters, and auth scheme in advance, hardcoded. An MCP server exposes a <code>tools/list</code> manifest so the agent discovers capabilities dynamically, maintains state across a session, and uses a standardized OAuth 2.1 flow for auth rather than each API\\'s bespoke scheme. The practical failure mode of raw HTTP at scale: when the tool landscape changes — a new endpoint, a deprecated parameter — every hardcoded HTTP call breaks silently; MCP\\'s session-level capability manifest surfaces changes the agent can adapt to. Building on raw HTTP for agents is not wrong at toy scale — it fails at production scale when the number of tools grows past what any developer can maintain manually.',\n },\n {\n id: 'q2-4', num: '2.4',\n question: 'Is MCP like Zapier?',\n answer: 'MCP and Zapier solve adjacent problems with different audiences. <strong>Zapier automates workflows between apps for non-technical users; MCP is a protocol for AI agents to call tools programmatically</strong>. Zapier\\'s model is: a human configures a trigger-and-action workflow once; Zapier runs it. MCP\\'s model is: an AI agent discovers available tools at runtime and decides which ones to call. The two are not mutually exclusive — Zapier built MCP support on top of its library of 9,000+ apps and 30,000+ actions, meaning an AI agent with an MCP client can now reach every Zapier-connected app without the human workflow-configuration step. The practitioner nuance: Zapier MCP is useful when you want AI agent access to Zapier\\'s app coverage without building individual MCP servers for each app.',\n },\n {\n id: 'q2-5', num: '2.5',\n question: 'What is the difference between MCP and Copilot?',\n answer: 'GitHub Copilot is an AI coding assistant that now operates as an <strong>MCP client</strong> — MCP is the protocol Copilot uses to reach external tools, not a competitor to Copilot. The relationship: Copilot is software running in VS Code or a browser; when Copilot needs to call an external tool (a Jira ticket, a GitHub repo, a web search), it does so through MCP. Before MCP, Copilot\\'s tool integrations were custom connectors maintained by Microsoft. With MCP, any tool that publishes an MCP server becomes callable by Copilot without Microsoft writing a dedicated integration. The distinction developers miss: evaluating \"MCP vs. Copilot\" is a category error — Copilot is an adopter of MCP, not an alternative to it.',\n },\n {\n id: 'q2-6', num: '2.6',\n question: 'What is the difference between MCP and LLM?',\n answer: 'An LLM is the <strong>reasoning engine</strong> — the model that reads input, generates text, and makes decisions. MCP is the protocol that gives that engine hands. Without MCP (or a comparable integration layer), an LLM can only work with what\\'s in its context window — text and pre-loaded data. With MCP, the LLM can call tools at runtime: retrieve a live database record, execute a search query, write a file, trigger a workflow. <em>Claude 3.5 Sonnet reasoning about a customer support ticket</em> is an LLM at work; Claude calling a CRM to retrieve the customer\\'s history mid-conversation is MCP at work. The frame that matters: LLMs decide; MCP acts. A sophisticated AI agent needs both.',\n },\n {\n id: 'q2-7', num: '2.7',\n question: 'Is MCP just a JSON?',\n answer: 'MCP uses <strong>JSON-RPC 2.0</strong> as its message format, but calling it \"just JSON\" misses the protocol. JSON-RPC 2.0 defines the envelope — request IDs, method names, parameters, error codes. MCP adds on top of that a defined session lifecycle: initialization handshake → capability negotiation → tool discovery → tool calls → termination. JSON alone specifies none of that structure. The distinction matters when debugging: a malformed MCP session isn\\'t a JSON syntax error — it\\'s a lifecycle state error, which requires understanding the protocol\\'s state machine, not just validating JSON. The practitioner test: if your MCP server returns valid JSON but ignores the initialization handshake, every MCP client will reject it — not because the JSON is wrong, but because the protocol contract is broken.',\n },\n ],\n },\n {\n id: 's3',\n num: '03',\n title: 'MCP Adoption — Who',\n titleItalic: 'Actually Uses It',\n deck: 'Which platforms adopted MCP, which haven\\'t, and the one misconception that sends most developers to the wrong conclusion.',\n callout: {\n eyebrow: 'Highest-value misconception',\n heading: 'MCP is not only for Claude.',\n body: 'MCP has 300+ clients as of March 2026 — including ChatGPT, GitHub Copilot, VS Code, Cursor, and Google Gemini. Anthropic created it. The Linux Foundation now governs it. <strong>Write one MCP server. Every major AI platform can call it.</strong>',\n },\n cards: [\n {\n id: 'q3-1', num: '3.1',\n question: 'Is MCP only for Claude?',\n answer: 'No — and this is the highest-value misconception in the MCP ecosystem. <strong>MCP has 300+ clients as of March 2026</strong>, including ChatGPT, GitHub Copilot, VS Code, Cursor, Windsurf, AWS Bedrock, Google Gemini, and JetBrains IDEs. Anthropic created MCP but donated its governance to the Agentic AI Foundation under the Linux Foundation in December 2025, making it a vendor-neutral standard no single company controls. OpenAI formally adopted MCP in March 2025 — four months after Anthropic launched it. The practical implication for developers: an MCP server you build today is reachable by Claude, ChatGPT, and GitHub Copilot without any modification — write the server once, serve every major agent platform.',\n },\n {\n id: 'q3-2', num: '3.2',\n question: 'Does ChatGPT use MCP?',\n answer: 'Yes — <strong>OpenAI formally adopted MCP in March 2025</strong> and added MCP support to ChatGPT apps in September 2025. OpenAI\\'s adoption removed the last credible argument that MCP was a Claude-exclusive or Anthropic-controlled standard. When OpenAI adopted MCP, the protocol had an estimated 22 million monthly SDK downloads; by March 2026 that figure reached 97 million. ChatGPT is now one of more than 300 MCP clients — meaning any MCP server you build is natively callable from ChatGPT without any OpenAI-specific integration work. The sequence developers should know: Anthropic launched → OpenAI adopted → Microsoft integrated → Google followed — MCP\\'s cross-vendor adoption happened in under 18 months.',\n },\n {\n id: 'q3-3', num: '3.3',\n question: 'Does Microsoft use MCP?',\n answer: 'Yes — Microsoft integrated MCP into <strong>GitHub Copilot, Visual Studio Code, and Copilot Studio</strong>. The GitHub Copilot extension in VS Code is among the most widely used MCP clients in the developer tooling ecosystem. Microsoft\\'s adoption matters structurally: it brought MCP into enterprise environments at scale, since VS Code and GitHub Copilot are standard tooling in most engineering organizations. The consequence for MCP server builders: publishing an MCP server means your tool is callable from VS Code\\'s AI features — the IDE that runs on more developer machines than any other. Microsoft\\'s integration also means MCP is no longer a decision individual developers make; it\\'s a platform decision that enterprise engineering teams inherit from their tooling.',\n },\n {\n id: 'q3-4', num: '3.4',\n question: 'Is Apple using MCP?',\n answer: 'Apple has not made a public MCP announcement as of May 2026. <strong>The honest answer is: unknown, but structurally likely.</strong> MCP clients that run on Apple platforms — Claude Desktop, VS Code, Cursor — are in wide use on macOS. If Apple builds agentic AI features into iOS or macOS, adopting MCP would give it instant interoperability with every existing MCP server ecosystem rather than requiring Apple to build a proprietary tool integration standard from scratch. The precedent: every other major AI platform that initially appeared absent from MCP (OpenAI, Microsoft, Google) has since formally adopted it. The practitioner read: Apple\\'s silence is not a rejection — it\\'s the gap between enterprise announcement cycles and protocol adoption reality.',\n },\n {\n id: 'q3-5', num: '3.5',\n question: 'Is Zapier MCP free?',\n answer: 'Yes — <strong>Zapier MCP is included on all Zapier plans, including the Free tier</strong>, at no additional cost. There is no separate product SKU. The only cost is task consumption: each MCP tool call uses 2 tasks from your existing Zapier task quota. A developer on Zapier\\'s free plan can connect an AI agent to Zapier\\'s 9,000+ app library and 30,000+ actions today, using their existing task allocation. The nuance that changes the math: \"free\" means no incremental charge, not zero cost — if an agent makes 500 MCP tool calls in a month, that consumes 1,000 tasks from your quota. High-volume agent workflows will exhaust free-tier quotas quickly and require a paid plan.',\n },\n ],\n },\n {\n id: 's4',\n num: '04',\n title: 'When MCP',\n titleItalic: 'Breaks Down',\n deck: 'When not to use MCP, why production projects stall, and the honest comparison to adjacent tools.',\n callout: {\n eyebrow: 'Practitioner test',\n heading: 'MCP has no native auth.',\n body: 'The spec recommends OAuth 2.1 with PKCE. Your MCP server only has it if you built it. <strong>Every production MCP project that stalled did so at auth, not at the protocol itself.</strong>',\n },\n cards: [\n {\n id: 'q4-1', num: '4.1',\n question: 'When not to use MCP?',\n answer: 'Skip MCP when your system is <strong>developer-to-API rather than agent-to-tool</strong>. If a human developer is writing the integration code, REST is simpler and adds no session-management overhead. Skip MCP when you control both ends of the integration and don\\'t need runtime discovery — if you own the calling code and the tool, you already know what the tool does; the <code>tools/list</code> handshake is unnecessary overhead. Skip MCP when latency is the primary constraint — persistent sessions add round-trip initialization costs that stateless REST calls don\\'t. Skip MCP when your AI use case is inference-only: if the model generates text without calling any external system, MCP adds complexity with zero benefit. The practitioner test: if a human could write the integration code once and it would never need to change, use REST.',\n },\n {\n id: 'q4-2', num: '4.2',\n question: 'Why are people moving away from MCP?',\n answer: 'The most common friction points are auth complexity, debugging difficulty, and server quality variance. <strong>MCP has no native authentication</strong> — developers must implement OAuth 2.1 with PKCE themselves, and the gap between \"runs locally\" and \"ships securely to production\" is where most MCP projects stall. Debugging is harder than REST because persistent sessions have lifecycle state: a broken MCP connection isn\\'t a failed HTTP request — it\\'s a state machine that failed at initialization, capability negotiation, or mid-session, and the error may not surface clearly. The 10,000+ public MCP servers have wildly inconsistent quality — some are maintained production services; many are experimental projects with no uptime guarantees. \"Moving away\" overstates it: developers who understand MCP\\'s limits ship successfully; the ones who expected plug-and-play get surprised by the operational requirements.',\n },\n {\n id: 'q4-3', num: '4.3',\n question: 'Is A2A dead?',\n answer: 'A2A — Google\\'s Agent-to-Agent protocol — is not dead, but it has not achieved the ecosystem density MCP has. <strong>MCP and A2A address different layers</strong>: MCP handles tool access (an agent calling an external capability); A2A handles agent coordination (one agent delegating a task to another agent). They are complementary, not competing. The adoption gap is real: MCP has 97 million monthly SDK downloads and 300+ clients as of March 2026; A2A\\'s ecosystem is smaller by every public measure. The honest framing: A2A is the correct protocol for multi-agent orchestration problems; MCP is the correct protocol for tool-calling problems. A developer who needs both can use both — and the most sophisticated agent architectures will.',\n },\n {\n id: 'q4-4', num: '4.4',\n question: 'Will MCP replace API?',\n answer: 'No — <strong>MCP wraps APIs rather than replacing them</strong>. MCP servers use REST APIs internally; they expose MCP above and call REST below. The correct model: REST remains the implementation layer; MCP becomes the interface layer that AI agents interact with. This isn\\'t a philosophical position — it\\'s how production MCP servers are built. <em>An MCP server for Stripe</em> doesn\\'t replace Stripe\\'s REST API; it wraps it, adding the <code>tools/list</code> manifest and session management that AI agents expect. The question developers should ask instead: not \"will MCP replace REST?\" but \"at which layer does MCP belong in my stack?\" — the answer is always above your existing APIs, never instead of them.',\n },\n {\n id: 'q4-5', num: '4.5',\n question: 'Why are people against Copilot?',\n answer: 'Copilot critics object to Microsoft\\'s <strong>pricing model, data training practices, and the perception that Copilot is a productivity layer rather than a reasoning upgrade</strong>. Common complaints include the cost per seat relative to perceived productivity gains, concerns about code written in Copilot being used to train future models, and the view that Copilot autocompletes rather than reasons. These criticisms are entirely separate from MCP — MCP is the protocol Copilot uses to reach external tools; it doesn\\'t change Copilot\\'s pricing, training data policies, or reasoning depth. The relevant clarification for developers evaluating both: being against Copilot doesn\\'t mean avoiding MCP — every other major AI platform (Claude, ChatGPT, Cursor) also uses MCP and has none of Copilot\\'s specific controversies.',\n },\n ],\n },\n {\n id: 's5',\n num: '05',\n title: 'MCP Architecture',\n titleItalic: '& Security',\n deck: 'Transport layers, encryption, auth, and the gap between what MCP promises and what your server actually ships.',\n callout: {\n eyebrow: 'Security gap',\n heading: 'MCP won\\'t refuse an HTTP connection.',\n body: 'The spec recommends HTTPS. Enforcement is the developer\\'s job. Most MCP tutorials run over HTTP for simplicity — developers copy that configuration to production and ship an insecure server without any warning from the protocol. <strong>TLS is your responsibility, not MCP\\'s.</strong>',\n },\n cards: [\n {\n id: 'q5-1', num: '5.1',\n question: 'Is MCP built on HTTP?',\n answer: 'MCP supports two transport layers: <strong>stdio for local in-process communication and HTTP with Server-Sent Events (SSE) for remote connections</strong>. Local MCP servers — the kind that run on a developer\\'s machine alongside Claude Desktop — use stdio, which is faster and simpler because the agent and server are in the same process space. Remote production MCP servers use HTTP/SSE, which enables cross-network communication but requires TLS since MCP has no native encryption layer. Most production MCP servers use the HTTP transport — it\\'s what makes an MCP server callable from any agent on any machine. The choice isn\\'t either-or: a single MCP server implementation can support both transports, but most developers building for production start with HTTP/SSE.',\n },\n {\n id: 'q5-2', num: '5.2',\n question: 'Does MCP use HTTP or HTTPS?',\n answer: 'MCP supports both, but <strong>the spec explicitly recommends HTTPS for any remote server</strong> — and running an MCP server over plain HTTP in production is a security vulnerability, not a configuration choice. MCP does not natively enforce encryption; TLS must be configured by the developer. The risk is concrete: MCP sessions carry tool call parameters and responses over a persistent connection — plain HTTP exposes that entire session to interception. Local development over localhost HTTP is acceptable (the connection doesn\\'t leave the machine); any server exposed over a network must use HTTPS. The gap most teams hit: MCP tutorials run over HTTP for simplicity; developers copy that configuration to production and ship an insecure server without realizing the protocol never warned them.',\n },\n {\n id: 'q5-3', num: '5.3',\n question: 'Does MCP use OAuth?',\n answer: 'MCP standardizes <strong>OAuth 2.1 with PKCE</strong> for authentication in remote server connections — but this is not built into the base protocol. OAuth support was added to the MCP spec after initial launch, when real-world adoption revealed auth as the most common production gap. Implementing it requires developers to configure an OAuth 2.1 server, handle PKCE flows, and manage token refresh — none of which MCP handles automatically. Local MCP servers running over stdio typically require no auth because they run in a trusted local environment. Remote servers require auth, and OAuth 2.1 with PKCE is the spec-recommended approach. The practitioner reality: \"MCP supports OAuth\" means MCP defines how OAuth should work in its context — it doesn\\'t mean your MCP server has OAuth until you build it.',\n },\n {\n id: 'q5-4', num: '5.4',\n question: 'Is an MCP server just an API?',\n answer: 'An MCP server looks like an API from the outside but differs in three structural ways. First, it exposes a <strong>standard capabilities manifest via <code>tools/list</code></strong> — a traditional API has static docs; an MCP server has a live, machine-readable manifest the agent queries at runtime. Second, it maintains session state across multiple calls within a connection — REST APIs are stateless by design; MCP sessions are stateful. Third, it uses JSON-RPC 2.0 rather than REST conventions — request/response patterns, error codes, and method naming all follow JSON-RPC semantics, not HTTP verb conventions. <em>Calling an MCP server</em> and <em>calling a REST API</em> look superficially similar from a network perspective; they\\'re architecturally different contracts that break in different ways when misused.',\n },\n ],\n },\n {\n id: 's6',\n num: '06',\n title: 'MCP Ecosystem',\n titleItalic: '& What\\'s Next',\n deck: 'Power Automate, Google\\'s investment, and why MCP is a layer above APIs rather than a replacement for them.',\n cards: [\n {\n id: 'q6-1', num: '6.1',\n question: 'What has replaced Power Automate?',\n answer: 'Nothing has fully replaced Power Automate — it remains Microsoft\\'s enterprise workflow product and continues to serve the structured, if-then automation use cases it was built for. <strong>What MCP and AI agents are taking from Power Automate is the dynamic, decision-driven tier of automation</strong> — tasks that require reasoning, not just routing. Power Automate routes data between systems based on rules a human writes; an MCP-connected agent can decide which tools to call, handle edge cases without pre-written rules, and adapt to inputs that a static workflow would reject. <em>Routing a support ticket to the right queue</em> is Power Automate\\'s domain; <em>reading the ticket, checking the customer\\'s history, drafting a response, and escalating if needed</em> is where MCP-connected agents outperform static workflow tools. The transition isn\\'t replacement — it\\'s a shift in which automation problems belong in which category.',\n },\n {\n id: 'q6-2', num: '6.2',\n question: 'Does Google own 14% of Anthropic?',\n answer: 'Google has invested significantly in Anthropic across multiple rounds, but <strong>the exact ownership percentage is not publicly disclosed</strong>. Reports indicate Google invested $300 million in a 2023 funding round and participated in subsequent rounds. The precise ownership stake — including whether it is or was 14% — has not been confirmed in any public filing. What is confirmed: Google Cloud and Anthropic have a partnership that includes MCP integration into Google\\'s Gemini models and Google Cloud services. The relevant fact for MCP evaluation: Google\\'s investment in Anthropic did not prevent Google from independently adopting MCP — both Google Gemini and Google Cloud services are listed among MCP clients, and adoption was driven by the protocol\\'s open governance under the Linux Foundation, not by equity relationships.',\n },\n {\n id: 'q6-3', num: '6.3',\n question: 'Is MCP basically an API?',\n answer: 'MCP is best understood as a <strong>layer above APIs, not a replacement for them</strong>. Saying MCP is basically an API is like saying HTTP is basically a phone call — technically there\\'s a connection, but the architectural purpose is different. Traditional APIs serve developers who know what they want to call and write the call in advance. MCP serves AI agents that discover what\\'s available at runtime and decide what to call dynamically. The difference shows up at scale: an API breaks when you add a new endpoint that no existing code knows to call; an MCP server\\'s <code>tools/list</code> response automatically surfaces the new capability to every connected agent on the next session. MCP Scraper is an example of what this makes possible — a web data tool built not for developers to integrate manually, but for AI agents to discover and call directly, the way MCP was designed to work.',\n },\n ],\n },\n ],\n },\n {\n slug: 'when-not-to-use-an-mcp',\n title: 'When Not to Use an MCP: The Architectural Decision Guide',\n titleDisplay: 'When Not to Use',\n titleDisplayItalic: 'an MCP.',\n description: 'Not complexity—architecture. The three signals that disqualify MCP, the A2A and function-calling alternatives, and the protocol durability question for 2026.',\n publishedAt: '2026-05-24',\n author: 'MCP Scraper',\n authorInitials: 'M',\n tags: ['MCP', 'AI', 'protocol', 'architecture', 'agents'],\n category: 'MCP Protocol',\n badge: '36 questions answered',\n fieldGuideLabel: 'Field guide',\n deck: 'The complexity calculus is the wrong frame. In 2026, the real question is whether MCP will still be the dominant protocol when your integration ships — and that requires reading the ecosystem, not the docs.',\n readTimeMinutes: 15,\n stats: [\n { value: '5', label: 'sections' },\n { value: '36', label: 'questions' },\n { value: '3', label: 'skip signals' },\n { value: '0', label: 'fluff' },\n ],\n ctaHeading: 'Read the ecosystem with',\n ctaHeadingItalic: 'live PAA data.',\n ctaBody: 'MCP Scraper harvests People Also Ask questions at scale — so your MCP decision is based on what practitioners are actually searching right now, not what the docs say they should ask.',\n sections: [\n {\n id: 's1',\n num: '01',\n title: 'What MCP',\n titleItalic: 'Actually Is.',\n deck: 'Every article about MCP tells you it\\'s a protocol that lets AI models call tools. That definition is technically accurate and practically useless — it skips the problem MCP was invented to solve, which is the only thing that tells you whether you need it at all.',\n cards: [\n {\n id: 'q1-1', num: '1.1',\n question: 'What is an MCP in AI?',\n answer: 'MCP (Model Context Protocol) is an open standard that solves the N×M integration problem — the combinatorial explosion that happens when M AI models each need custom connectors to N external tools. Before MCP existed, connecting three AI models to ten tools required up to thirty custom integrations; MCP standardizes the interface so any compliant model can call any compliant server without custom work. <strong>The official description is \"like a USB-C port for AI applications\" — a single standardized connector that replaces a sprawl of proprietary cables.</strong> The current stable specification (2025-11-25) is built on JSON-RPC 2.0, defines three roles (Hosts, Clients, Servers), and is supported across Claude, ChatGPT, Visual Studio Code, and Cursor. The question to ask before adopting MCP is not \"what is it\" but \"do I have an N×M problem worth solving at the protocol layer\" — if you have one model and one tool, you don\\'t.',\n source: 'https://modelcontextprotocol.io',\n },\n {\n id: 'q1-2', num: '1.2',\n question: 'What is MCP in simple terms?',\n answer: 'MCP is the standardized language that lets an AI model ask an external tool to do something — and get a structured answer back — without either side needing to know how the other was built. <strong>Think of it as a universal remote control for AI agents: instead of each AI building its own custom remote for each device it wants to control, MCP gives every device a standard input jack and every remote a standard output plug.</strong> In practice, an MCP server exposes a list of \"tools\" (callable functions) with descriptions the AI model reads. The model decides which tool to call and passes structured arguments; the server executes the operation and returns a result. What makes this worth a protocol is that the model doesn\\'t need to be rewritten when the tool changes, and the tool doesn\\'t need to be rewritten when a new AI model is added. A practitioner who has shipped MCP integrations will tell you the simplification is real at scale — and essentially invisible for single-tool, single-model use cases.',\n source: 'https://modelcontextprotocol.io',\n },\n {\n id: 'q1-3', num: '1.3',\n question: 'What problems does MCP solve?',\n answer: 'MCP solves two structural problems that emerge when AI systems grow beyond a single model connected to a single tool: the N×M connector problem and the capability-discovery problem. <strong>The N×M connector problem is architectural: without a standard, every new AI model requires custom integration code for every tool it needs to call — the cost scales multiplicatively, not additively.</strong> The capability-discovery problem is subtler: before MCP, a model had to know at design time exactly what an external tool could do; with MCP, the server advertises its capabilities dynamically at session start, so the model can adapt to whatever tools are available. Both problems are irrelevant when you have one model and one tool with a stable interface — which is why \"what problems does MCP solve\" is also the correct frame for \"when not to use MCP.\" If neither problem applies to your current system, the protocol layer adds overhead without payoff.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q1-4', num: '1.4',\n question: 'What does the MCP do?',\n answer: 'MCP manages the full lifecycle of a tool-calling session between an AI model and an external server: capability negotiation at connection, tool invocation during the session, and structured result delivery back to the model. <strong>The stateful session is MCP\\'s defining feature — unlike a REST API call where each request is independent, an MCP session maintains context across multiple tool calls, so the model can use the output of one tool as the input to the next without the orchestration living inside the model itself.</strong> In concrete terms: when a Claude instance connects to an MCP server, the server first sends a list of available tools with descriptions. Claude reads those descriptions, decides which tool to call, sends a structured JSON-RPC request, and receives a structured result. The session stays open so Claude can call additional tools without re-authenticating or re-negotiating capabilities. For single-tool, single-call integrations, this session overhead is pure cost.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q1-5', num: '1.5',\n question: 'Is MCP a tool or framework?',\n answer: 'MCP is a protocol — not a tool, not a framework, and not a library. <strong>A protocol defines the rules for how two parties communicate; it does not prescribe how either party is implemented, which is what makes MCP portable across AI models and tool servers built in different languages and architectures.</strong> The practical consequence is that calling MCP a \"framework\" is a category error that leads to wrong architectural expectations: frameworks come with opinions about structure, abstractions, and project layout. MCP has none of those — it defines message formats, session lifecycle, and capability negotiation only. If you need a framework to build MCP clients or servers, you use an SDK (Anthropic provides SDKs for Python and TypeScript); the SDK is the framework layer on top of the protocol. The distinction matters when evaluating adoption cost: you\\'re not adopting a framework with its opinionated structure, you\\'re implementing a protocol that can live inside whatever structure you already have.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q1-6', num: '1.6',\n question: 'What is the difference between MCP and LLM?',\n answer: 'An LLM (Large Language Model) is the AI system that reasons and generates text; MCP is the protocol that tells the LLM how to interact with external tools. <strong>The relationship is one-directional: LLMs use MCP — MCP does not use or require an LLM.</strong> An LLM without MCP can only work with information it was trained on and whatever appears in its context window. An LLM with MCP can call external servers to retrieve current data, execute code, query databases, and take actions in other systems — then incorporate those results into its reasoning. MCP adds the tool-use capability; the LLM supplies the reasoning about when and how to use those tools. The confusion between MCP and LLM typically indicates someone comparing a capability (tool-calling) with the system exercising that capability (the model) — the correct comparison is MCP vs. function calling (another tool-use mechanism built into model APIs), not MCP vs. LLM.',\n },\n ],\n interactiveHtml: `<div class=\"si si-quiz\">\n <span class=\"si-heading\">Quick check — what did you just read?</span>\n <div class=\"si-quiz-q\" data-correct=\"1\">\n <p class=\"si-quiz-q-text\">What was broken before MCP existed that MCP is designed to fix?</p>\n <div class=\"si-quiz-opts\">\n <button class=\"si-quiz-opt\" data-idx=\"0\">AI models couldn't understand natural language tool names</button>\n <button class=\"si-quiz-opt\" data-idx=\"1\">Every AI model needed a custom integration for every tool it wanted to use</button>\n <button class=\"si-quiz-opt\" data-idx=\"2\">REST APIs were too slow for real-time agent workflows</button>\n </div>\n <p class=\"si-quiz-explanation\" hidden>MCP solves the N×M integration problem — before it existed, connecting M AI models to N tools required M×N custom connectors; MCP standardizes the interface so any compliant model can call any compliant tool without custom work.</p>\n </div>\n <div class=\"si-quiz-q\" data-correct=\"1\">\n <p class=\"si-quiz-q-text\">Is MCP only for Claude?</p>\n <div class=\"si-quiz-opts\">\n <button class=\"si-quiz-opt\" data-idx=\"0\">Yes — it was built by Anthropic and only works with Claude</button>\n <button class=\"si-quiz-opt\" data-idx=\"1\">No — ChatGPT, Microsoft Copilot, and Apple Intelligence have all adopted MCP</button>\n <button class=\"si-quiz-opt\" data-idx=\"2\">No — but Claude has the most MCP server support by count</button>\n </div>\n <p class=\"si-quiz-explanation\" hidden>MCP is an open protocol. ChatGPT, Microsoft Copilot, and Apple Intelligence have all adopted it, which is part of what gives it durability as a standard beyond Anthropic's own ecosystem.</p>\n </div>\n <script>(function(){\n document.querySelectorAll('.si-quiz .si-quiz-q').forEach(function(qBlock){\n var correct=+qBlock.dataset.correct;\n var explanation=qBlock.querySelector('.si-quiz-explanation');\n qBlock.querySelectorAll('.si-quiz-opt').forEach(function(btn){\n btn.addEventListener('click',function(){\n if(qBlock.dataset.answered)return;\n qBlock.dataset.answered='1';\n var idx=+btn.dataset.idx;\n qBlock.querySelectorAll('.si-quiz-opt').forEach(function(b,i){\n b.disabled=true;\n if(i===correct)b.classList.add('si-quiz-correct');\n else if(i===idx&&idx!==correct)b.classList.add('si-quiz-wrong');\n });\n explanation.hidden=false;\n });\n });\n });\n })();<\\/script>\n</div>`,\n },\n {\n id: 's2',\n num: '02',\n title: 'Is MCP Still',\n titleItalic: 'Worth Betting On?',\n deck: 'The complexity calculus is the wrong frame. The real question in 2026 is whether MCP will still be the dominant protocol by the time your integration ships — and that answer requires reading the ecosystem, not the docs.',\n callout: {\n eyebrow: 'Key finding',\n heading: 'Betting on MCP is a protocol survival bet, not a complexity tradeoff.',\n body: 'A2A (Google\\'s Agent-to-Agent protocol), native function calling in closed-ecosystem deployments (ChatGPT, Copilot, Apple Intelligence), and direct HTTP tool APIs are not theoretical alternatives — they are <strong>active defection paths already visible in the PAA surface</strong>. \"Don\\'t use MCP when it\\'s too complex\" is technically correct and strategically useless for anyone building with a 12-month horizon.',\n },\n cards: [\n {\n id: 'q2-1', num: '2.1',\n question: 'Is MCP still relevant in 2026?',\n answer: 'MCP is relevant in 2026 and its durability signal is stronger than at any prior point — but the relevance is conditional on use case in a way no competitor article acknowledges. <strong>The strongest durability signal is cross-vendor adoption: ChatGPT, Microsoft Copilot, and Apple Intelligence have all shipped MCP support, meaning no single company can deprecate or replace the protocol unilaterally without breaking their own integrations.</strong> The 2026 MCP roadmap confirms active governance investment — transport scalability, enterprise readiness, and a governance working group are all in-flight. What no competitor article acknowledges is that relevance and fitness-for-your-use-case are orthogonal: MCP can be a durable protocol standard and still be the wrong choice for a specific latency-sensitive or single-tool integration. The practitioner question is not \"is MCP relevant\" but \"is the problem MCP solves the problem I have\" — and the ecosystem signal helps answer the first; the architecture of your integration answers the second.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-mcp-roadmap/',\n },\n {\n id: 'q2-2', num: '2.2',\n question: 'Is MCP deprecated?',\n answer: 'MCP is not deprecated — the current stable specification is 2025-11-25 and a 2026-07-28 release candidate has been published with significant transport and extensibility improvements. <strong>A protocol with an active release candidate, multi-vendor adoption across competing platforms, and an official 2026 governance roadmap is the opposite of deprecated.</strong> The question surfaces in search because MCP\\'s early adoption phase produced a large number of low-quality MCP servers that have been abandoned — which creates confusion between \"MCP servers being deprecated\" and \"MCP the protocol being deprecated.\" Individual MCP servers deprecate; the protocol has not. The 2026 roadmap explicitly lists \"governance maturation\" as a priority, including a contributor ladder and working groups — infrastructure you only invest in for a protocol you intend to be running for years.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q2-3', num: '2.3',\n question: 'Why are people moving away from MCP?',\n answer: 'The defection signal in the PAA surface is real, but it is use-case-specific rather than a wholesale ecosystem move. <strong>Practitioners are moving away from MCP for specific workloads — particularly latency-sensitive single-tool integrations — because the protocol overhead (tens to hundreds of milliseconds per invocation) is architectural and cannot be tuned away, making direct REST calls or native function calling the correct choice for those scenarios.</strong> A second, smaller defection is organizational: teams that adopted MCP early for simple automation use cases discovered that a shell script or cron job does the same work with zero protocol overhead. What the PAA questions signal is not that MCP is failing but that a correction is underway — the early hype surface over-applied MCP to integrations that never needed a protocol layer. The result is practitioners who correctly concluded MCP was wrong for their use case, but incorrectly generalized that conclusion into \"MCP is wrong.\"',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q2-4', num: '2.4',\n question: 'Is the industry moving away from MCP?',\n answer: 'The industry is not moving away from MCP — the industry is stratifying MCP adoption by use case, which looks like defection from the outside. <strong>The stronger indicator of industry trajectory is that Google\\'s A2A protocol, the most credible competitor to MCP\\'s ecosystem coordination role, is positioned as complementary rather than competing: A2A handles agent-to-agent coordination while MCP handles agent-to-tool connections.</strong> Apple Intelligence, Microsoft Copilot, and ChatGPT are all shipping MCP support in 2026 — the set of vendors who would benefit most from a competitor to MCP being viable are the vendors most committed to it. The search question \"is the industry moving away from MCP\" is itself a signal: it reflects practitioners who have seen real-world MCP failures (security incidents, performance overloads) and are trying to determine whether those are early-adoption edge cases or signs of structural protocol weakness. They are edge cases.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-mcp-roadmap/',\n },\n {\n id: 'q2-5', num: '2.5',\n question: 'What has replaced MCP?',\n answer: 'Nothing has replaced MCP as the dominant agent-to-tool protocol standard as of 2026 — the closest alternative is native function calling within single-provider ecosystems, but that solves a narrower problem than MCP addresses. <strong>Google\\'s A2A (Agent-to-Agent protocol) is the most commonly cited replacement candidate, but it operates at a different layer: A2A coordinates between AI agents, MCP connects agents to tools — a system can and does use both simultaneously.</strong> Direct REST API calls are a legitimate alternative when portability and multi-tool orchestration are not requirements — but REST does not replace MCP at the protocol layer, it replaces the use cases where MCP was never the right choice. The practical state in 2026: MCP owns agent-to-tool protocol, A2A owns agent-to-agent coordination, and native function calling owns single-provider single-tool integrations. Three layers, three tools — none replacing the others.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-mcp-roadmap/',\n },\n {\n id: 'q2-6', num: '2.6',\n question: 'Did Anthropic abandon MCP?',\n answer: 'Anthropic has not abandoned MCP — Anthropic transferred MCP governance to an independent multi-vendor body specifically to prevent it from being an Anthropic-controlled protocol that competitors would be reluctant to adopt. <strong>The governance transfer is the strongest evidence of long-term commitment: Anthropic gave up unilateral control of the specification precisely to make it a durable industry standard rather than a proprietary Anthropic tool.</strong> The 2026 roadmap lists enterprise readiness, transport evolution, and governance maturation as the four active priorities — all of which require continued investment, not abandonment. Claude Desktop remains the reference MCP client implementation and Claude Code ships with built-in MCP client support. The \"did Anthropic abandon MCP\" question in the PAA surface reflects early-stage ecosystem anxiety, not observable evidence.',\n source: 'https://anthropic.com',\n },\n {\n id: 'q2-7', num: '2.7',\n question: 'Is MCP out of date?',\n answer: 'MCP is not out of date — the 2025-11-25 stable specification is the current standard, and a 2026-07-28 release candidate introduces stateless protocol core and an official extensions framework that addresses the scalability limitations practitioners have identified as MCP\\'s most significant architectural constraint. <strong>The 2026-07-28 release candidate\\'s stateless protocol core — eliminating session headers so any MCP request can land on any server instance — directly resolves the sticky-routing problem that makes MCP difficult to scale horizontally, which is the primary architectural criticism of the protocol in production deployments.</strong> A protocol with an active release candidate in flight is current by definition. What is out of date are many of the MCP tutorials and server implementations published in 2024 and early 2025, before the 2025-11-25 stable spec — the protocol has evolved and most existing guides have not.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/',\n },\n {\n id: 'q2-8', num: '2.8',\n question: 'Why is MCP outdated?',\n answer: 'MCP is not outdated as a protocol, but specific MCP implementations and architectural patterns from 2024 are legitimately obsolete. <strong>The stateful session model in the 2025-11-25 spec — which requires sticky routing for horizontal scaling — is being replaced in the 2026-07-28 release candidate by a stateless protocol core that any load balancer can route without coordination.</strong> Production teams that built MCP infrastructure against the 2024 draft spec and are running into horizontal scaling constraints are experiencing real architectural debt, not protocol obsolescence. The correct frame: the underlying standard is evolving to fix known weaknesses; the teams whose infrastructure is \"outdated\" are those who built against pre-stable versions and have not migrated. The release candidate\\'s ten-week Tier 1 SDK window means that SDK-based implementations will be straightforward to update; custom protocol implementations will require more deliberate migration work.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/',\n },\n {\n id: 'q2-9', num: '2.9',\n question: 'Is A2A dead?',\n answer: 'A2A (Google\\'s Agent-to-Agent protocol) is not dead — it is active and positioned as complementary to MCP, not competing with it. <strong>A2A handles coordination between AI agents (one agent dispatching tasks to another); MCP handles agent-to-tool connections (an agent calling a file system, database, or web API) — a well-architected multi-agent system uses both protocols simultaneously, with A2A orchestrating at the agent layer and MCP connecting each agent to its tools.</strong> The \"is A2A dead\" question in the PAA surface reflects the same protocol-survival anxiety as the MCP trajectory questions — practitioners are trying to determine which protocol bets are safe. The practical answer is that A2A and MCP are not competing for the same layer, so the question of which one \"wins\" is a category error. The correct question is whether your system has agent-to-agent coordination needs (A2A) and agent-to-tool needs (MCP) — most agentic systems have both.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-mcp-roadmap/',\n },\n ],\n interactiveHtml: `<div class=\"si si-quiz\">\n <span class=\"si-heading\">Which adoption signals actually matter?</span>\n <div class=\"si-quiz-q\" data-correct=\"1\">\n <p class=\"si-quiz-q-text\">Which of the following signals most strongly indicates a protocol standard is durable — not just hyped?</p>\n <div class=\"si-quiz-opts\">\n <button class=\"si-quiz-opt\" data-idx=\"0\">More than 16,000 servers published on the ecosystem registry</button>\n <button class=\"si-quiz-opt\" data-idx=\"1\">Multi-vendor adoption from competing platforms — ChatGPT, Copilot, and Apple Intelligence all shipping MCP support</button>\n <button class=\"si-quiz-opt\" data-idx=\"2\">Anthropic's continued investment in the Claude MCP SDK</button>\n </div>\n <p class=\"si-quiz-explanation\" hidden>Cross-vendor adoption from competing platforms is the strongest durability signal — it means no single company can deprecate or replace the protocol unilaterally without breaking their own integrations.</p>\n </div>\n <div class=\"si-quiz-q\" data-correct=\"1\">\n <p class=\"si-quiz-q-text\">Is A2A (Google's Agent-to-Agent protocol) a replacement for MCP?</p>\n <div class=\"si-quiz-opts\">\n <button class=\"si-quiz-opt\" data-idx=\"0\">Yes — A2A handles what MCP handles, and Google's scale makes it the safer bet</button>\n <button class=\"si-quiz-opt\" data-idx=\"1\">No — A2A and MCP operate at different layers; A2A coordinates between agents, MCP connects agents to tools</button>\n <button class=\"si-quiz-opt\" data-idx=\"2\">Not yet — A2A is in beta and has not shipped production support</button>\n </div>\n <p class=\"si-quiz-explanation\" hidden>A2A and MCP are complementary, not competing — A2A handles agent-to-agent coordination while MCP handles agent-to-tool connections. An agentic system can use both simultaneously.</p>\n </div>\n <script>(function(){\n document.querySelectorAll('.si-quiz .si-quiz-q').forEach(function(qBlock){\n var correct=+qBlock.dataset.correct;\n var explanation=qBlock.querySelector('.si-quiz-explanation');\n qBlock.querySelectorAll('.si-quiz-opt').forEach(function(btn){\n btn.addEventListener('click',function(){\n if(qBlock.dataset.answered)return;\n qBlock.dataset.answered='1';\n var idx=+btn.dataset.idx;\n qBlock.querySelectorAll('.si-quiz-opt').forEach(function(b,i){\n b.disabled=true;\n if(i===correct)b.classList.add('si-quiz-correct');\n else if(i===idx&&idx!==correct)b.classList.add('si-quiz-wrong');\n });\n explanation.hidden=false;\n });\n });\n });\n })();<\\/script>\n</div>`,\n },\n {\n id: 's3',\n num: '03',\n title: 'The Auth and Transport',\n titleItalic: 'Layer Exposed.',\n deck: 'Every article about how MCP works tells you it uses JSON-RPC over HTTP with OAuth. Which parts of that stack are mandatory versus configurable, and where the auth implementation diverges from standard OAuth flows, is information that no competitor article provides.',\n cards: [\n {\n id: 'q3-1', num: '3.1',\n question: 'Does MCP use OAuth?',\n answer: 'MCP uses OAuth 2.1, but only for remote server deployments — local stdio transports require no OAuth at all. <strong>The critical clarification that no competitor article makes is that MCP mandates Authorization Code flow with PKCE, not the simpler client credentials flow that many backend developers default to when implementing OAuth for service-to-service communication.</strong> The distinction matters because client credentials (service account tokens) are easier to implement and widely used for API authentication, but MCP\\'s security model requires the delegated-authorization pattern that Authorization Code + PKCE provides. Teams building MCP servers for the first time who reach for client credentials because they\\'re familiar with it from other service-to-service auth implementations will find their implementation non-compliant. Budget the Authorization Code + PKCE implementation time explicitly — it is not optional for remote deployments.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q3-2', num: '3.2',\n question: 'Does MCP require OAuth?',\n answer: 'MCP requires OAuth 2.1 only for remote server deployments — the stdio transport used for local (same-machine) integrations has no OAuth requirement. <strong>This transport-conditional auth requirement is the single most important implementation detail for any team evaluating MCP adoption cost: if your use case is a local MCP server running on the same machine as the client, OAuth is not in scope; if your use case is a remote server that any compliant client connects to over the network, OAuth 2.1 with Authorization Code + PKCE is mandatory, not optional.</strong> The practical implication: a solo developer building an MCP server for personal use with Claude Desktop faces zero auth overhead. An engineering team building a shared MCP server for multi-user remote access faces a full OAuth 2.1 implementation — dynamic client registration, PKCE, token storage, refresh flows, and the security hardening that the postmark-mcp breach demonstrated is necessary even for internal tools.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q3-3', num: '3.3',\n question: 'Does MCP only support OAuth?',\n answer: 'MCP\\'s remote transport requires OAuth 2.1 — it does not support API key authentication, JWT bearer tokens issued by internal services, or session cookie authentication at the protocol level. <strong>Local stdio transports bypass the auth layer entirely, so \"MCP only supports OAuth\" is accurate for remote deployments and irrelevant for local ones — but teams who discover this constraint after committing to a remote deployment architecture will find there is no protocol-compliant workaround.</strong> Some MCP server implementations add custom API key handling at the application layer on top of the protocol, but this is a non-standard extension, not a supported alternative. The MCP 2026 roadmap identifies SSO integration as an \"enterprise readiness\" priority that is currently the \"least defined\" — meaning enterprise-grade auth patterns beyond basic OAuth 2.1 are still unresolved at the specification level. For teams whose auth infrastructure is built around SAML, LDAP, or internal JWT issuers, the current MCP auth model requires a dedicated translation layer.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-mcp-roadmap/',\n },\n {\n id: 'q3-4', num: '3.4',\n question: 'Is MCP built on HTTP?',\n answer: 'MCP supports multiple transports — HTTP (with Server-Sent Events for streaming), stdio, and WebSockets — and is not exclusively HTTP-based. <strong>The stdio transport runs over standard input/output streams with no network layer, making it suitable for local integrations where HTTP would add unnecessary complexity; the HTTP + SSE transport is the standard for remote deployments.</strong> The current stable specification (2025-11-25) defines Streamable HTTP as the remote transport, but the 2026 roadmap identifies \"transport evolution and scalability\" as a top priority because Streamable HTTP has documented production challenges with horizontal scaling. The 2026-07-28 release candidate addresses this by introducing a stateless protocol core that removes the sticky-routing constraint that makes Streamable HTTP difficult to scale across multiple server instances.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q3-5', num: '3.5',\n question: 'Does MCP use HTTP or HTTPS?',\n answer: 'MCP requires HTTPS for all remote server deployments — HTTP is only valid for stdio local transports where no network layer is involved. <strong>The requirement is not optional: a remote MCP server operating over plain HTTP exposes all tool descriptions, tool arguments, and tool results in plaintext — and because tool descriptions are processed as instructions by the AI model, plaintext transmission of tool descriptions is a prompt injection surface that attackers can exploit at the network layer.</strong> The practical consequence: any MCP remote server that accepts HTTP connections without TLS should be treated as misconfigured by default, not as a valid \"internal-only\" deployment. Internal networks are not a substitute for transport encryption when the payloads include AI model instructions. HTTPS enforcement is deployment infrastructure, not application code — but it is the first verification any MCP deployment should make before exposing a server to any client other than localhost.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q3-6', num: '3.6',\n question: 'Is MCP just a JSON?',\n answer: 'MCP is built on JSON-RPC 2.0 — a protocol for remote procedure calls encoded as JSON messages — but the JSON encoding is a transport detail, not what MCP is. <strong>What distinguishes MCP from \"just a JSON API\" is the stateful session lifecycle and capability negotiation: a JSON API call is stateless (each request is independent); an MCP session is stateful (the server advertises capabilities at connect time, and both parties maintain session state across multiple tool invocations).</strong> The confusion is understandable because an MCP message looks like a JSON API payload — it has a method, params, and result field. The difference is in what those fields encode: not CRUD operations on resources, but tool invocations within a capability-negotiated session. Practitioners who have built REST APIs find MCP\\'s JSON encoding familiar; the session model is what requires the conceptual adjustment. If the session model isn\\'t providing value for your use case, you likely don\\'t need MCP.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n ],\n interactiveHtml: `<div class=\"si si-checklist\">\n <span class=\"si-heading\">MCP auth and transport — verify before you deploy</span>\n <div class=\"si-check-progress-row\">\n <div class=\"si-check-bar-wrap\"><div class=\"si-check-bar\" id=\"si-check-bar\" style=\"width:0%\"></div></div>\n <span class=\"si-check-count\" id=\"si-check-count\">0 / 5 done</span>\n </div>\n <ul class=\"si-check-list\" id=\"si-check-list\">\n <li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"si-check\"> Confirm your deployment transport: stdio (local, no OAuth required) or SSE/HTTP (remote, OAuth 2.1 mandatory)</label></li>\n <li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"si-check\"> Verify your OAuth implementation uses Authorization Code + PKCE — MCP remote servers do not support the client credentials flow</label></li>\n <li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"si-check\"> Confirm HTTPS is enforced end-to-end — HTTP is only valid for stdio local transports, not remote server deployments</label></li>\n <li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"si-check\"> Audit your tool descriptions for untrusted input — tool descriptions are processed as instructions by the model, making them a prompt injection surface</label></li>\n <li class=\"si-check-item\"><label><input type=\"checkbox\" class=\"si-check\"> Measure your transport overhead: stdio vs SSE vs HTTP streaming each carry different per-invocation latency that compounds across a multi-tool session</label></li>\n </ul>\n <script>(function(){\n var checks=document.querySelectorAll('.si-checklist .si-check');\n var bar=document.getElementById('si-check-bar');\n var count=document.getElementById('si-check-count');\n var total=checks.length;\n function upd(){var done=document.querySelectorAll('.si-checklist .si-check:checked').length;bar.style.width=(done/total*100)+'%';count.textContent=done+' / '+total+' done';}\n checks.forEach(function(c){c.addEventListener('change',upd);});\n })();<\\/script>\n</div>`,\n },\n {\n id: 's4',\n num: '04',\n title: 'MCP vs. REST, HTTP,',\n titleItalic: 'and Everything Else.',\n deck: 'MCP does not replace REST. The three architectural scenarios where reaching for MCP creates more complexity than it removes are precisely the ones most articles use as MCP success cases.',\n cards: [\n {\n id: 'q4-1', num: '4.1',\n question: 'When to use MCP over API?',\n answer: 'Use MCP over a direct API when two conditions are true simultaneously: your agent needs to orchestrate multiple distinct tools within a single session, and portability across AI models matters. <strong>The decision flips to direct API the moment either condition drops: a single-tool integration has no session state to manage across calls, so MCP\\'s stateful session is overhead without benefit; a locked single-provider deployment has no portability requirement, so MCP\\'s cross-model compatibility is irrelevant.</strong> The latency constraint is the third variable: MCP adds tens to hundreds of milliseconds per invocation at the protocol layer — this overhead is architectural and cannot be mitigated by simplifying the MCP server implementation. If your integration must complete in under 200ms end-to-end, that overhead may consume your entire latency budget before the tool does any work. The correct three-question test: Does the agent need multiple tools in one session? Does the integration need to survive a model change? Can the use case tolerate MCP\\'s protocol overhead? All three \"yes\" answers justify MCP; any single \"no\" answer warrants a direct REST call.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q4-2', num: '4.2',\n question: 'Why use MCP instead of HTTP?',\n answer: 'Use MCP instead of direct HTTP when your agent needs capability negotiation — discovering at runtime what an external system can do rather than hardcoding the API surface at design time. <strong>Dynamic capability discovery is MCP\\'s primary architectural advantage over direct HTTP: an MCP server tells the client what tools it has; a REST API requires the client to know the endpoint structure before the first call.</strong> For integrations where the tool set is stable and known at design time, this advantage vanishes — and direct HTTP is simpler to implement, easier to debug, lower latency, and requires no protocol-layer auth overhead. The honest comparison: MCP earns its complexity overhead when you are building a system where the set of available tools may change without redeploying the client, or where the same client code must run against multiple different MCP servers. For everything else, a typed REST client with an OpenAPI spec gets you there faster with fewer moving parts.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q4-3', num: '4.3',\n question: 'Can MCP replace REST API?',\n answer: 'MCP cannot replace REST APIs — they solve different problems at different layers of the stack, and the scenarios where MCP is the right choice are a subset of what REST handles, not a superset. <strong>REST is a stateless request-response architecture for accessing and manipulating resources; MCP is a stateful session protocol for AI agents to discover and invoke external capabilities — the two solve adjacent, not identical, problems.</strong> Most MCP servers are built on top of REST APIs: the MCP server wraps an existing REST endpoint, translates the AI model\\'s structured tool call into an HTTP request, and returns the response. Replacing REST with MCP would be replacing the underlying API with the wrapper — the opposite of the correct architecture. The practical frame: REST is how systems talk to each other; MCP is how AI agents talk to systems. Both layers persist in a well-architected agentic system.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q4-4', num: '4.4',\n question: 'Is MCP faster than API?',\n answer: 'MCP is slower than a direct API call for the same operation — the protocol overhead adds tens to hundreds of milliseconds per invocation, and this is architectural, not a tuning problem. <strong>The latency penalty comes from three sources that cannot be optimized away: the capability negotiation handshake at session start, the JSON-RPC message serialization and deserialization on each tool call, and the transport layer overhead of the SSE or HTTP streaming connection.</strong> A direct REST API call bypasses all three overhead sources. The correct question is not \"is MCP faster\" but \"does MCP\\'s value — stateful sessions, capability negotiation, cross-model portability — justify the latency overhead for my use case.\" For batch processing, multi-step orchestration with long-running tools, and integrations where tool results feed into subsequent tool calls, the session value justifies the latency cost. For a single deterministic lookup that must complete in under 150ms, direct REST is the only viable choice.',\n },\n {\n id: 'q4-5', num: '4.5',\n question: 'Will MCP replace APIs?',\n answer: 'MCP will not replace APIs — it will add a protocol layer on top of APIs for agentic use cases, in the same way HTTP did not replace TCP but became the dominant application-layer protocol for web traffic. <strong>The analogy to HTTP is instructive: HTTP standardized how clients and servers communicate over the web without replacing the underlying TCP infrastructure; MCP standardizes how AI agents communicate with tool servers without replacing the underlying REST APIs those servers expose.</strong> The realistic 2026 scenario: most services will continue to expose REST APIs as their primary integration surface; a growing subset will additionally expose MCP servers for agentic clients that need dynamic tool discovery and stateful sessions. Both surfaces coexist in the same infrastructure. The practitioners who will be caught out are those treating \"API\" and \"MCP server\" as mutually exclusive choices rather than as different interfaces to the same underlying capabilities.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q4-6', num: '4.6',\n question: 'Is MCP the new HTTP?',\n answer: 'MCP is not the new HTTP — it is more accurately the new Language Server Protocol: a specialized standard for a specific client-server relationship (AI agent to tool server) that solves the N×M connector problem for one class of software interaction. <strong>HTTP is a general-purpose application-layer protocol used by virtually every networked application; MCP is purpose-built for agentic AI tool-calling, a narrower use case that shares HTTP as its transport but is not a replacement for it.</strong> The \"MCP is the new HTTP\" framing (cited by some practitioners) is a claim about trajectory and ubiquity, not architecture. For that claim to be true, MCP would need to become as universal as HTTP — meaning every networked service exposes an MCP interface alongside its HTTP interface. The 2026 roadmap suggests this is aspirational rather than imminent, and it is not a reason to adopt MCP for integrations where the HTTP layer is already sufficient.',\n source: 'https://modelcontextprotocol.io',\n },\n {\n id: 'q4-7', num: '4.7',\n question: 'Is MCP like Zapier?',\n answer: 'MCP and Zapier solve similar problems — connecting applications — but at fundamentally different layers: Zapier connects applications through predefined human-authored automation flows; MCP connects AI agents to tool servers so the agent can decide at runtime what to call and in what sequence. <strong>The defining difference is autonomy: a Zapier workflow is static — a human defined \"when X happens, do Y\"; an MCP integration is dynamic — the AI model reads available tools and decides what to call based on its reasoning about the current task.</strong> For teams currently using Zapier for deterministic automations, the decision calculus is straightforward: if the automation logic is well-defined and human-designed, Zapier or a direct API integration is simpler and cheaper to operate. MCP becomes relevant when you need the AI model to be the decision-maker about which tools to invoke — not when you need to connect two services and have already decided what should happen.',\n },\n {\n id: 'q4-8', num: '4.8',\n question: 'What is the difference between MCP and copilot?',\n answer: 'MCP is a protocol; Copilot (Microsoft) is an AI assistant product that has adopted MCP as one of its tool-calling mechanisms. <strong>The distinction matters because \"switching from MCP to Copilot\" is a category error — Microsoft Copilot uses MCP to connect to external tools, so Copilot and MCP are complementary, not competing choices.</strong> The relevant comparison for practitioners is between MCP (the open protocol) and Copilot\\'s native function calling (Microsoft\\'s proprietary tool-calling mechanism within their AI ecosystem). Native Copilot function calling is simpler to implement for Microsoft 365 integrations and carries no MCP protocol overhead — but it is locked to Copilot and does not port to Claude, ChatGPT, or other AI models. MCP-wrapped tools used by Copilot can also be used by any other MCP-compliant client. For organizations already standardized on Microsoft 365 and committed to Copilot, native function calling is the faster path; for organizations wanting to hedge across AI providers, MCP is the portability layer.',\n source: 'https://modelcontextprotocol.io/quickstart/user',\n },\n ],\n interactiveHtml: `<div class=\"si si-comparison\">\n <span class=\"si-heading\">Pick your integration pattern — see when MCP wins and when it doesn't</span>\n <div class=\"si-tabs\" role=\"tablist\">\n <button class=\"si-tab si-tab-active\" role=\"tab\" aria-selected=\"true\" data-panel=\"sip0\">MCP</button>\n <button class=\"si-tab\" role=\"tab\" aria-selected=\"false\" data-panel=\"sip1\">REST / HTTP</button>\n <button class=\"si-tab\" role=\"tab\" aria-selected=\"false\" data-panel=\"sip2\">Native Function Calling</button>\n </div>\n <div class=\"si-panels\">\n <div class=\"si-panel\" id=\"sip0\" role=\"tabpanel\">\n <p class=\"si-panel-title\">MCP — Model Context Protocol</p>\n <ul class=\"si-panel-pros\">\n <li>Stateful session with capability negotiation — the server advertises its tools dynamically</li>\n <li>Any compliant AI model can call any compliant MCP server without a custom integration</li>\n <li>Multi-tool orchestration across a single session without re-authentication</li>\n </ul>\n <ul class=\"si-panel-cons\">\n <li>Tens to hundreds of milliseconds overhead per tool invocation — architectural, not tunable</li>\n <li>OAuth 2.1 mandatory for remote deployments — significant implementation overhead for simple use cases</li>\n <li>Overkill when a single deterministic endpoint is all the agent needs</li>\n </ul>\n <p class=\"si-panel-verdict\">Use MCP when your agent orchestrates multiple tools across a stateful session and portability across AI models matters. Skip it when you need one fast, deterministic call.</p>\n </div>\n <div class=\"si-panel\" id=\"sip1\" role=\"tabpanel\" hidden>\n <p class=\"si-panel-title\">REST / HTTP — direct API</p>\n <ul class=\"si-panel-pros\">\n <li>Stateless — no session overhead, predictable latency</li>\n <li>No protocol layer between the agent and the endpoint — simpler debug surface</li>\n <li>Every existing API is already REST — zero migration cost</li>\n </ul>\n <ul class=\"si-panel-cons\">\n <li>Every AI model needs a custom integration — no portability across models</li>\n <li>No dynamic capability negotiation — the model must know the API surface at design time</li>\n <li>N×M custom connectors as you scale models and tools</li>\n </ul>\n <p class=\"si-panel-verdict\">Use direct REST when your integration is deterministic, latency-sensitive, or involves a single endpoint. REST is not going away — it solves a different layer of the problem.</p>\n </div>\n <div class=\"si-panel\" id=\"sip2\" role=\"tabpanel\" hidden>\n <p class=\"si-panel-title\">Native Function Calling (OpenAI / Copilot / Apple)</p>\n <ul class=\"si-panel-pros\">\n <li>Zero protocol overhead — function calling is part of the model API response schema</li>\n <li>Simplest path for single-provider deployments — no MCP server to host or maintain</li>\n <li>Full access to provider-specific features (streaming, parallelism) without a transport layer</li>\n </ul>\n <ul class=\"si-panel-cons\">\n <li>Provider lock-in — function schemas are not portable across OpenAI, Anthropic, and Google APIs</li>\n <li>No cross-model portability — a function defined for GPT-4o does not run on Claude without rewriting</li>\n <li>No multi-server session — each function call is stateless and provider-scoped</li>\n </ul>\n <p class=\"si-panel-verdict\">Use native function calling when locked to one AI provider and portability is not a current requirement. It is the fastest path and the highest lock-in path simultaneously.</p>\n </div>\n </div>\n <script>(function(){\n document.querySelectorAll('.si-comparison .si-tab').forEach(function(tab){\n tab.addEventListener('click',function(){\n document.querySelectorAll('.si-comparison .si-tab').forEach(function(t){t.classList.remove('si-tab-active');t.setAttribute('aria-selected','false');});\n document.querySelectorAll('.si-comparison .si-panel').forEach(function(p){p.hidden=true;});\n tab.classList.add('si-tab-active');tab.setAttribute('aria-selected','true');\n document.getElementById(tab.dataset.panel).hidden=false;\n });\n });\n })();<\\/script>\n</div>`,\n },\n {\n id: 's5',\n num: '05',\n title: 'When MCP Becomes',\n titleItalic: 'the Problem.',\n deck: 'The pitfall is not that MCP is complex — it\\'s that the complexity penalty is architectural and shows up after you\\'ve shipped. The postmark-mcp breach and SuperAGI\\'s production overload incidents both followed the same pattern: MCP added overhead to a use case that never needed a protocol layer.',\n cards: [\n {\n id: 'q5-1', num: '5.1',\n question: 'When not to use MCP?',\n answer: 'Skip MCP when any one of three conditions is true: your agent needs a single deterministic call to one endpoint; your integration is locked to one AI provider with no planned model migration; or your use case cannot tolerate tens to hundreds of milliseconds of added per-invocation latency. <strong>The conditions are independent — any single one is sufficient to disqualify MCP, because each one removes the specific value MCP provides: multi-tool session management, cross-model portability, and acceptable latency overhead respectively.</strong> The postmark-mcp breach illustrates the fourth disqualifying condition: an MCP integration where tool descriptions are sourced from untrusted external inputs without sanitization. Tool descriptions are processed as instructions by the AI model — an attacker who controls a tool description controls the model\\'s behavior within that session. The correct frame is not \"MCP is too complex\" but \"MCP adds overhead to a problem I don\\'t have\" — the complexity is justifiable when the problem is real, unjustifiable when it is not.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q5-2', num: '5.2',\n question: 'What are the pitfalls of MCP?',\n answer: 'The three verified production pitfalls of MCP are latency accumulation, auth under-implementation, and tool description injection — and all three share the same root cause: adopting MCP before understanding which parts of its architecture are the cost you pay and which are the value you receive. <strong>Latency accumulation is the most common: MCP\\'s per-invocation overhead compounds across multi-tool sessions — a five-tool session with 100ms overhead per call adds 500ms of protocol latency before any tool does substantive work, which is unacceptable in user-facing interactions.</strong> Auth under-implementation is the most dangerous: the postmark-mcp breach demonstrated that an MCP server with incomplete OAuth hardening can be silently hijacked — in that case, malicious BCC instructions were injected via tool descriptions, turning a legitimate email server into an exfiltration channel. Tool description injection is the most architecturally novel: because tool descriptions are processed as model instructions, any untrusted input that reaches a tool description field is a prompt injection vector, not a data sanitization problem.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q5-3', num: '5.3',\n question: 'When and when not to use MCP?',\n answer: 'Use MCP when multi-tool orchestration, cross-model portability, and tolerable latency all align; skip MCP when any one of those conditions is absent. <strong>The decision is binary for each condition: MCP\\'s stateful session adds no value without multiple tools; MCP\\'s cross-model portability is irrelevant if you are locked to one provider; and MCP\\'s latency overhead is architecturally non-negotiable — there is no MCP implementation that avoids it for latency-sensitive use cases.</strong> The practical test: if your integration can be described as \"call this one endpoint with these arguments and return the result,\" MCP is the wrong choice regardless of how many MCP servers exist in the ecosystem. If your integration requires the AI model to discover what tools are available, call multiple tools in sequence, and adapt its plan based on intermediate results — that is the use case MCP was built for. SuperAGI\\'s production overload incidents confirm that high-frequency MCP orchestration requires deliberate capacity planning; the protocol overhead is not free at scale.',\n },\n {\n id: 'q5-4', num: '5.4',\n question: 'Why is MCP inefficient?',\n answer: 'MCP is inefficient for use cases where its architecture provides no benefit — the inefficiency is not a defect in the protocol but a mismatch between the problem MCP solves and the problem being applied to it. <strong>The specific source of inefficiency is the stateful session model: establishing a session, negotiating capabilities, maintaining connection state, and handling JSON-RPC message framing all add overhead that a stateless direct REST call does not incur — and that overhead delivers no value when the integration is a single deterministic call that does not need session state or capability negotiation.</strong> The 2026-07-28 release candidate\\'s stateless protocol core addresses one dimension of this inefficiency — removing session headers so any server instance can handle any request — but does not eliminate the per-invocation JSON-RPC overhead. The practitioner frame: measure actual per-invocation latency in your environment before standardizing on MCP; the \"tens to hundreds of milliseconds\" figure is a reported range, and your actual overhead depends on transport, server implementation, and network topology.',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q5-5', num: '5.5',\n question: 'Why not use MCP?',\n answer: 'The three strongest reasons not to use MCP are: the integration does not require a stateful multi-tool session; the auth implementation complexity is unjustified for the use case; and the team has not audited tool description inputs for injection risk. <strong>The auth implementation reason is particularly underweighted in current discourse: implementing OAuth 2.1 with Authorization Code + PKCE correctly — including token storage, refresh flows, and the security hardening that prevents the postmark-mcp breach pattern — requires deliberate engineering investment that is not in scope for a simple tool integration.</strong> Teams that underestimate this and ship MCP servers with incomplete auth hardening produce a worse security posture than a well-implemented direct API with a static token, because MCP\\'s tool description attack surface is novel and not covered by standard web application security checklists. The correct question before adopting MCP is not \"can we make this work\" but \"does the value of stateful multi-tool orchestration and cross-model portability justify the implementation overhead of correct OAuth 2.1 and tool description sanitization.\"',\n source: 'https://modelcontextprotocol.io/specification/2025-11-25',\n },\n {\n id: 'q5-6', num: '5.6',\n question: 'When should we use MCP?',\n answer: 'Use MCP when three architectural conditions align: the agent needs to orchestrate multiple distinct tools within a single session, the integration must remain portable across AI models or providers, and per-invocation latency of tens to hundreds of milliseconds is acceptable for the use case. <strong>The highest-value MCP use case in 2026 is a multi-model agentic system where different AI models handle different sub-tasks and each needs access to a shared set of tools — MCP\\'s cross-model portability means the tool servers are written once and callable by Claude, ChatGPT, and Copilot without rewriting integration code for each provider.</strong> A secondary strong signal is development tooling: AI-assisted coding assistants that need access to file systems, terminals, databases, and documentation simultaneously are the use case that MCP\\'s stateful session and capability negotiation were designed for. Claude Desktop and Cursor\\'s MCP adoption are leading indicators of the use case MCP wins — not generic automation, but agentic development environments where tool orchestration complexity is genuinely high.',\n source: 'https://modelcontextprotocol.io/quickstart/user',\n },\n {\n id: 'q5-7', num: '5.7',\n question: 'What are the disadvantages of MPC?',\n answer: 'The architectural disadvantages of MCP (the Model Context Protocol, not MPC the economic term) are four: latency overhead, auth implementation cost, horizontal scaling constraints, and the tool description injection attack surface. <strong>The most practically significant disadvantage for production systems is that MCP\\'s current stable specification requires sticky routing for horizontal scaling — stateful sessions must land on the same server instance, which prevents standard load balancer behavior and requires session-aware routing infrastructure that teams building simple REST services have never needed before.</strong> The 2026-07-28 release candidate addresses this with a stateless protocol core, but that spec is a release candidate, not the stable version — teams building production systems on the current stable spec (2025-11-25) must design for sticky routing now and plan for migration when the new spec stabilizes. The tool description injection attack surface is the least-understood disadvantage: because AI models process tool descriptions as instructions, any input path that reaches tool description fields is a security boundary requiring the same sanitization discipline as SQL injection or XSS attack surfaces.',\n source: 'https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/',\n },\n ],\n interactiveHtml: `<div class=\"si si-decision\">\n <span class=\"si-heading\">Should you use MCP for this integration?</span>\n <div class=\"si-questions\">\n <div class=\"si-q\" data-q=\"0\">\n <p class=\"si-q-text\">Does your agent need to call multiple distinct tools — file system, database, web search, email — within a single session?</p>\n <div class=\"si-q-opts\">\n <label class=\"si-opt\"><input type=\"radio\" name=\"siq0\" value=\"1\"> Yes — multi-tool orchestration across one session</label>\n <label class=\"si-opt\"><input type=\"radio\" name=\"siq0\" value=\"0\"> No — one tool, one deterministic call</label>\n </div>\n </div>\n <div class=\"si-q\" data-q=\"1\">\n <p class=\"si-q-text\">Does the integration need to work across more than one AI model or provider (e.g. Claude today, GPT-5 next quarter)?</p>\n <div class=\"si-q-opts\">\n <label class=\"si-opt\"><input type=\"radio\" name=\"siq1\" value=\"1\"> Yes — model portability matters</label>\n <label class=\"si-opt\"><input type=\"radio\" name=\"siq1\" value=\"0\"> No — locked to one provider</label>\n </div>\n </div>\n <div class=\"si-q\" data-q=\"2\">\n <p class=\"si-q-text\">Can your integration tolerate tens to hundreds of milliseconds of added latency per tool invocation?</p>\n <div class=\"si-q-opts\">\n <label class=\"si-opt\"><input type=\"radio\" name=\"siq2\" value=\"1\"> Yes — latency is not the primary constraint</label>\n <label class=\"si-opt\"><input type=\"radio\" name=\"siq2\" value=\"0\"> No — latency-sensitive, every millisecond counts</label>\n </div>\n </div>\n </div>\n <div class=\"si-result\" id=\"si-result\" hidden>\n <p class=\"si-result-label\" id=\"si-result-label\"></p>\n <p class=\"si-result-body\" id=\"si-result-body\"></p>\n </div>\n <script>(function(){\n var results=[\n {label:'Skip MCP',body:'All three conditions suggest MCP adds overhead without payoff. A direct REST endpoint, a shell script, or native function calling will be faster, simpler to debug, and easier to maintain.'},\n {label:'Pilot before committing',body:'Mixed signals. At least one condition favors MCP but at least one is a red flag. Run a time-boxed pilot — measure actual per-invocation latency overhead before standardizing.'},\n {label:'MCP is a strong fit',body:'Multi-tool orchestration, model portability, and tolerable latency all align. Build the OAuth 2.1 layer properly from the start — the postmark-mcp breach pattern comes from skipping auth hardening under shipping pressure.'}\n ];\n function check(){\n var score=0,answered=0;\n for(var i=0;i<3;i++){var r=document.querySelector('input[name=\"siq'+i+'\"]:checked');if(r){score+=+r.value;answered++;}}\n if(answered<3)return;\n var res=score>=2?results[2]:score===1?results[1]:results[0];\n var el=document.getElementById('si-result');\n document.getElementById('si-result-label').textContent=res.label;\n document.getElementById('si-result-body').textContent=res.body;\n el.hidden=false;\n }\n document.querySelectorAll('.si-decision input[type=\"radio\"]').forEach(function(r){r.addEventListener('change',check);});\n })();<\\/script>\n</div>`,\n },\n ],\n },\n]","import type { BlogPost, BlogPostSection, BlogPostCard } from './types.js'\n\nfunction faqJsonLd(post: BlogPost): string {\n const firstFive = post.sections.flatMap(s => s.cards).slice(0, 5)\n const items = firstFive.map(c => `{\n \"@type\": \"Question\",\n \"name\": ${JSON.stringify(c.question)},\n \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": ${JSON.stringify(c.answer.replace(/<[^>]+>/g, ''))} }\n }`)\n return `{\n \"@context\": \"https://schema.org\",\n \"@type\": \"FAQPage\",\n \"mainEntity\": [${items.join(',')}]\n }`\n}\n\nfunction articleJsonLd(post: BlogPost): string {\n const canonical = post.canonicalUrl ?? `https://mcpscraper.dev/blog/${post.slug}/`\n return `{\n \"@context\": \"https://schema.org\",\n \"@type\": \"Article\",\n \"headline\": ${JSON.stringify(post.title)},\n \"description\": ${JSON.stringify(post.description)},\n \"author\": { \"@type\": \"Organization\", \"name\": \"MCP Scraper\" },\n \"publisher\": { \"@type\": \"Organization\", \"name\": \"MCP Scraper\", \"url\": \"https://mcpscraper.dev\" },\n \"datePublished\": ${JSON.stringify(post.publishedAt)},\n \"dateModified\": ${JSON.stringify(post.updatedAt ?? post.publishedAt)},\n \"url\": ${JSON.stringify(canonical)}\n }`\n}\n\nfunction breadcrumbJsonLd(post: BlogPost): string {\n const canonical = post.canonicalUrl ?? `https://mcpscraper.dev/blog/${post.slug}/`\n return `{\n \"@context\": \"https://schema.org\",\n \"@type\": \"BreadcrumbList\",\n \"itemListElement\": [\n { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https://mcpscraper.dev\" },\n { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Blog\", \"item\": \"https://mcpscraper.dev/blog/\" },\n { \"@type\": \"ListItem\", \"position\": 3, \"name\": ${JSON.stringify(post.title)}, \"item\": ${JSON.stringify(canonical)} }\n ]\n }`\n}\n\nfunction renderCard(card: BlogPostCard): string {\n return `<div class=\"card\" id=\"${card.id}\">\n <button class=\"card-trigger\" aria-expanded=\"false\" aria-controls=\"body-${card.id}\">\n <span class=\"card-num\">${card.num}</span>\n <span class=\"card-question\">${card.question}</span>\n <span class=\"card-chevron\" aria-hidden=\"true\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\"><path d=\"M4 6.5 9 11.5 14 6.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </span>\n </button>\n <div class=\"card-body\" id=\"body-${card.id}\" hidden>\n <p>${card.answer}</p>\n </div>\n </div>`\n}\n\nfunction renderSection(section: BlogPostSection, tocTitle: string): string {\n const calloutHtml = section.callout ? `\n <div class=\"callout\">\n <p class=\"callout-eyebrow\">${section.callout.eyebrow}</p>\n <h3 class=\"callout-heading\">${section.callout.heading}</h3>\n <p class=\"callout-body\">${section.callout.body}</p>\n </div>` : ''\n\n const cardsHtml = section.cards.map(renderCard).join('\\n ')\n\n const interactiveHtml = section.interactiveHtml ? `\\n <div class=\"section-interactive\">${section.interactiveHtml}</div>` : ''\n\n return `<section class=\"post-section\" id=\"${section.id}\" aria-label=\"${tocTitle}\">\n <div class=\"section-header\">\n <span class=\"section-num\">${section.num}</span>\n <h2 class=\"section-title\">${section.title}${section.titleItalic ? ` <em>${section.titleItalic}</em>` : ''}</h2>\n <p class=\"section-deck\">${section.deck}</p>\n </div>${calloutHtml}\n <div class=\"card-stack\">\n ${cardsHtml}\n </div>${interactiveHtml}\n </section>`\n}\n\nfunction tocItem(section: BlogPostSection): string {\n const label = `${section.title}${section.titleItalic ? ' ' + section.titleItalic : ''}`\n const subItems = section.cards.map(card => `<li>\n <a href=\"#${card.id}\" class=\"toc-sub-link\" data-toc=\"${card.id}\">\n <span class=\"toc-sub-num\">${card.num}</span>\n <span class=\"toc-sub-label\">${card.question}</span>\n </a>\n </li>`).join('\\n ')\n return `<li>\n <a href=\"#${section.id}\" class=\"toc-link\" data-toc=\"${section.id}\">\n <span class=\"toc-num\">${section.num}</span>\n <span class=\"toc-label\">${label}</span>\n </a>\n <ul class=\"toc-sub-list\">\n ${subItems}\n </ul>\n </li>`\n}\n\nconst CSS = `\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Trial-VF.woff2\") format(\"woff2\");\n font-weight: 100 900;\n font-style: normal;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Light-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 300;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Regular-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 400;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Medium-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 500;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Bold-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 700;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Black-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 900;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa Display\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Black-Trial.woff2\") format(\"woff2\");\n font-weight: 900;\n font-style: normal;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa Display\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Black-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 900;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa Display\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Bold-Trial.woff2\") format(\"woff2\");\n font-weight: 700;\n font-style: normal;\n font-display: swap;\n }\n\n :root {\n --brand: #d06045;\n --brand-dark: #b34e36;\n --ink: #1a1a1a;\n --ink-2: #444;\n --ink-3: #777;\n --rule: #e8e4de;\n --bg: #faf9f7;\n --bg-card: #fff;\n --rail-w: 260px;\n --content-max: 760px;\n --gap: 2rem;\n --radius: 8px;\n }\n\n html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6,\n p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del,\n dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt,\n var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label,\n legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside,\n canvas, details, embed, figure, figcaption, footer, header, hgroup, menu,\n nav, output, ruby, section, summary, time, mark, audio, video, button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n body {\n font-family: \"GT Flexa\", system-ui, sans-serif;\n font-weight: 400;\n font-size: 17px;\n line-height: 1.7;\n color: var(--ink);\n background: var(--bg);\n -webkit-font-smoothing: antialiased;\n }\n\n a { color: inherit; }\n\n .read-progress {\n position: fixed;\n top: 0; left: 0;\n width: 0%;\n height: 3px;\n background: var(--brand);\n z-index: 1000;\n transition: width 0.1s linear;\n }\n\n .site-nav {\n position: sticky;\n top: 0;\n z-index: 100;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 2rem;\n background: rgba(250,249,247,0.92);\n backdrop-filter: blur(8px);\n border-bottom: 1px solid var(--rule);\n }\n .site-nav .logo {\n font-family: \"GT Flexa Display\", sans-serif;\n font-weight: 900;\n font-size: 1.1rem;\n letter-spacing: -0.02em;\n text-decoration: none;\n color: var(--ink);\n }\n .site-nav .logo span { color: var(--brand); }\n .nav-blog-link {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 500;\n font-size: 0.88rem;\n color: var(--ink-3);\n text-decoration: none;\n transition: color 0.15s;\n }\n .nav-blog-link:hover { color: var(--ink); }\n .nav-right { display: flex; align-items: center; gap: 1.25rem; }\n .nav-cta {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.5rem 1.1rem;\n background: var(--brand);\n color: #fff;\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 600;\n font-size: 0.85rem;\n border-radius: 6px;\n text-decoration: none;\n transition: background 0.15s;\n }\n .nav-cta:hover { background: var(--brand-dark); }\n\n .hero {\n padding: 4.5rem 2rem 3rem;\n max-width: calc(var(--rail-w) + var(--content-max) + 4rem + var(--gap));\n margin: 0 auto;\n }\n .hero-meta {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n font-size: 0.82rem;\n font-weight: 500;\n color: var(--ink-3);\n letter-spacing: 0.04em;\n text-transform: uppercase;\n }\n .hero-meta .badge {\n background: var(--brand);\n color: #fff;\n padding: 0.2em 0.7em;\n border-radius: 4px;\n font-size: 0.75rem;\n letter-spacing: 0.05em;\n }\n .hero-meta .sep { opacity: 0.4; }\n .hero-title {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 900;\n font-size: clamp(2.6rem, 6vw, 4.4rem);\n line-height: 1.0;\n letter-spacing: -0.03em;\n color: var(--ink);\n margin-bottom: 1.25rem;\n }\n .hero-title em {\n font-style: italic;\n color: var(--brand);\n }\n .hero-deck {\n font-size: 1.15rem;\n font-style: italic;\n color: var(--ink-2);\n line-height: 1.6;\n max-width: 620px;\n margin-bottom: 2rem;\n }\n .stat-strip {\n display: flex;\n gap: 2rem;\n padding: 1.25rem 1.5rem;\n background: var(--bg-card);\n border: 1px solid var(--rule);\n border-radius: var(--radius);\n flex-wrap: wrap;\n }\n .stat { display: flex; flex-direction: column; gap: 0.1rem; }\n .stat-value {\n font-family: \"GT Flexa Display\", sans-serif;\n font-weight: 900;\n font-size: 1.6rem;\n line-height: 1;\n color: var(--brand);\n letter-spacing: -0.02em;\n }\n .stat-label {\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--ink-3);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .post-layout {\n display: grid;\n grid-template-columns: var(--rail-w) 1fr;\n gap: var(--gap);\n max-width: calc(var(--rail-w) + var(--content-max) + 4rem + var(--gap));\n margin: 0 auto;\n padding: 0 2rem 6rem;\n align-items: start;\n }\n\n .toc-rail {\n position: sticky;\n top: 68px;\n padding-top: 2rem;\n }\n .toc-label-text {\n font-size: 0.7rem;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ink-3);\n margin-bottom: 0.75rem;\n padding-left: 0.5rem;\n }\n .toc-list { list-style: none; }\n .toc-list li { margin-bottom: 0.1rem; }\n .toc-link {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n padding: 0.45rem 0.5rem;\n border-radius: 5px;\n text-decoration: none;\n font-size: 0.82rem;\n font-weight: 450;\n color: var(--ink-3);\n transition: color 0.15s, background 0.15s;\n }\n .toc-link:hover { color: var(--ink); background: var(--rule); }\n .toc-link.active { color: var(--brand); font-weight: 600; }\n .toc-num {\n font-family: \"GT Flexa Display\", sans-serif;\n font-weight: 900;\n font-size: 0.7rem;\n color: var(--brand);\n opacity: 0.6;\n flex-shrink: 0;\n }\n .toc-link.active .toc-num { opacity: 1; }\n .toc-sub-list { list-style: none; padding-left: 1.25rem; margin: 0.15rem 0 0.4rem; }\n .toc-sub-list li { margin-bottom: 0; }\n .toc-sub-link {\n display: flex;\n align-items: baseline;\n gap: 0.35rem;\n padding: 0.28rem 0.5rem;\n border-radius: 4px;\n text-decoration: none;\n font-size: 0.76rem;\n font-weight: 400;\n color: var(--ink-3);\n opacity: 0.7;\n transition: color 0.15s, background 0.15s, opacity 0.15s;\n line-height: 1.35;\n }\n .toc-sub-link:hover { color: var(--ink); background: var(--rule); opacity: 1; }\n .toc-sub-link.active { color: var(--brand); font-weight: 500; opacity: 1; }\n .toc-sub-num {\n font-size: 0.65rem;\n color: var(--brand);\n opacity: 0.5;\n flex-shrink: 0;\n }\n .toc-sub-link.active .toc-sub-num { opacity: 1; }\n .toc-sub-label { min-width: 0; }\n\n .post-content { min-width: 0; padding-top: 2rem; }\n\n .post-section { margin-bottom: 4rem; }\n .section-header {\n padding-bottom: 1.5rem;\n margin-bottom: 1.5rem;\n border-bottom: 2px solid var(--rule);\n }\n .section-num {\n display: block;\n font-family: \"GT Flexa Display\", sans-serif;\n font-weight: 900;\n font-size: 0.7rem;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: var(--brand);\n margin-bottom: 0.4rem;\n }\n .section-title {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 900;\n font-size: clamp(1.7rem, 3.5vw, 2.2rem);\n line-height: 1.1;\n letter-spacing: -0.025em;\n color: var(--ink);\n margin-bottom: 0.6rem;\n }\n .section-title em {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 900;\n font-style: italic;\n color: var(--brand);\n }\n .section-deck {\n font-size: 1rem;\n font-style: italic;\n color: var(--ink-2);\n line-height: 1.5;\n }\n\n .callout {\n background: var(--bg-card);\n border: 1px solid var(--rule);\n border-left: 3px solid var(--brand);\n border-radius: var(--radius);\n padding: 1.25rem 1.5rem;\n margin-bottom: 1.5rem;\n }\n .callout-eyebrow {\n font-size: 0.72rem;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--brand);\n margin-bottom: 0.35rem;\n }\n .callout-heading {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 900;\n font-size: 1.05rem;\n letter-spacing: -0.01em;\n color: var(--ink);\n margin-bottom: 0.5rem;\n }\n .callout-body {\n font-size: 0.9rem;\n color: var(--ink-2);\n line-height: 1.6;\n }\n\n .section-interactive { margin-top: 2rem; }\n\n .si {\n background: var(--bg-card);\n border: 1px solid var(--rule);\n border-radius: var(--radius);\n padding: 1.5rem;\n }\n .si-heading {\n display: block;\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 700;\n font-size: 0.82rem;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--brand);\n margin-bottom: 1.25rem;\n }\n\n .si-calc-inputs { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1.25rem; }\n .si-label { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.88rem; font-weight: 600; color: var(--ink-2); }\n .si-slider-row { display: flex; align-items: center; gap: 0.75rem; }\n .si-range { flex: 1; accent-color: var(--brand); cursor: pointer; }\n .si-range-val { font-family: \"GT Flexa Display\", sans-serif; font-weight: 900; font-size: 1rem; color: var(--brand); min-width: 2.5rem; text-align: right; }\n .si-calc-output { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }\n .si-stat { background: var(--bg); border-radius: 6px; padding: 1rem; text-align: center; }\n .si-stat-accent { background: color-mix(in srgb, var(--brand) 8%, var(--bg)); }\n .si-stat-num { display: block; font-family: \"GT Flexa Display\", sans-serif; font-weight: 900; font-size: 2rem; color: var(--ink); line-height: 1; margin-bottom: 0.35rem; }\n .si-stat-accent .si-stat-num { color: var(--brand); }\n .si-stat-label { font-size: 0.78rem; color: var(--ink-3); font-weight: 500; }\n\n .si-questions { display: flex; flex-direction: column; gap: 1.25rem; margin-bottom: 1rem; }\n .si-q-text { font-size: 0.9rem; font-weight: 600; color: var(--ink); margin-bottom: 0.5rem; }\n .si-q-opts { display: flex; flex-direction: column; gap: 0.4rem; }\n .si-opt { display: flex; align-items: center; gap: 0.5rem; font-size: 0.88rem; color: var(--ink-2); cursor: pointer; }\n .si-opt input { accent-color: var(--brand); }\n .si-result { background: color-mix(in srgb, var(--brand) 6%, var(--bg)); border: 1px solid color-mix(in srgb, var(--brand) 30%, var(--rule)); border-radius: 6px; padding: 1rem 1.25rem; margin-top: 1rem; }\n .si-result-label { font-weight: 700; font-size: 0.9rem; color: var(--brand); margin-bottom: 0.35rem; }\n .si-result-body { font-size: 0.88rem; color: var(--ink-2); line-height: 1.55; }\n\n .si-demo-input-row { display: flex; gap: 0.5rem; margin-bottom: 0.75rem; }\n .si-demo-input { flex: 1; border: 1px solid var(--rule); border-radius: 6px; padding: 0.6rem 0.75rem; font-family: inherit; font-size: 0.9rem; background: var(--bg); color: var(--ink); outline: none; }\n .si-demo-input:focus { border-color: var(--brand); }\n .si-demo-btn { padding: 0.6rem 1.1rem; background: var(--brand); color: #fff; border: none; border-radius: 6px; font-family: inherit; font-weight: 600; font-size: 0.85rem; cursor: pointer; white-space: nowrap; transition: background 0.15s; }\n .si-demo-btn:hover:not(:disabled) { background: var(--brand-dark); }\n .si-demo-btn:disabled { opacity: 0.6; cursor: default; }\n .si-demo-status { font-size: 0.85rem; color: var(--ink-3); padding: 0.5rem 0; }\n .si-demo-results { list-style: none; display: flex; flex-direction: column; gap: 0.35rem; }\n .si-demo-item { font-size: 0.88rem; color: var(--ink-2); padding: 0.5rem 0.75rem; background: var(--bg); border-radius: 5px; border: 1px solid var(--rule); }\n\n .si-tabs { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-bottom: 1rem; }\n .si-tab { padding: 0.45rem 0.9rem; border: 1px solid var(--rule); border-radius: 20px; background: none; font-family: inherit; font-size: 0.82rem; font-weight: 600; color: var(--ink-3); cursor: pointer; transition: all 0.15s; }\n .si-tab:hover { color: var(--ink); border-color: var(--ink-3); }\n .si-tab-active { background: var(--brand); color: #fff; border-color: var(--brand); }\n .si-panel-title { font-weight: 700; font-size: 0.95rem; color: var(--ink); margin-bottom: 0.75rem; }\n .si-panel-pros, .si-panel-cons { list-style: none; display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.75rem; }\n .si-panel-pros li::before { content: '✓ '; color: #3a9e5f; font-weight: 700; }\n .si-panel-cons li::before { content: '✗ '; color: #c0392b; font-weight: 700; }\n .si-panel-pros li, .si-panel-cons li { font-size: 0.88rem; color: var(--ink-2); }\n .si-panel-verdict { font-size: 0.85rem; font-style: italic; color: var(--ink-3); border-top: 1px solid var(--rule); padding-top: 0.75rem; }\n\n .si-check-progress-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem; }\n .si-check-bar-wrap { flex: 1; height: 4px; background: var(--rule); border-radius: 2px; overflow: hidden; }\n .si-check-bar { height: 100%; background: var(--brand); border-radius: 2px; transition: width 0.3s; }\n .si-check-count { font-size: 0.78rem; font-weight: 600; color: var(--ink-3); white-space: nowrap; }\n .si-check-list { list-style: none; display: flex; flex-direction: column; gap: 0.5rem; }\n .si-check-item label { display: flex; align-items: flex-start; gap: 0.6rem; font-size: 0.9rem; color: var(--ink-2); cursor: pointer; line-height: 1.45; }\n .si-check-item input[type=\"checkbox\"] { accent-color: var(--brand); margin-top: 0.2em; flex-shrink: 0; }\n\n .si-eco-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 0.5rem; margin-bottom: 0.75rem; }\n .si-eco-item { display: flex; flex-direction: column; align-items: flex-start; gap: 0.35rem; padding: 0.75rem; border: 1px solid var(--rule); border-radius: 6px; background: var(--bg); cursor: pointer; text-align: left; font-family: inherit; transition: border-color 0.15s; }\n .si-eco-item:hover { border-color: var(--ink-3); }\n .si-eco-active { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 4%, var(--bg)); }\n .si-eco-name { font-weight: 700; font-size: 0.88rem; color: var(--ink); }\n .si-eco-badge { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; padding: 0.15rem 0.45rem; border-radius: 10px; }\n .si-eco-badge-released { background: #d4edda; color: #256736; }\n .si-eco-badge-beta { background: #fff3cd; color: #856404; }\n .si-eco-badge-announced { background: #d1ecf1; color: #1a5e6e; }\n .si-eco-detail { padding: 0.85rem 1rem; background: var(--bg); border: 1px solid var(--rule); border-radius: 6px; font-size: 0.88rem; color: var(--ink-2); line-height: 1.55; }\n\n .si-quiz-q { margin-bottom: 1rem; }\n .si-quiz-q-text { font-weight: 600; font-size: 0.92rem; color: var(--ink); margin-bottom: 0.75rem; }\n .si-quiz-opts { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.75rem; }\n .si-quiz-opt { padding: 0.55rem 0.9rem; border: 1px solid var(--rule); border-radius: 6px; background: var(--bg); font-family: inherit; font-size: 0.88rem; color: var(--ink-2); cursor: pointer; text-align: left; transition: border-color 0.15s; }\n .si-quiz-opt:hover:not(:disabled) { border-color: var(--ink-3); }\n .si-quiz-correct { background: #d4edda; border-color: #3a9e5f; color: #256736; }\n .si-quiz-wrong { background: #fde8e8; border-color: #c0392b; color: #c0392b; }\n .si-quiz-explanation { font-size: 0.85rem; color: var(--ink-2); font-style: italic; border-left: 2px solid var(--brand); padding-left: 0.75rem; line-height: 1.55; }\n\n .si-codegen-inputs { display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1rem; }\n .si-text-input { width: 100%; border: 1px solid var(--rule); border-radius: 6px; padding: 0.55rem 0.75rem; font-family: \"GT Flexa\", monospace; font-size: 0.88rem; background: var(--bg); color: var(--ink); outline: none; }\n .si-text-input:focus { border-color: var(--brand); }\n .si-codegen-output-wrap { position: relative; }\n .si-codegen-pre { background: #1a1a1a; border-radius: 6px; padding: 1rem 1.25rem; overflow-x: auto; }\n .si-codegen-pre code { font-family: \"SF Mono\", \"Fira Code\", monospace; font-size: 0.82rem; color: #e8e8e8; white-space: pre; }\n .si-copy-btn { position: absolute; top: 0.5rem; right: 0.5rem; padding: 0.3rem 0.65rem; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.15); border-radius: 4px; color: #ccc; font-size: 0.75rem; font-family: inherit; cursor: pointer; transition: background 0.15s; }\n .si-copy-btn:hover { background: rgba(255,255,255,0.2); }\n\n .si-diagram-wrap { position: relative; }\n .si-svg { width: 100%; height: auto; }\n .si-hotspot { fill: var(--brand); opacity: 0.15; cursor: pointer; transition: opacity 0.15s; }\n .si-hotspot:hover { opacity: 0.35; }\n .si-diagram-detail { margin-top: 0.75rem; padding: 0.85rem 1rem; background: var(--bg); border: 1px solid var(--rule); border-radius: 6px; font-size: 0.88rem; color: var(--ink-2); }\n .si-diagram-detail strong { display: block; color: var(--ink); margin-bottom: 0.35rem; font-size: 0.9rem; }\n\n .si-timeline-track { position: relative; padding-left: 1.5rem; }\n .si-timeline-track::before { content: ''; position: absolute; left: 0.45rem; top: 0.6rem; bottom: 0.6rem; width: 2px; background: var(--rule); }\n .si-timeline-event { position: relative; display: grid; grid-template-columns: 80px 1rem 1fr; gap: 0 0.75rem; align-items: start; padding-bottom: 1.25rem; }\n .si-tl-date { font-size: 0.75rem; font-weight: 700; color: var(--ink-3); letter-spacing: 0.04em; padding-top: 0.15rem; text-align: right; }\n .si-tl-dot { width: 0.65rem; height: 0.65rem; background: var(--brand); border-radius: 50%; margin-top: 0.3rem; flex-shrink: 0; }\n .si-tl-title { display: block; font-weight: 700; font-size: 0.9rem; color: var(--ink); margin-bottom: 0.3rem; }\n .si-tl-body { font-size: 0.85rem; color: var(--ink-2); line-height: 1.5; }\n\n .si-ba-toggle { display: flex; gap: 0; margin-bottom: 0.75rem; border: 1px solid var(--rule); border-radius: 6px; overflow: hidden; width: fit-content; }\n .si-ba-btn { padding: 0.45rem 1rem; background: none; border: none; font-family: inherit; font-size: 0.85rem; font-weight: 600; color: var(--ink-3); cursor: pointer; transition: all 0.15s; }\n .si-ba-active { background: var(--brand); color: #fff; }\n .si-ba-pane { background: #1a1a1a; border-radius: 6px; padding: 1rem 1.25rem; overflow-x: auto; margin: 0; }\n .si-ba-pane code { font-family: \"SF Mono\", \"Fira Code\", monospace; font-size: 0.82rem; color: #e8e8e8; white-space: pre; }\n\n .si-persona-selector { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-bottom: 1rem; }\n .si-persona-btn { padding: 0.45rem 0.9rem; border: 1px solid var(--rule); border-radius: 20px; background: none; font-family: inherit; font-size: 0.82rem; font-weight: 600; color: var(--ink-3); cursor: pointer; transition: all 0.15s; }\n .si-persona-btn:hover { color: var(--ink); border-color: var(--ink-3); }\n .si-persona-active { background: var(--brand); color: #fff; border-color: var(--brand); }\n .si-persona-panel { font-size: 0.9rem; color: var(--ink-2); line-height: 1.6; }\n .si-persona-panel p { margin-bottom: 0.6rem; }\n .si-persona-panel ul { padding-left: 1.2rem; margin-bottom: 0.6rem; }\n .si-persona-panel li { margin-bottom: 0.3rem; }\n\n @media (max-width: 600px) {\n .si-calc-output { grid-template-columns: 1fr; }\n .si-eco-grid { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); }\n .si-timeline-event { grid-template-columns: 60px 1rem 1fr; }\n .si-demo-input-row { flex-direction: column; }\n }\n .card-stack { display: flex; flex-direction: column; gap: 0.5rem; }\n .card {\n background: var(--bg-card);\n border: 1px solid var(--rule);\n border-radius: var(--radius);\n overflow: hidden;\n }\n .card-trigger {\n width: 100%;\n display: grid;\n grid-template-columns: 3rem 1fr 1.5rem;\n align-items: start;\n gap: 0.5rem;\n padding: 1rem 1rem 1rem 0.75rem;\n background: none;\n border: none;\n cursor: pointer;\n text-align: left;\n font-family: inherit;\n transition: background 0.12s;\n }\n .card-trigger:hover { background: var(--bg); }\n .card-trigger[aria-expanded=\"true\"] { background: var(--bg); }\n .card-num {\n font-family: \"GT Flexa Display\", sans-serif;\n font-weight: 900;\n font-size: 0.7rem;\n color: var(--brand);\n opacity: 0.55;\n padding-top: 0.15em;\n white-space: nowrap;\n }\n .card-trigger[aria-expanded=\"true\"] .card-num { opacity: 1; }\n .card-question {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 700;\n font-size: 0.95rem;\n line-height: 1.35;\n letter-spacing: -0.01em;\n color: var(--ink);\n }\n .card-chevron {\n color: var(--ink-3);\n transition: transform 0.2s;\n padding-top: 0.1em;\n }\n .card-trigger[aria-expanded=\"true\"] .card-chevron { transform: rotate(180deg); }\n .card-body {\n padding: 0 1rem 1rem calc(0.75rem + 3rem + 0.5rem);\n font-size: 0.93rem;\n line-height: 1.7;\n color: var(--ink-2);\n }\n .card-body strong { color: var(--ink); font-weight: 600; }\n .card-body em { font-style: italic; }\n\n .cta-block {\n margin-top: 5rem;\n padding: 3rem;\n background: var(--ink);\n border-radius: 12px;\n color: #fff;\n }\n .cta-heading {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 900;\n font-size: clamp(1.8rem, 4vw, 2.6rem);\n letter-spacing: -0.03em;\n line-height: 1.05;\n margin-bottom: 0.9rem;\n }\n .cta-heading em { font-style: italic; color: var(--brand); }\n .cta-body {\n font-size: 1rem;\n line-height: 1.65;\n color: rgba(255,255,255,0.72);\n max-width: 520px;\n margin-bottom: 1.75rem;\n }\n .cta-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n padding: 0.8rem 1.6rem;\n background: var(--brand);\n color: #fff;\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 600;\n font-size: 0.95rem;\n border-radius: 7px;\n text-decoration: none;\n transition: background 0.15s;\n }\n .cta-btn:hover { background: var(--brand-dark); }\n\n .site-footer {\n border-top: 1px solid var(--rule);\n padding: 2rem;\n text-align: center;\n font-size: 0.82rem;\n color: var(--ink-3);\n }\n\n .mob-toc { display: none; margin: 0 1.25rem 1.5rem; }\n .mob-toc-toggle {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n background: var(--bg-card);\n border: 1px solid var(--rule);\n border-radius: var(--radius);\n font-family: inherit;\n font-size: 0.8rem;\n font-weight: 600;\n color: var(--ink-2);\n cursor: pointer;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n }\n .mob-toc-toggle svg { transition: transform 0.2s; flex-shrink: 0; }\n .mob-toc-toggle[aria-expanded=\"true\"] svg { transform: rotate(180deg); }\n .mob-toc-panel {\n border: 1px solid var(--rule);\n border-top: none;\n border-radius: 0 0 var(--radius) var(--radius);\n background: var(--bg-card);\n padding: 0.4rem;\n }\n .mob-toc-panel[hidden] { display: none; }\n\n @media (max-width: 860px) {\n .post-layout {\n grid-template-columns: 1fr;\n padding: 0 1.25rem 4rem;\n }\n .toc-rail { display: none; }\n .mob-toc { display: block; }\n .hero { padding: 3rem 1.25rem 2rem; }\n .hero-title { font-size: clamp(2rem, 8vw, 3rem); }\n .hero-meta { flex-wrap: wrap; row-gap: 0.35rem; }\n .site-nav { padding: 0.75rem 1.25rem; }\n .cta-block { padding: 2rem 1.5rem; }\n .stat-strip { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }\n }\n`\n\nexport function renderPost(post: BlogPost): string {\n const canonical = post.canonicalUrl ?? `https://mcpscraper.dev/blog/${post.slug}/`\n const ogImage = post.ogImage ?? `https://mcpscraper.dev/blog/${post.slug}/og.png`\n const tocItems = post.sections.map(tocItem).join('\\n ')\n const sectionsHtml = post.sections.map(s => {\n const label = `${s.title}${s.titleItalic ? ' ' + s.titleItalic : ''}`\n return renderSection(s, label)\n }).join('\\n\\n ')\n\n const statsHtml = post.stats.map(s =>\n `<div class=\"stat\"><span class=\"stat-value\">${s.value}</span><span class=\"stat-label\">${s.label}</span></div>`\n ).join('\\n ')\n\n const year = new Date().getFullYear()\n const pubDate = new Date(post.publishedAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${post.title} | MCP Scraper</title>\n <meta name=\"description\" content=\"${post.description}\">\n <link rel=\"canonical\" href=\"${canonical}\">\n\n <meta property=\"og:type\" content=\"article\">\n <meta property=\"og:title\" content=\"${post.title}\">\n <meta property=\"og:description\" content=\"${post.description}\">\n <meta property=\"og:url\" content=\"${canonical}\">\n <meta property=\"og:image\" content=\"${ogImage}\">\n <meta property=\"og:site_name\" content=\"MCP Scraper\">\n <meta property=\"article:published_time\" content=\"${post.publishedAt}\">\n ${post.updatedAt ? `<meta property=\"article:modified_time\" content=\"${post.updatedAt}\">` : ''}\n ${post.tags.map(t => `<meta property=\"article:tag\" content=\"${t}\">`).join('\\n ')}\n\n <meta name=\"twitter:card\" content=\"summary_large_image\">\n <meta name=\"twitter:title\" content=\"${post.title}\">\n <meta name=\"twitter:description\" content=\"${post.description}\">\n <meta name=\"twitter:image\" content=\"${ogImage}\">\n\n <script type=\"application/ld+json\">${articleJsonLd(post)}</script>\n <script type=\"application/ld+json\">${faqJsonLd(post)}</script>\n <script type=\"application/ld+json\">${breadcrumbJsonLd(post)}</script>\n\n <style>${CSS}</style>\n</head>\n<body>\n <div class=\"read-progress\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"0\"></div>\n\n <nav class=\"site-nav\" aria-label=\"Site navigation\">\n <a href=\"/\" class=\"logo\">MCP<span>.</span>Scraper</a>\n <div class=\"nav-right\">\n <a href=\"/blog/\" class=\"nav-blog-link\">Blog</a>\n <a href=\"/#pricing\" class=\"nav-cta\">Get API Access</a>\n </div>\n </nav>\n\n <header class=\"hero\">\n <div class=\"hero-meta\">\n ${post.fieldGuideLabel ? `<span class=\"badge\">${post.fieldGuideLabel}</span><span class=\"sep\">·</span>` : ''}\n <span>${post.category}</span>\n <span class=\"sep\">·</span>\n <span>${post.badge}</span>\n <span class=\"sep\">·</span>\n <time datetime=\"${post.publishedAt}\">${pubDate}</time>\n </div>\n <h1 class=\"hero-title\">${post.titleDisplay} <em>${post.titleDisplayItalic}</em></h1>\n <p class=\"hero-deck\">${post.deck}</p>\n <div class=\"stat-strip\">\n ${statsHtml}\n </div>\n </header>\n\n <nav class=\"mob-toc\" aria-label=\"Mobile table of contents\">\n <button class=\"mob-toc-toggle\" aria-expanded=\"false\" aria-controls=\"mob-toc-panel\">\n Contents\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\"><path d=\"M4 6 8 10 12 6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </button>\n <div class=\"mob-toc-panel\" id=\"mob-toc-panel\" hidden>\n <ul class=\"toc-list\">\n ${tocItems}\n </ul>\n </div>\n </nav>\n\n <div class=\"post-layout\">\n <aside class=\"toc-rail\" aria-label=\"Table of contents\">\n <p class=\"toc-label-text\">Contents</p>\n <ul class=\"toc-list\">\n ${tocItems}\n </ul>\n </aside>\n\n <main class=\"post-content\" id=\"post-main\">\n ${sectionsHtml}\n\n <div class=\"cta-block\">\n <h2 class=\"cta-heading\">${post.ctaHeading} <em>${post.ctaHeadingItalic}</em></h2>\n <p class=\"cta-body\">${post.ctaBody}</p>\n <a href=\"/#pricing\" class=\"cta-btn\">Start free →</a>\n </div>\n </main>\n </div>\n\n <footer class=\"site-footer\">\n <p>© ${year} MCP Scraper · <a href=\"/privacy\">Privacy</a> · <a href=\"/terms\">Terms</a></p>\n </footer>\n\n <script>\n (function () {\n var bar = document.querySelector('.read-progress');\n var main = document.getElementById('post-main');\n function updateProgress() {\n var rect = main.getBoundingClientRect();\n var total = main.scrollHeight - window.innerHeight;\n var scrolled = Math.max(0, -rect.top);\n var pct = total > 0 ? Math.min(100, (scrolled / total) * 100) : 0;\n bar.style.width = pct + '%';\n bar.setAttribute('aria-valuenow', Math.round(pct));\n }\n window.addEventListener('scroll', updateProgress, { passive: true });\n updateProgress();\n\n var allTocLinks = Array.from(document.querySelectorAll('.toc-link, .toc-sub-link'));\n var allAnchors = allTocLinks.map(function(l) {\n var id = l.getAttribute('data-toc');\n return { id: id, el: id ? document.getElementById(id) : null, link: l };\n }).filter(function(a) { return a.el; });\n function updateToc() {\n var scrollY = window.scrollY + 120;\n var active = allAnchors[0] ? allAnchors[0].id : null;\n for (var i = 0; i < allAnchors.length; i++) {\n if (allAnchors[i].el.offsetTop <= scrollY) active = allAnchors[i].id;\n }\n allAnchors.forEach(function(a) {\n a.link.classList.toggle('active', a.id === active);\n });\n }\n window.addEventListener('scroll', updateToc, { passive: true });\n updateToc();\n\n document.querySelectorAll('.toc-sub-link').forEach(function(link) {\n link.addEventListener('click', function() {\n var cardId = this.getAttribute('data-toc');\n var card = document.getElementById(cardId);\n if (card) {\n var trigger = card.querySelector('.card-trigger');\n if (trigger && trigger.getAttribute('aria-expanded') !== 'true') trigger.click();\n }\n });\n });\n\n var mobToggle = document.querySelector('.mob-toc-toggle');\n var mobPanel = document.getElementById('mob-toc-panel');\n if (mobToggle && mobPanel) {\n mobToggle.addEventListener('click', function() {\n var open = mobToggle.getAttribute('aria-expanded') === 'true';\n mobToggle.setAttribute('aria-expanded', open ? 'false' : 'true');\n mobPanel.hidden = open;\n });\n mobPanel.querySelectorAll('.toc-link, .toc-sub-link').forEach(function(link) {\n link.addEventListener('click', function() {\n mobToggle.setAttribute('aria-expanded', 'false');\n mobPanel.hidden = true;\n });\n });\n }\n\n var triggers = document.querySelectorAll('.card-trigger');\n triggers.forEach(function (btn) {\n btn.addEventListener('click', function () {\n var expanded = this.getAttribute('aria-expanded') === 'true';\n var bodyId = this.getAttribute('aria-controls');\n var body = document.getElementById(bodyId);\n this.setAttribute('aria-expanded', expanded ? 'false' : 'true');\n if (body) body.hidden = expanded;\n });\n });\n })();\n </script>\n</body>\n</html>`\n}\n\nexport function renderBlogIndex(posts: BlogPost[]): string {\n const sorted = [...posts].sort((a, b) => b.publishedAt.localeCompare(a.publishedAt))\n const totalCards = posts.reduce((sum, p) => sum + p.sections.reduce((s2, sec) => s2 + sec.cards.length, 0), 0)\n\n const catCounts: Record<string, number> = { all: posts.length }\n for (const p of posts) {\n catCounts[p.category] = (catCounts[p.category] ?? 0) + 1\n }\n const cats = Object.keys(catCounts).filter(c => c !== 'all').sort((a, b) => catCounts[b] - catCounts[a])\n\n const chipHtml = ['all', ...cats].map(cat => {\n const label = cat === 'all' ? 'All' : cat\n return `<button class=\"chip${cat === 'all' ? ' active' : ''}\" data-cat=\"${cat}\">${label} <span class=\"chip-count\">${catCounts[cat]}</span></button>`\n }).join('\\n ')\n\n const cardHtml = sorted.map(p => {\n const pubDate = new Date(p.publishedAt).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })\n const searchText = `${p.title} ${p.deck} ${p.category} ${p.tags.join(' ')}`.toLowerCase()\n const eyebrow = [p.fieldGuideLabel, p.category].filter(Boolean).join(' · ')\n return `<a href=\"/blog/${p.slug}/\" class=\"blog-card\" data-cat=\"${p.category}\" data-search=\"${searchText.replace(/\"/g, '"')}\">\n <div class=\"blog-card-eyebrow\">\n <span class=\"blog-card-cat-dot\"></span>\n <span>${eyebrow}</span>\n </div>\n <h2 class=\"blog-card-title\">${p.titleDisplay} <em>${p.titleDisplayItalic}</em></h2>\n <p class=\"blog-card-deck\">${p.deck}</p>\n <div class=\"blog-card-footer\">\n <time datetime=\"${p.publishedAt}\">${pubDate}</time>\n <span class=\"blog-card-sep\">·</span>\n <span>${p.readTimeMinutes} min read</span>\n <span class=\"blog-card-sep\">·</span>\n <span>${p.badge}</span>\n </div>\n </a>`\n }).join('\\n ')\n\n const year = new Date().getFullYear()\n\n const INDEX_CSS = `\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Trial-VF.woff2\") format(\"woff2\");\n font-weight: 100 900;\n font-style: normal;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Regular-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 400;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Bold-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 700;\n font-style: italic;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa Display\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Black-Trial.woff2\") format(\"woff2\");\n font-weight: 900;\n font-style: normal;\n font-display: swap;\n }\n @font-face {\n font-family: \"GT Flexa Display\";\n src: url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Black-Italic-Trial.woff2\") format(\"woff2\");\n font-weight: 900;\n font-style: italic;\n font-display: swap;\n }\n\n :root {\n --brand: #d06045;\n --brand-dark: #b34e36;\n --ink: #1a1a1a;\n --ink-2: #444;\n --ink-3: #777;\n --rule: #e8e4de;\n --bg: #faf9f7;\n --bg-card: #fff;\n --radius: 8px;\n --page-max: 1200px;\n }\n\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: \"GT Flexa\", system-ui, sans-serif;\n font-weight: 400;\n font-size: 16px;\n line-height: 1.65;\n color: var(--ink);\n background: var(--bg);\n -webkit-font-smoothing: antialiased;\n }\n\n a { color: inherit; text-decoration: none; }\n\n .site-nav {\n position: sticky;\n top: 0;\n z-index: 100;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 2rem;\n background: rgba(250,249,247,0.92);\n backdrop-filter: blur(8px);\n border-bottom: 1px solid var(--rule);\n }\n .site-nav .logo {\n font-family: \"GT Flexa Display\", sans-serif;\n font-weight: 900;\n font-size: 1.1rem;\n letter-spacing: -0.02em;\n color: var(--ink);\n }\n .site-nav .logo span { color: var(--brand); }\n .nav-right { display: flex; align-items: center; gap: 1.25rem; }\n .nav-blog-link {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 600;\n font-size: 0.88rem;\n color: var(--brand);\n }\n .nav-cta {\n display: inline-flex;\n align-items: center;\n padding: 0.5rem 1.1rem;\n background: var(--brand);\n color: #fff;\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 600;\n font-size: 0.85rem;\n border-radius: 6px;\n transition: background 0.15s;\n }\n .nav-cta:hover { background: var(--brand-dark); }\n\n .blog-hero {\n max-width: var(--page-max);\n margin: 0 auto;\n padding: 4rem 2rem 2.5rem;\n border-bottom: 1px solid var(--rule);\n }\n .blog-eyebrow {\n font-size: 0.72rem;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: var(--brand);\n margin-bottom: 0.9rem;\n }\n .blog-hero-title {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 900;\n font-size: clamp(2.4rem, 5vw, 4rem);\n letter-spacing: -0.03em;\n line-height: 1.0;\n color: var(--ink);\n margin-bottom: 1rem;\n }\n .blog-hero-title em { font-style: italic; color: var(--brand); }\n .blog-hero-deck {\n font-size: 1.05rem;\n font-style: italic;\n color: var(--ink-2);\n max-width: 560px;\n margin-bottom: 2rem;\n line-height: 1.55;\n }\n .blog-hero-stats {\n display: flex;\n gap: 2.5rem;\n flex-wrap: wrap;\n }\n .blog-stat { display: flex; flex-direction: column; gap: 0.05rem; }\n .blog-stat-value {\n font-family: \"GT Flexa Display\", sans-serif;\n font-weight: 900;\n font-size: 1.5rem;\n color: var(--brand);\n letter-spacing: -0.02em;\n line-height: 1;\n }\n .blog-stat-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--ink-3);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .blog-controls {\n max-width: var(--page-max);\n margin: 0 auto;\n padding: 1.5rem 2rem;\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-wrap: wrap;\n border-bottom: 1px solid var(--rule);\n }\n .blog-search-wrap {\n position: relative;\n flex-shrink: 0;\n }\n .blog-search-icon {\n position: absolute;\n left: 0.75rem;\n top: 50%;\n transform: translateY(-50%);\n color: var(--ink-3);\n pointer-events: none;\n }\n .blog-search {\n padding: 0.5rem 0.75rem 0.5rem 2.25rem;\n border: 1px solid var(--rule);\n border-radius: 6px;\n background: var(--bg-card);\n font-family: \"GT Flexa\", sans-serif;\n font-size: 0.88rem;\n color: var(--ink);\n width: 220px;\n outline: none;\n transition: border-color 0.15s;\n }\n .blog-search:focus { border-color: var(--brand); }\n .blog-search::placeholder { color: var(--ink-3); }\n .chip-row {\n display: flex;\n gap: 0.4rem;\n flex-wrap: wrap;\n flex: 1;\n }\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.38rem 0.85rem;\n border: 1px solid var(--rule);\n border-radius: 100px;\n background: var(--bg-card);\n font-family: \"GT Flexa\", sans-serif;\n font-size: 0.82rem;\n font-weight: 500;\n color: var(--ink-2);\n cursor: pointer;\n transition: border-color 0.15s, color 0.15s, background 0.15s;\n white-space: nowrap;\n }\n .chip:hover { border-color: var(--ink-3); color: var(--ink); }\n .chip.active {\n background: var(--ink);\n border-color: var(--ink);\n color: #fff;\n }\n .chip-count {\n font-size: 0.72rem;\n font-weight: 600;\n opacity: 0.65;\n }\n .chip.active .chip-count { opacity: 0.7; }\n\n .blog-results-bar {\n max-width: var(--page-max);\n margin: 0 auto;\n padding: 1rem 2rem 0.5rem;\n font-size: 0.8rem;\n color: var(--ink-3);\n font-weight: 500;\n letter-spacing: 0.02em;\n }\n\n .blog-grid {\n max-width: var(--page-max);\n margin: 0 auto;\n padding: 1rem 2rem 6rem;\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 1.25rem;\n }\n\n .blog-card {\n display: flex;\n flex-direction: column;\n gap: 0.6rem;\n padding: 1.6rem 1.75rem;\n background: var(--bg-card);\n border: 1px solid var(--rule);\n border-radius: var(--radius);\n transition: border-color 0.18s, transform 0.18s, box-shadow 0.18s;\n }\n .blog-card:hover {\n border-color: var(--brand);\n transform: translateY(-2px);\n box-shadow: 0 4px 20px rgba(0,0,0,0.07);\n }\n .blog-card-eyebrow {\n display: flex;\n align-items: center;\n gap: 0.45rem;\n font-size: 0.72rem;\n font-weight: 600;\n color: var(--brand);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n .blog-card-cat-dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--brand);\n flex-shrink: 0;\n }\n .blog-card-title {\n font-family: \"GT Flexa\", sans-serif;\n font-weight: 900;\n font-size: clamp(1.25rem, 2.5vw, 1.55rem);\n letter-spacing: -0.025em;\n line-height: 1.1;\n color: var(--ink);\n flex: 1;\n }\n .blog-card-title em { font-style: italic; color: var(--brand); }\n .blog-card-deck {\n font-size: 0.88rem;\n font-style: italic;\n color: var(--ink-2);\n line-height: 1.55;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n .blog-card-footer {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.75rem;\n color: var(--ink-3);\n font-weight: 500;\n margin-top: 0.25rem;\n flex-wrap: wrap;\n }\n .blog-card-sep { opacity: 0.5; }\n\n .blog-empty {\n max-width: var(--page-max);\n margin: 2rem auto;\n padding: 3rem 2rem;\n text-align: center;\n color: var(--ink-3);\n }\n .blog-empty p {\n font-size: 1rem;\n font-style: italic;\n }\n\n .site-footer {\n border-top: 1px solid var(--rule);\n padding: 2rem;\n text-align: center;\n font-size: 0.82rem;\n color: var(--ink-3);\n }\n\n @media (max-width: 640px) {\n .site-nav { padding: 0.75rem 1.25rem; }\n .blog-hero { padding: 2.5rem 1.25rem 2rem; }\n .blog-controls { padding: 1rem 1.25rem; gap: 0.75rem; }\n .blog-search { width: 100%; }\n .blog-search-wrap { width: 100%; }\n .blog-results-bar { padding: 0.75rem 1.25rem 0.25rem; }\n .blog-grid { padding: 0.75rem 1.25rem 4rem; gap: 1rem; }\n .blog-hero-stats { gap: 1.5rem; }\n }\n `\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Blog | MCP Scraper</title>\n <meta name=\"description\" content=\"Field guides on AI development, SEO, and developer tools. Every post answers 20–50 real search questions with sourced, practitioner-grade answers.\">\n <link rel=\"canonical\" href=\"https://mcpscraper.dev/blog/\">\n <meta property=\"og:type\" content=\"website\">\n <meta property=\"og:title\" content=\"Blog | MCP Scraper\">\n <meta property=\"og:description\" content=\"Field guides on AI development, SEO, and developer tools.\">\n <meta property=\"og:url\" content=\"https://mcpscraper.dev/blog/\">\n <meta name=\"twitter:card\" content=\"summary\">\n <style>${INDEX_CSS}</style>\n</head>\n<body>\n <nav class=\"site-nav\" aria-label=\"Site navigation\">\n <a href=\"/\" class=\"logo\">MCP<span>.</span>Scraper</a>\n <div class=\"nav-right\">\n <span class=\"nav-blog-link\">Blog</span>\n <a href=\"/#pricing\" class=\"nav-cta\">Get API Access</a>\n </div>\n </nav>\n\n <header class=\"blog-hero\">\n <p class=\"blog-eyebrow\">MCP Scraper Blog</p>\n <h1 class=\"blog-hero-title\">Field guides. <em>No fluff.</em></h1>\n <p class=\"blog-hero-deck\">Every post answers real search questions with sourced, practitioner-grade answers — no padding, no placeholder content.</p>\n <div class=\"blog-hero-stats\">\n <div class=\"blog-stat\">\n <span class=\"blog-stat-value\">${posts.length}</span>\n <span class=\"blog-stat-label\">Posts</span>\n </div>\n <div class=\"blog-stat\">\n <span class=\"blog-stat-value\">${totalCards}</span>\n <span class=\"blog-stat-label\">Questions answered</span>\n </div>\n <div class=\"blog-stat\">\n <span class=\"blog-stat-value\">${cats.length}</span>\n <span class=\"blog-stat-label\">Topics</span>\n </div>\n </div>\n </header>\n\n <div class=\"blog-controls\" role=\"search\" aria-label=\"Filter posts\">\n <div class=\"blog-search-wrap\">\n <svg class=\"blog-search-icon\" width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"6.5\" cy=\"6.5\" r=\"5\" stroke=\"currentColor\" stroke-width=\"1.4\"/>\n <path d=\"m10.5 10.5 3 3\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\"/>\n </svg>\n <input type=\"search\" class=\"blog-search\" id=\"blog-search\" placeholder=\"Search posts...\" aria-label=\"Search posts\">\n </div>\n <div class=\"chip-row\" id=\"chip-row\" role=\"group\" aria-label=\"Filter by category\">\n ${chipHtml}\n </div>\n </div>\n\n <p class=\"blog-results-bar\" id=\"results-bar\" aria-live=\"polite\">${posts.length} posts</p>\n\n <main class=\"blog-grid\" id=\"blog-grid\" aria-label=\"Blog posts\">\n ${cardHtml}\n </main>\n\n <div class=\"blog-empty\" id=\"blog-empty\" hidden aria-live=\"polite\">\n <p>No posts match — try a different search or category.</p>\n </div>\n\n <footer class=\"site-footer\">\n <p>© ${year} MCP Scraper · <a href=\"/privacy\">Privacy</a> · <a href=\"/terms\">Terms</a></p>\n </footer>\n\n <script>\n (function () {\n var cards = Array.from(document.querySelectorAll('.blog-card'));\n var searchInput = document.getElementById('blog-search');\n var chipRow = document.getElementById('chip-row');\n var resultsBar = document.getElementById('results-bar');\n var emptyState = document.getElementById('blog-empty');\n var activeCategory = 'all';\n var searchQuery = '';\n\n function applyFilters() {\n var visible = 0;\n cards.forEach(function (card) {\n var catMatch = activeCategory === 'all' || card.getAttribute('data-cat') === activeCategory;\n var searchMatch = searchQuery === '' || (card.getAttribute('data-search') || '').includes(searchQuery);\n var show = catMatch && searchMatch;\n card.hidden = !show;\n if (show) visible++;\n });\n var label = visible === 1 ? '1 post' : visible + ' posts';\n if (searchQuery || activeCategory !== 'all') {\n label += ' matching';\n }\n resultsBar.textContent = label;\n emptyState.hidden = visible > 0;\n }\n\n chipRow.addEventListener('click', function (e) {\n var chip = e.target.closest('.chip');\n if (!chip) return;\n activeCategory = chip.getAttribute('data-cat');\n Array.from(chipRow.querySelectorAll('.chip')).forEach(function (c) {\n c.classList.toggle('active', c === chip);\n });\n applyFilters();\n });\n\n searchInput.addEventListener('input', function () {\n searchQuery = this.value.toLowerCase().trim();\n applyFilters();\n });\n })();\n </script>\n</body>\n</html>`\n}\n","import satori from 'satori'\nimport { Resvg } from '@resvg/resvg-js'\nimport { readFileSync } from 'fs'\nimport { join } from 'path'\nimport type { BlogPost } from './types.js'\n\nconst FONTS_DIR = join(process.cwd(), 'public/fonts/gt-flexa')\nconst BG_PATH = join(process.cwd(), 'public/images/og-bg.png')\n\nlet fontDisplay: ArrayBuffer\nlet fontBody: ArrayBuffer\nlet bgDataUrl: string\n\nfunction loadAssets() {\n if (fontDisplay) return\n fontDisplay = readFileSync(join(FONTS_DIR, 'GT-Flexa-Black-Trial.otf')).buffer as ArrayBuffer\n fontBody = readFileSync(join(FONTS_DIR, 'GT-Flexa-Medium-Trial.otf')).buffer as ArrayBuffer\n bgDataUrl = `url('data:image/png;base64,${readFileSync(BG_PATH).toString('base64')}')`\n}\n\ntype El = { type: string; props: Record<string, unknown> }\n\nfunction el(type: string, style: Record<string, unknown>, ...children: (El | string | null | undefined)[]): El {\n const ch = children.flat().filter((c): c is El | string => c != null)\n return { type, props: { style, children: ch.length === 0 ? undefined : ch.length === 1 ? ch[0] : ch } }\n}\n\nfunction clean(text: string): string {\n return text\n .replace(/[\\u{1F000}-\\u{1FFFF}\\u{2600}-\\u{27BF}]/gu, '')\n .replace(/[().,!?]/g, '')\n .trim()\n}\n\nfunction tagline(post: BlogPost): string {\n if (post.titleDisplayItalic) return clean(post.titleDisplayItalic).slice(0, 38)\n if (post.titleDisplay) return clean(post.titleDisplay).slice(0, 38)\n return post.title.split(/\\s+/).slice(0, 4).join(' ')\n}\n\nexport async function generateOgImage(post: BlogPost): Promise<Buffer> {\n loadAssets()\n\n const punch = tagline(post)\n\n const tree = el('div', {\n width: 1200,\n height: 630,\n display: 'flex',\n flexDirection: 'column',\n padding: '64px 72px',\n backgroundImage: bgDataUrl,\n backgroundSize: 'cover',\n backgroundPosition: 'center',\n },\n el('div', {\n color: 'rgba(255,255,255,0.5)',\n fontSize: 20,\n letterSpacing: '0.05em',\n fontFamily: 'GTF',\n fontWeight: 500,\n }, 'MCP Scraper'),\n el('div', { display: 'flex', flex: 2 }, ''),\n el('div', {\n color: '#ffffff',\n fontSize: 80,\n lineHeight: 1.0,\n letterSpacing: '-0.03em',\n fontFamily: 'GTFXP',\n fontWeight: 900,\n maxWidth: 680,\n }, punch),\n el('div', { display: 'flex', flex: 1 }, ''),\n )\n\n const svg = await satori(tree as Parameters<typeof satori>[0], {\n width: 1200,\n height: 630,\n fonts: [\n { name: 'GTFXP', data: fontDisplay, weight: 900, style: 'normal' },\n { name: 'GTF', data: fontBody, weight: 500, style: 'normal' },\n ],\n })\n\n const resvg = new Resvg(svg)\n return Buffer.from(resvg.render().asPng())\n}\n","import { posts as blogPosts } from '../blog/registry.js'\nimport { renderPost } from '../blog/template.js'\nimport { generateOgImage } from '../blog/og.js'\nimport { Resend } from 'resend'\nimport { isPrivateIpAddress, resolvesToPrivateAddress, validatePublicHttpUrl } from './url-utils.js'\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { extractKpo } from './kpo-extractor.js'\nimport { capturePageData } from '../lib/screenshot.js'\nimport { harvestPageMedia, type MediaType } from '../lib/media-extractor.js'\nimport { spiderSite } from './site-mapper.js'\nimport { extractSite } from './site-extractor.js'\nimport { Hono } from 'hono'\nimport { serve as serveInngest } from 'inngest/hono'\nimport { inngest } from '../inngest/client.js'\nimport { siteAuditFn } from '../inngest/functions/site-audit.js'\nimport { siteAuditApp } from './site-audit-routes.js'\nimport { youtubeApp } from './youtube-routes.js'\nimport { screenshotApp } from './screenshot-routes.js'\nimport { facebookAdApp } from './facebook-ad-routes.js'\nimport { mapsApp } from './maps-routes.js'\nimport { serpIntelligenceApp } from './serp-intelligence-routes.js'\nimport { mcpApp } from '../mcp/mcp-routes.js'\nimport { stripeApp } from './stripe-routes.js'\nimport { startSiteAuditWorker } from './site-audit-worker.js'\nimport { createMiddleware } from 'hono/factory'\nimport { getCookie, setCookie, deleteCookie } from 'hono/cookie'\nimport {\n getUserByApiKey, getUserById, createUser, listUsers, deactivateUser,\n createJob, createRunningJob, getJob, listJobs, getUserStats,\n listRequestEvents, logRequestEvent,\n rotateApiKey, revokeApiKey, verifyPassword, getUserByEmail, setPassword,\n completeJob, failJob, cancelJob,\n countActiveJobsForUser, countActiveUsers,\n listHarvestAttempts,\n creditMc, debitMc, getLedger, setStripeCustomerId,\n setExtraConcurrencySlots, setConcurrencySubId, checkRateLimit,\n createPasswordResetToken, consumePasswordResetToken, reconcileBalanceMc,\n ledgerExistsForOperation,\n} from './db.js'\nimport Stripe from 'stripe'\nimport { MC_COSTS, BALANCE_PRICE_IDS, CREDIT_COST_CATALOG, CONCURRENCY_PRICE_ID, insufficientBalanceResponse, LedgerOperation } from './rates.js'\nimport { BillingCheckoutBodySchema } from './billing-schemas.js'\nimport { HarvestBodySchema, ExtractUrlBodySchema, MapUrlsBodySchema, ExtractSiteBodySchema } from './server-schemas.js'\nimport { grantSignupCredit, applyMonthlyFreeRefresh, runMonthlyRefreshSweep, getFreeCreditBreakdown } from './credit-operations.js'\nimport { harvest } from '../harvest.js'\nimport { classifyHarvestProblem, harvestProblemResponse, serializeHarvestProblem } from './harvest-problems.js'\nimport { createHarvestAttemptRecorder } from './harvest-attempt-events.js'\nimport { verifySession, signSession } from './session.js'\nimport type { User } from './db.js'\n\ntype Env = { Variables: { user: User } }\ntype SessEnv = { Variables: { sessionUser: User } }\n\nconst secureCookies = process.env.NODE_ENV === 'production' || process.env.VERCEL === '1'\nconst isProduction = secureCookies\nconst sessionCookieOptions = {\n httpOnly: true,\n secure: secureCookies,\n path: '/',\n maxAge: 60 * 60 * 24 * 30,\n sameSite: 'Strict' as const,\n}\n\nfunction configuredOrigins(): Set<string> {\n const origins = new Set(['https://mcpscraper.dev'])\n for (const raw of (process.env.ALLOWED_ORIGINS ?? process.env.APP_ORIGIN ?? '').split(',')) {\n const trimmed = raw.trim()\n if (trimmed) origins.add(trimmed)\n }\n if (!isProduction) {\n origins.add('http://localhost:5173')\n origins.add('http://localhost:3000')\n origins.add('http://localhost:63316')\n }\n return origins\n}\n\nfunction appOrigin(): string {\n return process.env.APP_ORIGIN?.trim() || 'https://mcpscraper.dev'\n}\n\nfunction clientIp(c: { req: { header: (name: string) => string | undefined } }): string {\n return (\n c.req.header('cf-connecting-ip') ||\n c.req.header('x-real-ip') ||\n c.req.header('x-forwarded-for')?.split(',')[0]?.trim() ||\n 'unknown'\n )\n}\n\nfunction rateLimitKey(c: { req: { header: (name: string) => string | undefined } }, email?: string): string {\n const normalizedEmail = email?.trim().toLowerCase()\n return normalizedEmail ? `${clientIp(c)}:${normalizedEmail}` : clientIp(c)\n}\n\nasync function enforceRateLimit(\n c: { req: { header: (name: string) => string | undefined }; json: (body: unknown, status?: number, headers?: Record<string, string>) => Response },\n scope: string,\n key: string,\n limit: number,\n windowSeconds: number,\n): Promise<Response | null> {\n const result = await checkRateLimit(scope, key, limit, windowSeconds)\n if (result.allowed) return null\n return c.json(\n { error: 'Too many attempts. Try again shortly.' },\n 429,\n { 'Retry-After': String(result.resetSeconds) },\n )\n}\n\nconst requireAllowedOrigin = createMiddleware(async (c, next) => {\n const origin = c.req.header('origin')\n if (!origin) return next()\n if (!configuredOrigins().has(origin)) return c.json({ error: 'Origin not allowed' }, 403)\n return next()\n})\n\n\nconst auth = createMiddleware<Env>(async (c, next) => {\n const key = c.req.header('x-api-key')\n if (!key) return c.json({ error: 'Missing API key' }, 401)\n const user = await getUserByApiKey(key)\n if (!user) return c.json({ error: 'Invalid or inactive API key' }, 401)\n const refreshed = await applyMonthlyFreeRefresh(user)\n const balanceMc = await reconcileBalanceMc(refreshed.id)\n c.set('user', { ...refreshed, balance_mc: balanceMc })\n return next()\n})\n\nconst adminAuth = createMiddleware(async (c, next) => {\n const configured = process.env.ADMIN_KEY?.trim()\n const key = c.req.header('x-admin-key')?.trim()\n if (!configured) return c.json({ error: 'Admin access disabled' }, 503)\n if (!key || key !== configured) return c.json({ error: 'Unauthorized' }, 401)\n return next()\n})\n\nconst sessionAuth = createMiddleware<SessEnv>(async (c, next) => {\n const token = getCookie(c, 'session')\n if (!token) return c.json({ error: 'Not authenticated' }, 401)\n const userId = verifySession(token)\n if (!userId) return c.json({ error: 'Session expired' }, 401)\n const user = await getUserById(userId)\n if (!user) return c.json({ error: 'User not found' }, 401)\n const refreshed = await applyMonthlyFreeRefresh(user)\n const balanceMc = await reconcileBalanceMc(refreshed.id)\n c.set('sessionUser', { ...refreshed, balance_mc: balanceMc })\n return next()\n})\n\nconst app = new Hono<Env>()\nconst STRIPE_API_VERSION = '2026-02-25.clover' as any\n\nfunction requireStripeSecret(): string | null {\n const secret = process.env.STRIPE_SECRET_KEY?.trim()\n if (secret) return secret\n if (process.env.NODE_ENV === 'production' || process.env.VERCEL === '1') return null\n return null\n}\n\nasync function createSignupStripeCustomer(email: string): Promise<string | null> {\n const secret = requireStripeSecret()\n if (!secret) return null\n const stripeClient = new Stripe(secret, { apiVersion: STRIPE_API_VERSION })\n const customer = await stripeClient.customers.create({\n email,\n description: 'MCP Scraper free account',\n metadata: {\n app: 'mcp-scraper',\n account_status: 'free',\n },\n })\n return customer.id\n}\n\napp.use('*', async (c, next) => {\n await next()\n c.header('Cache-Control', 'no-store')\n c.header('X-Content-Type-Options', 'nosniff')\n c.header('Referrer-Policy', 'same-origin')\n})\n\n\napp.post('/auth/register', requireAllowedOrigin, async (c) => {\n const { email, password } = await c.req.json<{ email?: string; password?: string }>()\n const normalizedEmail = email?.trim().toLowerCase()\n if (!normalizedEmail || !password) return c.json({ error: 'Email and password required' }, 400)\n if (password.length < 8) return c.json({ error: 'Password must be at least 8 characters' }, 400)\n const limited = await enforceRateLimit(c, 'auth_register', rateLimitKey(c), 5, 60 * 60)\n if (limited) return limited\n try {\n const existing = await getUserByEmail(normalizedEmail)\n if (existing) return c.json({ error: 'Email already registered' }, 409)\n let stripeCustomerId: string | null\n try {\n stripeCustomerId = await createSignupStripeCustomer(normalizedEmail)\n } catch {\n return c.json({ error: 'Stripe customer setup failed' }, 503)\n }\n if (!stripeCustomerId && (process.env.NODE_ENV === 'production' || process.env.VERCEL === '1')) {\n return c.json({ error: 'Stripe customer setup failed' }, 503)\n }\n const user = await createUser(normalizedEmail, undefined, password, stripeCustomerId ?? undefined)\n if (stripeCustomerId) {\n try {\n const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: STRIPE_API_VERSION })\n await stripeClient.customers.update(stripeCustomerId, {\n metadata: {\n app: 'mcp-scraper',\n app_user_id: String(user.id),\n account_status: 'free',\n },\n })\n } catch {}\n }\n const newKey = await rotateApiKey(user.id)\n await grantSignupCredit(user.id as number)\n const token = signSession(user.id as number)\n setCookie(c, 'session', token, sessionCookieOptions)\n return c.json({ email: user.email, api_key: newKey }, 201)\n } catch (err) {\n const msg = err instanceof Error ? err.message : ''\n if (msg.includes('UNIQUE')) return c.json({ error: 'Email already registered' }, 409)\n throw err\n }\n})\n\napp.post('/auth/login', requireAllowedOrigin, async (c) => {\n const { email, password } = await c.req.json<{ email?: string; password?: string }>()\n if (!email || !password) return c.json({ error: 'Email and password required' }, 400)\n const normalizedEmail = email.trim().toLowerCase()\n const limited = await enforceRateLimit(c, 'auth_login', rateLimitKey(c, normalizedEmail), 10, 15 * 60)\n if (limited) return limited\n const user = await getUserByEmail(normalizedEmail)\n if (!user || !user.password_hash || !verifyPassword(password, user.password_hash)) {\n return c.json({ error: 'Invalid email or password' }, 401)\n }\n const token = signSession(user.id)\n setCookie(c, 'session', token, sessionCookieOptions)\n return c.json({ email: user.email, api_key: user.key_active ? user.api_key : null })\n})\n\napp.post('/auth/logout', requireAllowedOrigin, (c) => {\n deleteCookie(c, 'session', { path: '/', secure: secureCookies, sameSite: 'Strict' })\n return c.json({ ok: true })\n})\n\napp.post('/auth/forgot-password', requireAllowedOrigin, async (c) => {\n const { email } = await c.req.json<{ email?: string }>()\n const normalizedEmail = email?.trim().toLowerCase()\n if (!normalizedEmail) return c.json({ ok: true })\n const ipLimited = await enforceRateLimit(c, 'auth_forgot_ip', rateLimitKey(c), 20, 60 * 60)\n if (ipLimited) return ipLimited\n const emailLimited = await enforceRateLimit(c, 'auth_forgot_email', rateLimitKey(c, normalizedEmail), 5, 60 * 60)\n if (emailLimited) return emailLimited\n const user = await getUserByEmail(normalizedEmail)\n if (!user) return c.json({ ok: true })\n const token = await createPasswordResetToken(user.id)\n const appUrl = process.env.APP_URL ?? 'https://mcpscraper.dev'\n const resetUrl = `${appUrl}/reset-password?token=${token}`\n if (process.env.RESEND_API_KEY) {\n try {\n const resend = new Resend(process.env.RESEND_API_KEY)\n await resend.emails.send({\n from: 'MCP Scraper <noreply@updates.mcpscraper.dev>',\n to: normalizedEmail,\n subject: 'Reset your MCP Scraper password',\n html: `<p>Hi,</p><p>Click the link below to reset your password. This link expires in 1 hour.</p><p><a href=\"${resetUrl}\">${resetUrl}</a></p><p>If you didn't request this, you can ignore this email.</p>`,\n })\n } catch {}\n }\n return c.json({ ok: true })\n})\n\napp.post('/auth/reset-password', requireAllowedOrigin, async (c) => {\n const { token, password } = await c.req.json<{ token?: string; password?: string }>()\n if (!token || !password) return c.json({ error: 'Token and password required' }, 400)\n if (password.length < 8) return c.json({ error: 'Password must be at least 8 characters' }, 400)\n const limited = await enforceRateLimit(c, 'auth_reset', rateLimitKey(c), 10, 60 * 60)\n if (limited) return limited\n const userId = await consumePasswordResetToken(token)\n if (!userId) return c.json({ error: 'Invalid or expired reset link' }, 400)\n await setPassword(userId, password)\n return c.json({ ok: true })\n})\n\napp.get('/stats/spots', async (c) => {\n const taken = await countActiveUsers()\n return c.json({ taken, open: true })\n})\n\napp.get('/me', async (c) => {\n const token = getCookie(c, 'session')\n if (!token) return c.json({ authenticated: false })\n\n const userId = verifySession(token)\n if (!userId) {\n deleteCookie(c, 'session', { path: '/', secure: secureCookies, sameSite: 'Strict' })\n return c.json({ authenticated: false })\n }\n\n const foundUser = await getUserById(userId)\n if (!foundUser) {\n deleteCookie(c, 'session', { path: '/', secure: secureCookies, sameSite: 'Strict' })\n return c.json({ authenticated: false })\n }\n\n const refreshed = await applyMonthlyFreeRefresh(foundUser)\n const balanceMc = await reconcileBalanceMc(refreshed.id)\n const user = { ...refreshed, balance_mc: balanceMc }\n const stats = await getUserStats(user.id)\n return c.json({\n authenticated: true,\n email: user.email,\n api_key: user.key_active ? user.api_key : null,\n created_at: user.created_at,\n balance_mc: user.balance_mc,\n balance_credits: user.balance_mc / 1_000,\n extra_concurrency_slots: user.extra_concurrency_slots,\n concurrency_limit: 1 + user.extra_concurrency_slots,\n has_concurrency_sub: !!user.concurrency_stripe_sub_id,\n ...stats,\n })\n})\n\napp.post('/api-key/rotate', requireAllowedOrigin, sessionAuth, async (c) => {\n const user = c.get('sessionUser')\n const newKey = await rotateApiKey(user.id)\n return c.json({ api_key: newKey })\n})\n\napp.delete('/api-key', requireAllowedOrigin, sessionAuth, async (c) => {\n const user = c.get('sessionUser')\n await revokeApiKey(user.id)\n return c.json({ ok: true })\n})\n\nconst BYPASS_EMAILS = new Set(\n (process.env.HARVEST_LIMIT_BYPASS_EMAILS ?? '').split(',').map(e => e.trim()).filter(Boolean)\n)\nconst SYNC_HARVEST_TIMEOUT_MS = Number(process.env.SYNC_HARVEST_TIMEOUT_MS ?? 105_000)\n\nfunction combineAbortSignals(signals: AbortSignal[]): AbortSignal {\n const controller = new AbortController()\n const abortFrom = (signal: AbortSignal) => {\n if (!controller.signal.aborted) controller.abort(signal.reason)\n }\n for (const signal of signals) {\n if (signal.aborted) {\n abortFrom(signal)\n break\n }\n signal.addEventListener('abort', () => abortFrom(signal), { once: true })\n }\n return controller.signal\n}\n\nfunction countPaaQuestions(result: unknown): number {\n if (!result || typeof result !== 'object') return 0\n const value = result as { totalQuestions?: unknown; flat?: unknown }\n if (typeof value.totalQuestions === 'number') return value.totalQuestions\n return Array.isArray(value.flat) ? value.flat.length : 0\n}\n\nfunction paaCostForQuestionCount(questionCount: number): number {\n return Math.max(1, questionCount) * MC_COSTS.paa\n}\n\nasync function checkHarvestLimits(userId: number | bigint, email: string, extraSlots = 0): Promise<{ error: string } | null> {\n if (BYPASS_EMAILS.has(email)) return null\n const limit = 1 + extraSlots\n const active = await countActiveJobsForUser(userId)\n if (active >= limit) return { error: `You have ${active} job${active !== 1 ? 's' : ''} running. Your account allows ${limit} concurrent job${limit !== 1 ? 's' : ''}. Wait for one to finish or add a concurrency slot at mcpscraper.dev/billing.` }\n return null\n}\n\napp.post('/harvest', auth, async (c) => {\n const user = c.get('user')\n const raw = await c.req.json().catch(() => ({}))\n const bodyResult = HarvestBodySchema.safeParse(raw)\n if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? 'Invalid request' }, 400)\n const body = bodyResult.data\n const callback = body.callback_url?.trim()\n if (callback) {\n const checked = await validatePublicHttpUrl(callback, { field: 'callback_url', requireHttps: true })\n if (checked.error) return c.json({ error: checked.error }, 400)\n body.callback_url = checked.parsed?.href\n }\n\n const limitErr = await checkHarvestLimits(user.id, user.email, user.extra_concurrency_slots)\n if (limitErr) return c.json(limitErr, 429)\n\n const options = {\n query: body.query.trim(),\n location: body.location,\n depth: Math.min(4, Math.max(1, body.depth ?? 4)),\n maxQuestions: Math.max(1, body.maxQuestions ?? 30),\n gl: body.gl ?? 'us',\n hl: body.hl ?? 'en',\n device: body.device ?? 'desktop',\n proxyMode: body.proxyMode ?? 'location',\n proxyZip: body.proxyZip,\n debug: body.debug ?? false,\n serpOnly: body.serpOnly ?? false,\n pages: Math.min(2, Math.max(1, body.pages ?? 1)),\n }\n\n const harvestCost = options.serpOnly ? MC_COSTS.serp : options.maxQuestions * MC_COSTS.paa\n const { ok: harvestOk, balance_mc: harvestBal } = await debitMc(user.id, harvestCost, options.serpOnly ? LedgerOperation.SERP : LedgerOperation.PAA, options.query)\n if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402)\n\n const jobId = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url)\n\n if (process.env.CRON_SECRET) {\n const url = new URL(c.req.url)\n void fetch(`${url.origin}/cron/tick`, {\n headers: { Authorization: `Bearer ${process.env.CRON_SECRET}` },\n }).catch(() => {})\n await new Promise((r) => setTimeout(r, 80))\n }\n\n return c.json({ job_id: jobId, status: 'pending' }, 202)\n})\n\napp.post('/harvest/sync', auth, async (c) => {\n const user = c.get('user')\n const raw = await c.req.json().catch(() => ({}))\n const bodyResult = HarvestBodySchema.safeParse(raw)\n if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? 'Invalid request' }, 400)\n const body = bodyResult.data\n\n const limitErr = await checkHarvestLimits(user.id, user.email, user.extra_concurrency_slots)\n if (limitErr) return c.json(limitErr, 429)\n\n const options = {\n query: body.query.trim(),\n location: body.location,\n depth: Math.min(4, Math.max(1, body.depth ?? 4)),\n maxQuestions: Math.max(1, body.maxQuestions ?? 30),\n gl: body.gl ?? 'us',\n hl: body.hl ?? 'en',\n device: body.device ?? 'desktop',\n proxyMode: body.proxyMode ?? 'location',\n proxyZip: body.proxyZip,\n debug: body.debug ?? false,\n serpOnly: body.serpOnly ?? false,\n pages: Math.min(2, Math.max(1, body.pages ?? 1)),\n }\n\n const syncCost = options.serpOnly ? MC_COSTS.serp : options.maxQuestions * MC_COSTS.paa\n const { ok: syncOk, balance_mc: syncBal } = await debitMc(user.id, syncCost, options.serpOnly ? LedgerOperation.SERP : LedgerOperation.PAA, options.query)\n if (!syncOk) return c.json(insufficientBalanceResponse(syncBal, syncCost), 402)\n\n const jobId = await createRunningJob(user.id, options.query, options)\n const recordAttempt = createHarvestAttemptRecorder(jobId, user.id)\n const syncSignal = combineAbortSignals([\n c.req.raw.signal,\n AbortSignal.timeout(Number.isFinite(SYNC_HARVEST_TIMEOUT_MS) && SYNC_HARVEST_TIMEOUT_MS > 0 ? SYNC_HARVEST_TIMEOUT_MS : 105_000),\n ])\n\n try {\n const result = await harvest({\n ...options,\n kernelApiKey: process.env.KERNEL_API_KEY?.trim(),\n headless: true,\n format: 'json',\n outputDir: '/tmp/paa-output-api',\n signal: syncSignal,\n onAttemptEvent: recordAttempt,\n })\n await completeJob(jobId, result)\n const attempts = await listHarvestAttempts(jobId, user.id)\n if (!options.serpOnly) {\n const actualCost = paaCostForQuestionCount(countPaaQuestions(result))\n const diff = syncCost - actualCost\n if (diff > 0) await creditMc(user.id, diff, LedgerOperation.PAA_REFUND, 'overestimate refund')\n else if (diff < 0) await debitMc(user.id, -diff, LedgerOperation.PAA, options.query)\n }\n return c.json({ job_id: jobId, status: 'done', result, attempts })\n } catch (err) {\n const problem = classifyHarvestProblem(err)\n const response = harvestProblemResponse(problem)\n const attempts = await listHarvestAttempts(jobId, user.id)\n if (problem.terminalStatus === 'cancelled' || c.req.raw.signal.aborted) {\n await cancelJob(jobId, serializeHarvestProblem(problem))\n await creditMc(user.id, syncCost, LedgerOperation.REFUND, 'cancelled call')\n return c.json({ job_id: jobId, status: 'cancelled', ...response, attempts }, problem.httpStatus as 408)\n }\n await failJob(jobId, serializeHarvestProblem(problem))\n await creditMc(user.id, syncCost, LedgerOperation.REFUND, 'failed call')\n return c.json({ job_id: jobId, status: 'failed', ...response, attempts }, problem.httpStatus as 500)\n }\n})\n\napp.get('/jobs/:id', auth, async (c) => {\n const job = await getJob(c.req.param('id'), c.get('user').id)\n if (!job) return c.json({ error: 'Job not found' }, 404)\n const attempts = await listHarvestAttempts(job.id, c.get('user').id)\n return c.json({ ...job, attempts })\n})\n\napp.get('/jobs', auth, async (c) => {\n return c.json(await listJobs(c.get('user').id))\n})\n\napp.get('/history', auth, async (c) => {\n const userId = c.get('user').id\n const [jobs, events] = await Promise.all([\n listJobs(userId),\n listRequestEvents(userId, 100),\n ])\n const jobRows = jobs.map(job => ({\n id: job.id,\n ts: job.created_at,\n query: job.query,\n location: (job.options?.location as string | undefined) ?? '',\n source: LedgerOperation.SERP,\n status: job.status,\n result_count: job.result ? ((job.result as any).flat?.length ?? 0) + ((job.result as any).organicResults?.length ?? 0) : 0,\n result: job.result ?? null,\n }))\n const eventRows = events.map(event => ({\n id: event.id,\n ts: event.created_at,\n query: event.query,\n location: event.location ?? '',\n source: event.source,\n status: event.status,\n result_count: event.result_count ?? 0,\n result: event.result ?? null,\n error: event.error ?? null,\n }))\n const rows = [...jobRows, ...eventRows].sort((a, b) => String(b.ts).localeCompare(String(a.ts)))\n return c.json(rows.slice(0, 100))\n})\n\napp.get('/ledger', auth, async (c) => {\n return c.json(await getLedger(c.get('user').id, 100))\n})\n\napp.post('/admin/users', adminAuth, async (c) => {\n const { email, name, password } = await c.req.json<{ email?: string; name?: string; password?: string }>()\n if (!email?.trim()) return c.json({ error: 'email is required' }, 400)\n try {\n return c.json(await createUser(email.trim(), name?.trim(), password?.trim()), 201)\n } catch (err) {\n const msg = err instanceof Error ? err.message : ''\n if (msg.includes('UNIQUE')) return c.json({ error: 'Email already registered' }, 409)\n throw err\n }\n})\n\napp.get('/admin/users', adminAuth, async (c) => c.json(await listUsers()))\n\napp.delete('/admin/users/:id', adminAuth, async (c) => {\n await deactivateUser(parseInt(c.req.param('id') ?? '0'))\n return c.json({ ok: true })\n})\n\napp.post('/admin/backfill-signup-credits', adminAuth, async (c) => {\n const users = await listUsers()\n\n let processed = 0\n let credited = 0\n let skipped = 0\n const users_credited: { id: number; email: string }[] = []\n\n for (const user of users) {\n processed++\n const alreadyHasGrant = await ledgerExistsForOperation(user.id, LedgerOperation.SIGNUP_GRANT)\n if (alreadyHasGrant) {\n skipped++\n } else {\n await grantSignupCredit(user.id)\n credited++\n users_credited.push({ id: user.id, email: user.email })\n }\n }\n\n return c.json({ processed, credited, skipped, users_credited })\n})\n\napp.post('/extract-url', auth, async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const bodyResult = ExtractUrlBodySchema.safeParse(raw)\n if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? 'Invalid request' }, 400)\n const { url, screenshot, screenshotDevice, extractBranding, downloadMedia, mediaTypes, allowLocal } = bodyResult.data\n\n if (!allowLocal) {\n const checked = await validatePublicHttpUrl(url, { field: 'URL' })\n if (checked.error || !checked.parsed) return c.json({ error: checked.error ?? 'Invalid URL' }, 400)\n } else {\n try { new URL(url) } catch { return c.json({ error: 'Invalid URL' }, 400) }\n }\n\n const canonicalUrl = (() => { try { return new URL(url).href } catch { return url } })()\n const user = c.get('user')\n const { ok: euOk, balance_mc: euBal } = await debitMc(user.id, MC_COSTS.page_scrape, LedgerOperation.EXTRACT_URL, new URL(canonicalUrl).hostname)\n if (!euOk) return c.json(insufficientBalanceResponse(euBal, MC_COSTS.page_scrape), 402)\n\n try {\n const kernelApiKey = process.env.KERNEL_API_KEY?.trim()\n const device = screenshotDevice === 'mobile' ? 'mobile' : 'desktop'\n\n const [result, pageData] = await Promise.all([\n extractKpo({ url: canonicalUrl, kernelApiKey }),\n (screenshot || extractBranding)\n ? capturePageData(canonicalUrl, { kernelApiKey, device, screenshot: !!screenshot, branding: !!extractBranding }).catch(() => null)\n : null,\n ])\n\n const screenshotBuf = pageData?.screenshot ?? null\n const brandingData = pageData?.branding ?? null\n\n let screenshotMeta: { savedPath: string; sizeBytes: number; device: string } | null = null\n if (screenshotBuf) {\n const outDir = join(homedir(), 'Downloads', 'mcp-scraper', 'screenshots')\n mkdirSync(outDir, { recursive: true })\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n const slug = canonicalUrl.replace(/^https?:\\/\\//, '').replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '').slice(0, 60)\n const filePath = join(outDir, `${stamp}-${slug}.png`)\n writeFileSync(filePath, screenshotBuf)\n screenshotMeta = { savedPath: filePath, sizeBytes: screenshotBuf.length, device }\n }\n\n const mediaMeta = downloadMedia\n ? await harvestPageMedia(result.bodyHtml, canonicalUrl, { types: (mediaTypes ?? ['image', 'video', 'audio']) as MediaType[] })\n : null\n\n await logRequestEvent({ userId: user.id, source: 'extract_url', status: 'done', query: canonicalUrl, resultCount: result.headings.length, result })\n return c.json({ ...result, screenshot: screenshotMeta, branding: brandingData, media: mediaMeta })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n await creditMc(user.id, MC_COSTS.page_scrape, LedgerOperation.EXTRACT_URL, 'failed call')\n await logRequestEvent({ userId: user.id, source: 'extract_url', status: 'failed', query: canonicalUrl, error: msg })\n return c.json({ error: msg }, 500)\n }\n})\n\napp.post('/map-urls', auth, async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const bodyResult = MapUrlsBodySchema.safeParse(raw)\n if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? 'Invalid request' }, 400)\n const body = bodyResult.data\n const checked = await validatePublicHttpUrl(body.url, { field: 'URL' })\n if (checked.error || !checked.parsed) return c.json({ error: checked.error ?? 'Invalid URL' }, 400)\n const parsed = checked.parsed\n const user = c.get('user')\n const { ok: mapOk, balance_mc: mapBal } = await debitMc(user.id, MC_COSTS.url_map, LedgerOperation.URL_MAP, parsed.hostname)\n if (!mapOk) return c.json(insufficientBalanceResponse(mapBal, MC_COSTS.url_map), 402)\n try {\n const result = await spiderSite({\n startUrl: parsed.href,\n maxUrls: Math.min(2000, Math.max(1, body.maxUrls ?? 500)),\n concurrency: Math.min(20, Math.max(1, body.concurrency ?? 12)),\n kernelApiKey: (body.browserFallback ?? body.kernelFallback) ? process.env.KERNEL_API_KEY : undefined,\n })\n await logRequestEvent({\n userId: user.id,\n source: 'map_urls',\n status: 'done',\n query: parsed.href,\n resultCount: Array.isArray((result as any).urls) ? (result as any).urls.length : null,\n result,\n })\n return c.json(result)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n await creditMc(user.id, MC_COSTS.url_map, LedgerOperation.URL_MAP_REFUND, 'failed call')\n await logRequestEvent({\n userId: user.id,\n source: 'map_urls',\n status: 'failed',\n query: parsed.href,\n error: msg,\n })\n return c.json({ error: msg }, 500)\n }\n})\n\napp.post('/extract-site', auth, async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const bodyResult = ExtractSiteBodySchema.safeParse(raw)\n if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? 'Invalid request' }, 400)\n const body = bodyResult.data\n const checked = await validatePublicHttpUrl(body.url, { field: 'URL' })\n if (checked.error || !checked.parsed) return c.json({ error: checked.error ?? 'Invalid URL' }, 400)\n const parsed = checked.parsed\n const user = c.get('user')\n const siteHoldMc = MC_COSTS.page_scrape * 10\n const { ok: siteOk, balance_mc: siteBal } = await debitMc(user.id, siteHoldMc, LedgerOperation.EXTRACT_SITE_HOLD, parsed.hostname)\n if (!siteOk) return c.json(insufficientBalanceResponse(siteBal, siteHoldMc), 402)\n try {\n const result = await extractSite({\n startUrl: parsed.href,\n maxPages: Math.min(200, Math.max(1, body.maxPages ?? 100)),\n kernelApiKey: (body.browserFallback ?? body.kernelFallback) ? process.env.KERNEL_API_KEY : undefined,\n })\n const pageCount = (result as { pages?: unknown[] }).pages?.length ?? 1\n const actualSiteMc = pageCount * MC_COSTS.page_scrape\n const siteDiff = siteHoldMc - actualSiteMc\n if (siteDiff > 0) await creditMc(user.id, siteDiff, LedgerOperation.EXTRACT_SITE_REFUND, 'overestimate refund')\n else if (siteDiff < 0) await debitMc(user.id, -siteDiff, LedgerOperation.EXTRACT_SITE, parsed.hostname)\n await logRequestEvent({\n userId: user.id,\n source: 'extract_site',\n status: 'done',\n query: parsed.href,\n resultCount: pageCount,\n result,\n })\n return c.json(result)\n } catch (err) {\n await creditMc(user.id, siteHoldMc, LedgerOperation.EXTRACT_SITE_REFUND, 'failed call')\n const msg = err instanceof Error ? err.message : String(err)\n await logRequestEvent({\n userId: user.id,\n source: 'extract_site',\n status: 'failed',\n query: parsed.href,\n error: msg,\n })\n return c.json({ error: msg }, 500)\n }\n})\n\napp.post('/billing/checkout', requireAllowedOrigin, sessionAuth, async (c) => {\n try {\n const user = c.get('sessionUser')\n const raw = await c.req.json()\n const parsed = BillingCheckoutBodySchema.safeParse(raw)\n if (!parsed.success) return c.json({ error: 'Invalid request' }, 400)\n const { priceId } = parsed.data\n if (!(priceId in BALANCE_PRICE_IDS)) return c.json({ error: 'Invalid price' }, 400)\n\n const secret = process.env.STRIPE_SECRET_KEY?.trim()\n if (!secret) return c.json({ error: 'Stripe is not configured.' }, 503)\n const stripeClient = new Stripe(secret, { apiVersion: STRIPE_API_VERSION })\n\n let customerId = user.stripe_customer_id\n if (!customerId) {\n const customer = await stripeClient.customers.create({ email: user.email })\n customerId = customer.id\n await setStripeCustomerId(user.id, customerId)\n }\n\n const session = await stripeClient.checkout.sessions.create({\n customer: customerId,\n mode: 'payment',\n line_items: [{ price: priceId, quantity: 1 }],\n ui_mode: 'embedded' as any,\n automatic_tax: { enabled: true },\n billing_address_collection: 'required',\n customer_update: { address: 'auto', name: 'auto' },\n tax_id_collection: { enabled: true },\n return_url: `${appOrigin()}/billing?session_id={CHECKOUT_SESSION_ID}`,\n })\n\n return c.json({ clientSecret: session.client_secret })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unable to start checkout.'\n console.error('[billing/checkout]', message)\n return c.json({ error: message }, 500)\n }\n})\n\napp.post('/billing/concurrency/checkout', requireAllowedOrigin, sessionAuth, async (c) => {\n try {\n const user = c.get('sessionUser')\n if (user.concurrency_stripe_sub_id) return c.json({ error: 'Already subscribed — cancel existing slot first to change.' }, 409)\n const secret = process.env.STRIPE_SECRET_KEY?.trim()\n if (!secret) return c.json({ error: 'Stripe is not configured.' }, 503)\n const stripeClient = new Stripe(secret, { apiVersion: STRIPE_API_VERSION })\n let customerId = user.stripe_customer_id\n if (!customerId) {\n const customer = await stripeClient.customers.create({ email: user.email })\n customerId = customer.id\n await setStripeCustomerId(user.id, customerId)\n }\n const session = await stripeClient.checkout.sessions.create({\n customer: customerId,\n mode: 'subscription',\n line_items: [{ price: CONCURRENCY_PRICE_ID, quantity: 1 }],\n ui_mode: 'embedded' as any,\n automatic_tax: { enabled: true },\n billing_address_collection: 'required',\n customer_update: { address: 'auto', name: 'auto' },\n tax_id_collection: { enabled: true },\n return_url: `${appOrigin()}/billing?slot_added=1`,\n })\n return c.json({ clientSecret: session.client_secret })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unable to start checkout.'\n console.error('[billing/concurrency/checkout]', message)\n return c.json({ error: message }, 500)\n }\n})\n\napp.post('/billing/concurrency/cancel', requireAllowedOrigin, sessionAuth, async (c) => {\n const user = c.get('sessionUser')\n if (!user.concurrency_stripe_sub_id) return c.json({ error: 'No active concurrency subscription.' }, 404)\n const stripeSecret = process.env.STRIPE_SECRET_KEY?.trim()\n if (!stripeSecret) return c.json({ error: 'Stripe is not configured.' }, 503)\n const stripeClient = new Stripe(stripeSecret, { apiVersion: STRIPE_API_VERSION })\n await stripeClient.subscriptions.cancel(user.concurrency_stripe_sub_id)\n await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1))\n await setConcurrencySubId(user.id, null)\n return c.json({ ok: true, concurrency_limit: user.extra_concurrency_slots })\n})\n\napp.get('/billing/balance', auth, async (c) => {\n const user = c.get('user')\n const balanceMc = await reconcileBalanceMc(user.id)\n const ledger = await getLedger(user.id, 20)\n const freeCredits = await getFreeCreditBreakdown(user.id)\n return c.json({\n balance_mc: balanceMc,\n balance_credits: balanceMc / 1_000,\n free_credits: freeCredits,\n ledger,\n })\n})\n\napp.post('/billing/credits', auth, async (c) => {\n const user = c.get('user')\n const balanceMc = await reconcileBalanceMc(user.id)\n const body = await c.req.json().catch(() => ({})) as { item?: string; includeLedger?: boolean }\n const query = body.item?.trim().toLowerCase()\n const costs = CREDIT_COST_CATALOG.map(({ aliases, ...cost }) => cost)\n const matchedCost = query\n ? CREDIT_COST_CATALOG.find(cost =>\n cost.label.toLowerCase().includes(query)\n || cost.key.toLowerCase() === query\n || cost.aliases.some(alias => alias.toLowerCase().includes(query) || query.includes(alias.toLowerCase())),\n )\n : undefined\n const ledger = body.includeLedger\n ? (await getLedger(user.id, 10)).map(row => ({\n amount_mc: row.amount_mc,\n operation: row.operation,\n description: row.description,\n created_at: row.created_at,\n }))\n : undefined\n\n return c.json({\n balance_mc: balanceMc,\n balance_credits: balanceMc / 1_000,\n item: body.item ?? null,\n matched_cost: matchedCost ? (({ aliases, ...cost }) => cost)(matchedCost) : null,\n costs,\n ledger,\n })\n})\n\napp.get('/cron/tick', async (c) => {\n const secret = c.req.header('authorization')\n if (!process.env.CRON_SECRET || secret !== `Bearer ${process.env.CRON_SECRET}`) {\n return c.json({ error: 'Unauthorized' }, 401)\n }\n const { drainQueue } = await import('./worker.js')\n const budget = { maxJobs: 10, deadlineMs: Date.now() + 280_000 }\n const [results, sweepResult] = await Promise.all([\n drainQueue(budget),\n runMonthlyRefreshSweep(),\n ])\n return c.json({ drained: results.length, results, sweepResult })\n})\n\napp.on(['GET', 'POST', 'PUT'], '/api/inngest', serveInngest({ client: inngest, functions: [siteAuditFn] }))\napp.route('/api/internal/site-architecture-auditor', siteAuditApp)\napp.route('/youtube', youtubeApp)\napp.route('/screenshot', screenshotApp)\napp.route('/facebook', facebookAdApp)\napp.route('/maps', mapsApp)\napp.route('/serp-intelligence', serpIntelligenceApp)\napp.route('/mcp', mcpApp)\napp.route('/stripe', stripeApp)\n\nif (!process.env.INNGEST_EVENT_KEY) {\n startSiteAuditWorker()\n}\n\napp.get('/font-test', (c) => {\n return c.html(`<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Section Title Font Test</title>\n<style>\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Lazer-Trial.woff2\") format(\"woff2\"); font-weight:100; font-style:normal; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Lazer-Italic-Trial.woff2\") format(\"woff2\"); font-weight:100; font-style:italic; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Thin-Trial.woff2\") format(\"woff2\"); font-weight:200; font-style:normal; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Thin-Italic-Trial.woff2\") format(\"woff2\"); font-weight:200; font-style:italic; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Light-Trial.woff2\") format(\"woff2\"); font-weight:300; font-style:normal; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Light-Italic-Trial.woff2\") format(\"woff2\"); font-weight:300; font-style:italic; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Regular-Trial.woff2\") format(\"woff2\"); font-weight:400; font-style:normal; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Regular-Italic-Trial.woff2\") format(\"woff2\"); font-weight:400; font-style:italic; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Medium-Trial.woff2\") format(\"woff2\"); font-weight:500; font-style:normal; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Medium-Italic-Trial.woff2\") format(\"woff2\"); font-weight:500; font-style:italic; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Bold-Trial.woff2\") format(\"woff2\"); font-weight:700; font-style:normal; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Bold-Italic-Trial.woff2\") format(\"woff2\"); font-weight:700; font-style:italic; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Black-Trial.woff2\") format(\"woff2\"); font-weight:900; font-style:normal; }\n @font-face { font-family:\"GTF\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Black-Italic-Trial.woff2\") format(\"woff2\"); font-weight:900; font-style:italic; }\n @font-face { font-family:\"GTF-XP\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Black-Trial.woff2\") format(\"woff2\"); font-weight:900; font-style:normal; }\n @font-face { font-family:\"GTF-XP\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Black-Italic-Trial.woff2\") format(\"woff2\"); font-weight:900; font-style:italic; }\n @font-face { font-family:\"GTF-XP\"; src:url(\"/fonts/gt-flexa/GT-Flexa-Expanded-Bold-Trial.woff2\") format(\"woff2\"); font-weight:700; font-style:normal; }\n\n *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\n body{font-family:\"GTF\",sans-serif;background:#f5f4f0;color:#111;padding:40px 24px 80px}\n h2{font-family:\"GTF-XP\",sans-serif;font-weight:900;font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:#aaa;margin:48px 0 18px}\n h2:first-of-type{margin-top:0}\n .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:18px}\n .card{background:white;border-radius:10px;padding:22px 22px 18px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n .lbl{font-size:.65rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:#ccc;margin-bottom:12px;border-bottom:1px solid #f0f0f0;padding-bottom:8px}\n .t{font-family:\"GTF-XP\",sans-serif;font-weight:900;font-size:clamp(1.35rem,2.6vw,1.75rem);line-height:1.12;letter-spacing:-.02em;color:#111}\n .t em{color:#c0392b;font-style:normal}\n .meta{font-size:.68rem;color:#ccc;margin-top:10px;font-family:monospace}\n\n .u1 em{font-family:\"GTF\";font-weight:100;font-style:normal}\n .u2 em{font-family:\"GTF\";font-weight:200;font-style:normal}\n .u3 em{font-family:\"GTF\";font-weight:300;font-style:normal}\n .u4 em{font-family:\"GTF\";font-weight:400;font-style:normal}\n .u5 em{font-family:\"GTF\";font-weight:500;font-style:normal}\n .u6 em{font-family:\"GTF\";font-weight:700;font-style:normal}\n .u7 em{font-family:\"GTF\";font-weight:900;font-style:normal}\n\n .i1 em{font-family:\"GTF\";font-weight:100;font-style:italic}\n .i2 em{font-family:\"GTF\";font-weight:200;font-style:italic}\n .i3 em{font-family:\"GTF\";font-weight:300;font-style:italic}\n .i4 em{font-family:\"GTF\";font-weight:400;font-style:italic}\n .i5 em{font-family:\"GTF\";font-weight:500;font-style:italic}\n .i6 em{font-family:\"GTF\";font-weight:700;font-style:italic}\n .i7 em{font-family:\"GTF\";font-weight:900;font-style:italic}\n</style>\n</head>\n<body>\n\n<h2>Standard — upright (color only)</h2>\n<div class=\"grid\">\n <div class=\"card\"><div class=\"lbl\">Lazer · 100</div><div class=\"t u1\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 100 · normal</div></div>\n <div class=\"card\"><div class=\"lbl\">Thin · 200</div><div class=\"t u2\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 200 · normal</div></div>\n <div class=\"card\"><div class=\"lbl\">Light · 300</div><div class=\"t u3\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 300 · normal</div></div>\n <div class=\"card\"><div class=\"lbl\">Regular · 400</div><div class=\"t u4\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 400 · normal</div></div>\n <div class=\"card\"><div class=\"lbl\">Medium · 500</div><div class=\"t u5\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 500 · normal</div></div>\n <div class=\"card\"><div class=\"lbl\">Bold · 700</div><div class=\"t u6\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 700 · normal</div></div>\n <div class=\"card\"><div class=\"lbl\">Black · 900</div><div class=\"t u7\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 900 · normal</div></div>\n</div>\n\n<h2>Standard — italic</h2>\n<div class=\"grid\">\n <div class=\"card\"><div class=\"lbl\">Lazer Italic · 100</div><div class=\"t i1\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 100 · italic</div></div>\n <div class=\"card\"><div class=\"lbl\">Thin Italic · 200</div><div class=\"t i2\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 200 · italic</div></div>\n <div class=\"card\"><div class=\"lbl\">Light Italic · 300</div><div class=\"t i3\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 300 · italic</div></div>\n <div class=\"card\"><div class=\"lbl\">Regular Italic · 400</div><div class=\"t i4\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 400 · italic</div></div>\n <div class=\"card\"><div class=\"lbl\">Medium Italic · 500</div><div class=\"t i5\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 500 · italic</div></div>\n <div class=\"card\"><div class=\"lbl\">Bold Italic · 700</div><div class=\"t i6\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 700 · italic</div></div>\n <div class=\"card\"><div class=\"lbl\">Black Italic · 900</div><div class=\"t i7\">Why Claude Behaves Differently <em>(And Why That's Complicated.)</em></div><div class=\"meta\">GTF · 900 · italic</div></div>\n</div>\n\n</body>\n</html>`)\n})\n\napp.get('/blog/:slug/og.png', async (c) => {\n const post = blogPosts.find(p => p.slug === c.req.param('slug'))\n if (!post) return c.notFound()\n const png = await generateOgImage(post)\n return new Response(new Uint8Array(png), {\n headers: { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=604800, immutable' },\n })\n})\n\napp.get('/blog/:slug', (c) => {\n const post = blogPosts.find(p => p.slug === c.req.param('slug'))\n if (!post) return c.html('<h1>Not found</h1>', 404)\n return c.html(renderPost(post))\n})\napp.get('/blog/:slug/', (c) => {\n const post = blogPosts.find(p => p.slug === c.req.param('slug'))\n if (!post) return c.html('<h1>Not found</h1>', 404)\n return c.html(renderPost(post))\n})\n\nexport { app }\n","import { isIP } from 'node:net'\nimport { lookup } from 'node:dns/promises'\n\nexport function isPrivateIpAddress(address: string): boolean {\n const kind = isIP(address)\n if (kind === 4) {\n const parts = address.split('.').map(Number)\n const [a, b] = parts\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168)\n )\n }\n if (kind === 6) {\n const lower = address.toLowerCase()\n return lower === '::1' || lower.startsWith('fc') || lower.startsWith('fd') || lower.startsWith('fe80:')\n }\n return false\n}\n\nexport async function resolvesToPrivateAddress(hostname: string): Promise<boolean> {\n const host = hostname.toLowerCase()\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local')) return true\n if (isPrivateIpAddress(host)) return true\n try {\n const addresses = await lookup(host, { all: true, verbatim: true })\n return addresses.some((entry) => isPrivateIpAddress(entry.address))\n } catch {\n return false\n }\n}\n\nexport async function validatePublicHttpUrl(raw: string, opts: { field: string; requireHttps?: boolean }): Promise<{ parsed?: URL; error?: string }> {\n let parsed: URL\n try {\n parsed = new URL(raw.trim())\n } catch {\n return { error: `Invalid ${opts.field}` }\n }\n const allowedProtocols = opts.requireHttps ? ['https:'] : ['http:', 'https:']\n if (!allowedProtocols.includes(parsed.protocol)) {\n return { error: opts.requireHttps ? `${opts.field} must use https` : `${opts.field} must use http or https` }\n }\n if (await resolvesToPrivateAddress(parsed.hostname)) {\n return { error: `${opts.field} must resolve to a public internet host` }\n }\n return { parsed }\n}\n","import TurndownService from 'turndown'\nimport { fetchWithKernel } from './kernel-fetch.js'\nimport { validatePublicHttpUrl } from './url-utils.js'\nimport type { KpoExtractUrlResult } from './schemas.js'\n\nexport interface KpoExtractOptions {\n url: string\n kernelApiKey?: string\n}\n\nexport interface KpoExtractOutput {\n url: string\n fetchedVia: 'fetch' | 'headless'\n durationMs: number\n plainFetchMs: number\n title: string | null\n meta: Record<string, string>\n headings: { level: number; text: string }[]\n schema: unknown[]\n kpo: KpoExtractUrlResult\n bodyHtml: string\n bodyMarkdown: string\n}\n\nexport async function extractKpo(opts: KpoExtractOptions): Promise<KpoExtractOutput> {\n const start = Date.now()\n const parsed = new URL(opts.url)\n\n const tryPlainFetch = async (): Promise<string | null> => {\n try {\n let target = parsed.href\n for (let redirects = 0; redirects < 5; redirects++) {\n const res = await fetch(target, {\n headers: { 'User-Agent': 'Mozilla/5.0 (compatible; ThorbitBot/1.0)' },\n signal: AbortSignal.timeout(15_000),\n redirect: 'manual',\n })\n if (res.status >= 300 && res.status < 400) {\n const location = res.headers.get('location')\n if (!location) return null\n const next = new URL(location, target).href\n const checkedRedirect = await validatePublicHttpUrl(next, { field: 'redirect URL' })\n if (checkedRedirect.error || !checkedRedirect.parsed) return null\n target = checkedRedirect.parsed.href\n continue\n }\n if (!res.ok) return null\n const text = await res.text()\n if (text.length < 2000) return null\n const stripped = text\n .replace(/<(script|style|noscript|svg|iframe|canvas)[^>]*>[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<(nav|header|footer|aside)[^>]*>[\\s\\S]*?<\\/\\1>/gi, '')\n const contentBlock =\n stripped.match(/<main[^>]*>([\\s\\S]*?)<\\/main>/i)?.[1] ??\n stripped.match(/<article[^>]*>([\\s\\S]*?)<\\/article>/i)?.[1] ??\n stripped.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i)?.[1] ?? ''\n const visibleContent = contentBlock.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ').trim()\n if (visibleContent.length < 300) return null\n return text\n }\n return null\n } catch {\n return null\n }\n }\n\n let html: string\n let fetchedVia: 'fetch' | 'headless' = 'fetch'\n\n const plainFetchStart = Date.now()\n const plainHtml = await tryPlainFetch()\n const plainFetchMs = Date.now() - plainFetchStart\n if (plainHtml) {\n html = plainHtml\n } else if (opts.kernelApiKey) {\n html = await fetchWithKernel(parsed.href)\n fetchedVia = 'headless'\n } else {\n throw new Error('Fetch failed: site blocked plain HTTP and no headless browser backend is configured')\n }\n\n const stripTags = (s: string) => s.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ').trim()\n const attr = (tag: string, name: string) => {\n const m = tag.match(new RegExp(`${name}\\\\s*=\\\\s*(?:\"([^\"]*?)\"|'([^']*?)'|([^\\\\s>]+))`, 'i'))\n return m ? (m[1] ?? m[2] ?? m[3] ?? '') : null\n }\n\n const titleMatch = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i)\n const title = titleMatch ? stripTags(titleMatch[1]) : null\n\n const meta: Record<string, string> = {}\n for (const m of html.matchAll(/<meta\\s[^>]+>/gi)) {\n const tag = m[0]\n const name = attr(tag, 'name') || attr(tag, 'property')\n const content = attr(tag, 'content')\n if (name && content) meta[name.toLowerCase()] = content\n }\n\n const headings: { level: number; text: string }[] = []\n for (const m of html.matchAll(/<(h[1-6])[^>]*>([\\s\\S]*?)<\\/\\1>/gi)) {\n const level = parseInt(m[1].slice(1))\n const text = stripTags(m[2])\n if (text) headings.push({ level, text })\n }\n\n const sanitizeJsonLd = (s: string) => s.replace(/[\\x00-\\x09\\x0b\\x0c\\x0e-\\x1f]/g, ' ')\n\n const parseJsonLd = (s: string): unknown | null => {\n const clean = sanitizeJsonLd(s.trim())\n try {\n return JSON.parse(clean)\n } catch (e) {\n const pos = /at position (\\d+)/.exec((e as Error).message)?.[1]\n if (pos) {\n try { return JSON.parse(clean.slice(0, Number(pos))) } catch { }\n }\n return null\n }\n }\n\n const schema: unknown[] = []\n for (const m of html.matchAll(/<script[^>]+type\\s*=\\s*[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi)) {\n const raw = parseJsonLd(m[1])\n if (raw === null) continue\n if (Array.isArray(raw)) schema.push(...raw)\n else schema.push(raw)\n }\n\n const SOCIAL_DOMAINS = [\n 'facebook.com', 'fb.com', 'twitter.com', 'x.com', 'linkedin.com',\n 'instagram.com', 'youtube.com', 'tiktok.com', 'pinterest.com',\n 'yelp.com', 'bbb.org', 'alignable.com', 'nextdoor.com',\n 'thumbtack.com', 'angi.com', 'angieslist.com', 'houzz.com',\n 'tripadvisor.com', 'google.com/maps', 'maps.google.com',\n 'trustpilot.com', 'mapquest.com', 'apple.com/maps', 'waze.com',\n 'about.me', 'crunchbase.com', 'sites.google.com',\n ]\n\n const htmlSocialLinks: string[] = []\n for (const m of html.matchAll(/<a\\s[^>]*href\\s*=\\s*[\"']([^\"']+)[\"'][^>]*>/gi)) {\n const href = m[1]\n if (SOCIAL_DOMAINS.some(d => href.includes(d))) {\n try {\n const u = new URL(href)\n if ((u.protocol === 'http:' || u.protocol === 'https:') && !htmlSocialLinks.includes(href)) {\n htmlSocialLinks.push(href)\n }\n } catch { }\n }\n }\n\n type SchemaNode = Record<string, unknown>\n const flatNodes: SchemaNode[] = schema.flatMap((s: unknown) => {\n const obj = s as SchemaNode\n return (obj?.['@graph'] as SchemaNode[] | undefined) ?? [obj]\n })\n\n const ENTITY_TYPES = [\n 'LocalBusiness', 'Organization', 'Person', 'Corporation',\n 'MedicalOrganization', 'ProfessionalService', 'LegalService',\n 'HealthAndBeautyBusiness', 'HomeAndConstructionBusiness',\n 'FoodEstablishment', 'Store', 'Hotel', 'AutomotiveBusiness',\n 'MarketingAgency', 'Plumber', 'HVACBusiness', 'ElectricalContractor',\n 'Dentist', 'Physician', 'LodgingBusiness', 'AutoDealer',\n 'RealEstateAgent', 'FinancialService', 'InsuranceAgency', 'Accountant',\n 'AccountingService', 'Attorney', 'EducationalOrganization',\n 'GovernmentOrganization', 'NGO', 'SportsOrganization',\n ]\n\n const entityNode = flatNodes.find((n: SchemaNode) => {\n const t = [n['@type']].flat() as string[]\n return t.some(x => ENTITY_TYPES.includes(x))\n }) ?? null\n\n const str = (v: unknown): string | null => typeof v === 'string' && v ? v : null\n const strs = (v: unknown): string[] => [v].flat().filter((x): x is string => typeof x === 'string' && !!x)\n\n const presentSchemaTypes = [...new Set(\n flatNodes.flatMap(n => ([n['@type']].flat() as string[]).filter(Boolean))\n )]\n const microdataTypes = [...new Set(\n [...html.matchAll(/itemtype\\s*=\\s*[\"']https?:\\/\\/schema\\.org\\/([^\"'\\s]+)[\"']/gi)].map((m) => m[1])\n )]\n\n const faqNode = flatNodes.find(n => ([n['@type']].flat() as string[]).includes('FAQPage')) ?? null\n const ratingNode = flatNodes.find(n =>\n ([n['@type']].flat() as string[]).some(t => t === 'AggregateRating' || t === 'Rating')\n ) ?? null\n const faqCount = faqNode ? ((faqNode.mainEntity as unknown[]) ?? []).length : 0\n const aggregateRating = ratingNode ? {\n value: str(ratingNode.ratingValue),\n best: str(ratingNode.bestRating) ?? null,\n count: str(ratingNode.reviewCount) ?? str(ratingNode.ratingCount) ?? null,\n } : null\n\n const canonicalMatch =\n html.match(/<link[^>]+rel\\s*=\\s*[\"']canonical[\"'][^>]+href\\s*=\\s*[\"']([^\"']+)[\"']/i) ??\n html.match(/<link[^>]+href\\s*=\\s*[\"']([^\"']+)[\"'][^>]+rel\\s*=\\s*[\"']canonical[\"']/i)\n const canonicalUrl = canonicalMatch ? canonicalMatch[1] : null\n\n const websiteNode = flatNodes.find(n => ([n['@type']].flat() as string[]).includes('WebSite')) ?? null\n const hasSearchAction = websiteNode\n ? ([websiteNode.potentialAction].flat().filter(Boolean) as SchemaNode[])\n .some(a => ([a['@type']].flat() as string[]).some(t => t.includes('SearchAction')))\n : false\n\n const nodeAreaServed = (node: SchemaNode): string[] => {\n const v = node.areaServed\n if (!v) return []\n if (typeof v === 'string') return [v]\n if (Array.isArray(v)) return v.map(x => (typeof x === 'string' ? x : str((x as SchemaNode).name) ?? '')).filter(Boolean)\n if (typeof v === 'object') return [str((v as SchemaNode).name) ?? str((v as SchemaNode)['@type']) ?? ''].filter(Boolean)\n return []\n }\n const unescape = (s: string) =>\n s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/&#(\\d+);/g, (_, n) => String.fromCharCode(parseInt(n)))\n\n let kpo: KpoExtractUrlResult\n\n if (entityNode) {\n const addrNode = (entityNode.address ?? {}) as SchemaNode\n const address = addrNode.streetAddress\n ? [addrNode.streetAddress, addrNode.addressLocality, addrNode.addressRegion, addrNode.postalCode, addrNode.addressCountry]\n .filter(Boolean).map(x => unescape(String(x))).join(', ')\n : str(entityNode.address)\n\n const logoNode = (entityNode.logo ?? {}) as SchemaNode\n const logoUrl = str(logoNode.url) ?? str(logoNode.contentUrl) ?? str(entityNode.logo)\n\n const sameAsSchema = strs(entityNode.sameAs).map(unescape)\n const sameAsMerged = [...new Set([...sameAsSchema, ...htmlSocialLinks])]\n\n const people: { name: string; role: string }[] = []\n for (const key of ['founders', 'founder', 'employee', 'member'] as const) {\n const entries = [entityNode[key]].flat().filter(Boolean) as SchemaNode[]\n for (const f of entries) {\n const name = str(f.name) ?? (typeof f === 'string' ? f : null)\n if (name && !people.find(p => p.name === name)) people.push({ name: unescape(name), role: key })\n }\n }\n\n const types = strs(entityNode['@type'])\n const schemaId = str(entityNode['@id'])\n const schemaUrl = str(entityNode.url)\n const areaServed = nodeAreaServed(entityNode)\n const hasWikipedia = sameAsMerged.some(u => u.includes('wikipedia.org'))\n const hasWikidata = sameAsMerged.some(u => u.includes('wikidata.org'))\n const napScore = [str(entityNode.name), address, str(entityNode.telephone)].filter(Boolean).length\n\n const missing: string[] = []\n if (!str(entityNode.name)) missing.push('name')\n if (!str(entityNode.foundingDate)) missing.push('foundingDate')\n if (!address) missing.push('address')\n if (!str(entityNode.telephone)) missing.push('telephone')\n if (!str(entityNode.email)) missing.push('email')\n if (!logoUrl) missing.push('logo')\n if (!people.length) missing.push('founders / key people')\n if (!schemaId) missing.push('add @id to entity schema — e.g. https://yourdomain.com/#Organization')\n if (!areaServed.length) missing.push('add areaServed to define geographic service scope')\n if (!hasWikipedia) missing.push('no Wikipedia in sameAs — add when eligible coverage exists')\n if (!hasWikidata) missing.push('no Wikidata in sameAs — create a Wikidata item and add its URL')\n if (!hasSearchAction) missing.push('add WebSite schema with SearchAction for sitelinks searchbox eligibility')\n\n kpo = {\n entityName: str(entityNode.name) ? unescape(str(entityNode.name)!) : null,\n type: types,\n foundingDate: str(entityNode.foundingDate) ?? str((entityNode as SchemaNode).foundingYear) ?? null,\n address,\n phone: str(entityNode.telephone) ?? null,\n email: str(entityNode.email) ? unescape(str(entityNode.email)!) : null,\n logo: logoUrl,\n sameAs: sameAsMerged,\n socialLinks: htmlSocialLinks,\n keyPeople: people,\n openingHours: strs(entityNode.openingHours),\n description: str(entityNode.description) ? unescape(str(entityNode.description)!) : (meta['description'] ?? null),\n schemaId,\n schemaUrl,\n areaServed: areaServed.length ? areaServed : null,\n hasWikipedia,\n hasWikidata,\n napScore,\n canonicalUrl,\n hasSearchAction,\n missingFields: missing,\n schemaTypes: presentSchemaTypes,\n microdataTypes,\n faqCount,\n aggregateRating,\n } satisfies KpoExtractUrlResult\n } else {\n const mdEntityType = microdataTypes.find(t => ENTITY_TYPES.includes(t)) ?? null\n const hasAnySchema = presentSchemaTypes.length > 0 || microdataTypes.length > 0\n let entitySchemaMsg: string\n if (mdEntityType) {\n entitySchemaMsg = `microdata @type ${mdEntityType} found — add matching JSON-LD for full eligibility`\n } else if (hasAnySchema) {\n const found = [...presentSchemaTypes, ...microdataTypes].join(', ')\n entitySchemaMsg = `schema found (${found}) but no entity type — add @type LocalBusiness or ProfessionalService`\n } else {\n entitySchemaMsg = 'no schema found — add JSON-LD with @type LocalBusiness or ProfessionalService'\n }\n const elseType = mdEntityType ? [mdEntityType] : []\n const elseSameAs = htmlSocialLinks\n kpo = {\n entityName: str(meta['og:site_name']) ?? null,\n type: elseType,\n foundingDate: null,\n address: null,\n phone: null,\n email: null,\n logo: str(meta['og:image']) ?? null,\n sameAs: elseSameAs,\n socialLinks: htmlSocialLinks,\n keyPeople: [],\n openingHours: [],\n description: str(meta['description']) ?? null,\n schemaId: null,\n schemaUrl: null,\n areaServed: null,\n hasWikipedia: elseSameAs.some(u => u.includes('wikipedia.org')),\n hasWikidata: elseSameAs.some(u => u.includes('wikidata.org')),\n napScore: 0,\n canonicalUrl,\n hasSearchAction,\n missingFields: [\n entitySchemaMsg,\n 'foundingDate', 'address', 'telephone', 'email', 'logo', 'founders / key people',\n 'add @id to entity schema — e.g. https://yourdomain.com/#Organization',\n 'add areaServed to define geographic service scope',\n 'no Wikipedia in sameAs — add when eligible coverage exists',\n 'no Wikidata in sameAs — create a Wikidata item and add its URL',\n ...(!hasSearchAction ? ['add WebSite schema with SearchAction for sitelinks searchbox eligibility'] : []),\n ],\n schemaTypes: presentSchemaTypes,\n microdataTypes,\n faqCount,\n aggregateRating,\n } satisfies KpoExtractUrlResult\n }\n\n const extractContentHtml = (raw: string): string => {\n const stripped = raw\n .replace(/<(script|style|noscript|svg|iframe|canvas)[^>]*>[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<(nav|header|footer|aside)[^>]*>[\\s\\S]*?<\\/\\1>/gi, '')\n const main = stripped.match(/<main[^>]*>([\\s\\S]*?)<\\/main>/i)\n if (main) return main[1]\n const body = stripped.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i)\n return body ? body[1] : stripped\n }\n\n const resolveImages = (fragment: string): string =>\n fragment.replace(/<img([^>]*?)\\/?>/gi, (_m, attrs) => {\n const src = (attrs.match(/\\bsrc\\s*=\\s*[\"']([^\"']*)[\"']/i) ?? [])[1] ?? ''\n if (src && !src.startsWith('data:')) return `<img${attrs}>`\n const real = (attrs.match(/data-(?:lazy-)?src\\s*=\\s*[\"']([^\"']+)[\"']/i) ?? [])[1]\n return real ? `<img src=\"${real}\">` : ''\n })\n\n const td = new TurndownService({ headingStyle: 'atx', bulletListMarker: '-', codeBlockStyle: 'fenced' })\n const bodyHtml = resolveImages(extractContentHtml(html))\n const bodyMarkdown = td.turndown(bodyHtml)\n .replace(/!\\[[^\\]]*\\]\\([^)]*\\)/g, '')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\[]\\([^)]*\\)/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n\n const durationMs = Date.now() - start\n\n return {\n url: parsed.href,\n fetchedVia,\n durationMs,\n plainFetchMs,\n title,\n meta,\n headings,\n schema,\n kpo,\n bodyHtml,\n bodyMarkdown,\n }\n}\n","import Kernel from '@onkernel/sdk'\nimport { chromium } from 'playwright'\n\nexport async function fetchWithKernel(url: string): Promise<string> {\n const apiKey = process.env.KERNEL_API_KEY\n if (!apiKey) throw new Error('Browser backend API key not set')\n\n const client = new Kernel({ apiKey })\n const kb = await client.browsers.create({ stealth: true, timeout_seconds: 60 })\n const browser = await chromium.connectOverCDP(kb.cdp_ws_url)\n\n try {\n const context = browser.contexts()[0] ?? await browser.newContext({\n userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',\n })\n const page = context.pages()[0] ?? await context.newPage()\n await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 })\n await page.waitForTimeout(2500)\n return await page.content()\n } finally {\n await browser.close().catch(() => {})\n await client.browsers.deleteByID(kb.session_id).catch(() => {})\n }\n}\n","import { chromium } from 'playwright'\nimport Kernel from '@onkernel/sdk'\nimport type { Page } from 'playwright'\nimport { extractBrandingFromPage, type BrandingData } from './branding-extractor.js'\n\nconst DESKTOP_VIEWPORT = { width: 1440, height: 900 }\nconst MOBILE_VIEWPORT = { width: 390, height: 844 }\n\nconst DESKTOP_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'\nconst MOBILE_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1'\n\nexport type ScreenshotDevice = 'desktop' | 'mobile'\n\nconst SCROLL_STEP = 900\nconst SETTLE_MS = 400\n\nexport async function scrollAndStitch(page: Page, device: ScreenshotDevice = 'desktop'): Promise<Buffer> {\n const vp = device === 'mobile' ? MOBILE_VIEWPORT : DESKTOP_VIEWPORT\n await page.setViewportSize(vp)\n\n await page.evaluate(() => {\n for (const img of Array.from(document.querySelectorAll('img[loading=\"lazy\"]')) as HTMLImageElement[]) {\n img.loading = 'eager'\n }\n })\n\n const totalH: number = await page.evaluate(() =>\n Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)\n )\n\n const strips: string[] = []\n let scrollY = 0\n\n while (scrollY < totalH) {\n await page.evaluate(y => window.scrollTo(0, y), scrollY)\n await page.waitForTimeout(SETTLE_MS)\n\n const remaining = totalH - scrollY\n const stripH = Math.min(SCROLL_STEP, remaining)\n\n const buf = await page.screenshot({\n type: 'png',\n clip: { x: 0, y: 0, width: vp.width, height: stripH },\n })\n strips.push(buf.toString('base64'))\n\n scrollY += SCROLL_STEP\n }\n\n await page.evaluate(() => window.scrollTo(0, 0))\n\n const base64 = await page.evaluate(\n async ({ strips, W, totalH, stripH }: { strips: string[]; W: number; totalH: number; stripH: number }) => {\n const canvas = document.createElement('canvas')\n canvas.width = W\n canvas.height = totalH\n const ctx = canvas.getContext('2d')!\n\n for (let i = 0; i < strips.length; i++) {\n const img = new Image()\n img.src = 'data:image/png;base64,' + strips[i]\n await new Promise<void>(r => { img.onload = () => r() })\n ctx.drawImage(img, 0, i * stripH)\n }\n\n return canvas.toDataURL('image/png').replace('data:image/png;base64,', '')\n },\n { strips, W: vp.width, totalH, stripH: SCROLL_STEP }\n )\n\n return Buffer.from(base64, 'base64')\n}\n\nexport interface PageCaptureResult {\n screenshot?: Buffer\n branding?: BrandingData\n}\n\nasync function captureLocal(url: string, device: ScreenshotDevice, wantScreenshot: boolean, wantBranding: boolean): Promise<PageCaptureResult> {\n const vp = device === 'mobile' ? MOBILE_VIEWPORT : DESKTOP_VIEWPORT\n const ua = device === 'mobile' ? MOBILE_UA : DESKTOP_UA\n const browser = await chromium.launch({ headless: true })\n const context = await browser.newContext({\n viewport: vp,\n locale: 'en-US',\n userAgent: ua,\n isMobile: device === 'mobile',\n })\n const page = await context.newPage()\n try {\n await page.goto(url, { waitUntil: 'networkidle', timeout: 60000 })\n await page.waitForTimeout(1500)\n const result: PageCaptureResult = {}\n if (wantBranding) result.branding = await extractBrandingFromPage(page)\n if (wantScreenshot) result.screenshot = await scrollAndStitch(page, device)\n return result\n } finally {\n await browser.close()\n }\n}\n\nasync function captureKernel(url: string, kernelApiKey: string, device: ScreenshotDevice, wantScreenshot: boolean, wantBranding: boolean): Promise<PageCaptureResult> {\n const kernel = new Kernel({ apiKey: kernelApiKey })\n const session = await kernel.browsers.create({ stealth: true, timeout_seconds: 240 })\n const browser = await chromium.connectOverCDP(session.cdp_ws_url)\n const vp = device === 'mobile' ? MOBILE_VIEWPORT : DESKTOP_VIEWPORT\n const ua = device === 'mobile' ? MOBILE_UA : DESKTOP_UA\n const context = browser.contexts()[0] ?? await browser.newContext({\n viewport: vp,\n userAgent: ua,\n isMobile: device === 'mobile',\n })\n const page = context.pages()[0] ?? await context.newPage()\n try {\n await page.goto(url, { waitUntil: 'networkidle', timeout: 60000 })\n await page.waitForTimeout(1500)\n const result: PageCaptureResult = {}\n if (wantBranding) result.branding = await extractBrandingFromPage(page)\n if (wantScreenshot) result.screenshot = await scrollAndStitch(page, device)\n return result\n } finally {\n await browser.close()\n }\n}\n\nexport async function captureScreenshot(url: string, kernelApiKey?: string, device: ScreenshotDevice = 'desktop'): Promise<Buffer> {\n const result = await capturePageData(url, { kernelApiKey, device, screenshot: true, branding: false })\n return result.screenshot!\n}\n\nexport async function extractBranding(url: string, kernelApiKey?: string, device: ScreenshotDevice = 'desktop'): Promise<BrandingData> {\n const result = await capturePageData(url, { kernelApiKey, device, screenshot: false, branding: true })\n return result.branding!\n}\n\nexport interface CapturePageDataOptions {\n kernelApiKey?: string\n device?: ScreenshotDevice\n screenshot?: boolean\n branding?: boolean\n}\n\nexport async function capturePageData(url: string, opts: CapturePageDataOptions = {}): Promise<PageCaptureResult> {\n const device = opts.device ?? 'desktop'\n const wantShot = opts.screenshot ?? false\n const wantBranding = opts.branding ?? false\n try {\n return await captureLocal(url, device, wantShot, wantBranding)\n } catch (localErr) {\n const msg = localErr instanceof Error ? localErr.message : String(localErr)\n if (!opts.kernelApiKey) throw new Error(`Local capture failed (${msg}) and no headless browser backend is configured`)\n return await captureKernel(url, opts.kernelApiKey, device, wantShot, wantBranding)\n }\n}\n","import type { Page } from 'playwright'\n\nexport interface BrandingColors {\n primary: string | null\n accent: string | null\n background: string | null\n text: string | null\n heading: string | null\n}\n\nexport interface BrandingFonts {\n heading: string | null\n body: string | null\n}\n\nexport interface BrandingAssets {\n logo: string | null\n favicon: string | null\n}\n\nexport interface BrandingData {\n colorScheme: 'light' | 'dark' | null\n colors: BrandingColors\n fonts: BrandingFonts\n assets: BrandingAssets\n}\n\nfunction rgbToHex(rgb: string): string | null {\n const m = rgb.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (!m) return null\n const r = parseInt(m[1], 10)\n const g = parseInt(m[2], 10)\n const b = parseInt(m[3], 10)\n return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('')\n}\n\nfunction luminance(hex: string): number {\n const m = hex.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i)\n if (!m) return 128\n const [r, g, b] = [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)]\n return 0.2126 * r + 0.7152 * g + 0.0722 * b\n}\n\nfunction isTransparentOrWhite(hex: string | null): boolean {\n if (!hex) return true\n if (hex === '#000000') return false\n const lum = luminance(hex)\n return lum > 240\n}\n\nfunction firstFont(fontFamily: string): string | null {\n if (!fontFamily) return null\n const first = fontFamily.split(',')[0].trim().replace(/['\"]/g, '')\n return first || null\n}\n\nexport async function extractBrandingFromPage(page: Page): Promise<BrandingData> {\n const evalScript = `\n (function() {\n function cs(el) { return el ? window.getComputedStyle(el) : null; }\n var navEl = document.querySelector('nav, header, [role=\"banner\"]');\n var bodyEl = document.body;\n var h1El = document.querySelector('h1');\n var btnEl = document.querySelector(\n 'a.btn-primary, button.btn-primary, .btn-primary, .cta-btn,' +\n 'a.button--primary, button.button--primary, [class*=\"btn-cta\"],' +\n '[class*=\"cta-button\"], .wp-block-button__link, [class*=\"hero\"] a'\n );\n var navStyle = cs(navEl);\n var bodyStyle = cs(bodyEl);\n var h1Style = cs(h1El);\n var btnStyle = cs(btnEl);\n var pageHost = window.location.hostname.replace(/^www\\./, '');\n function isSameDomain(src) {\n try { return new URL(src).hostname.replace(/^www\\./, '').endsWith(pageHost); } catch { return false; }\n }\n var logoSelectors = [\n 'header img[class*=\"logo\"]', 'nav img[class*=\"logo\"]',\n 'img[class*=\"site-logo\"]', 'img[class*=\"brand-logo\"]',\n 'img[alt*=\"logo\" i]', '.logo img', '#logo img',\n 'header img:first-of-type', 'nav img:first-of-type'\n ];\n var logoSrc = null;\n for (var i = 0; i < logoSelectors.length; i++) {\n var el = document.querySelector(logoSelectors[i]);\n if (el && el.src && isSameDomain(el.src)) { logoSrc = el.src; break; }\n }\n if (!logoSrc) {\n for (var j = 0; j < logoSelectors.length; j++) {\n var el2 = document.querySelector(logoSelectors[j]);\n if (el2 && el2.src) { logoSrc = el2.src; break; }\n }\n }\n var faviconEl = document.querySelector(\n 'link[rel~=\"icon\"], link[rel=\"shortcut icon\"], link[rel=\"apple-touch-icon\"]'\n );\n return {\n navBg: navStyle ? navStyle.backgroundColor : null,\n bodyBg: bodyStyle ? bodyStyle.backgroundColor : null,\n bodyColor: bodyStyle ? bodyStyle.color : null,\n h1Color: h1Style ? h1Style.color : null,\n btnBg: btnStyle ? btnStyle.backgroundColor : null,\n bodyFont: bodyStyle ? bodyStyle.fontFamily : null,\n h1Font: h1Style ? h1Style.fontFamily : null,\n logoSrc: logoSrc,\n faviconHref: faviconEl ? faviconEl.href : null,\n };\n })()\n `\n const raw = await page.evaluate(evalScript) as {\n navBg: string | null; bodyBg: string | null; bodyColor: string | null\n h1Color: string | null; btnBg: string | null; bodyFont: string | null\n h1Font: string | null; logoSrc: string | null; faviconHref: string | null\n }\n\n const navBgHex = rgbToHex(raw.navBg ?? '')\n const bodyBgHex = rgbToHex(raw.bodyBg ?? '')\n const primary = !isTransparentOrWhite(navBgHex) ? navBgHex : bodyBgHex\n const accent = rgbToHex(raw.btnBg ?? '')\n const text = rgbToHex(raw.bodyColor ?? '')\n const heading = rgbToHex(raw.h1Color ?? '')\n const background = bodyBgHex\n\n const scheme: 'light' | 'dark' | null = background\n ? luminance(background) > 128 ? 'light' : 'dark'\n : null\n\n return {\n colorScheme: scheme,\n colors: {\n primary: primary && !isTransparentOrWhite(primary) ? primary : null,\n accent: accent && !isTransparentOrWhite(accent) ? accent : null,\n background: background && !isTransparentOrWhite(background) ? background : null,\n text: text && text !== '#000000' ? text : null,\n heading: heading && heading !== '#000000' ? heading : null,\n },\n fonts: {\n body: firstFont(raw.bodyFont ?? ''),\n heading: firstFont(raw.h1Font ?? ''),\n },\n assets: {\n logo: raw.logoSrc || null,\n favicon: raw.faviconHref || null,\n },\n }\n}\n","import { createWriteStream, mkdirSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join, extname, basename } from 'node:path'\nimport { pipeline } from 'node:stream/promises'\nimport { Readable } from 'node:stream'\n\nconst AD_PATTERNS: string[] = [\n 'doubleclick.net', 'googlesyndication.com', 'googletagmanager.com',\n 'google-analytics.com', 'googletagservices.com', 'adservice.google',\n 'googletag.', 'pagead2.googlesyndication',\n 'facebook.net/tr', 'connect.facebook.net', 'fbcdn.net/rsrc',\n 'analytics.twitter.com', 'static.ads-twitter.com', 'ads.twitter.com',\n 't.co/i/adsct', 'pixel.advertising.com',\n 'hotjar.com', 'clarity.ms', 'quantserve.com', 'scorecardresearch.com',\n 'newrelic.com', 'nr-data.net', 'segment.io', 'segment.com',\n 'amplitude.com', 'mixpanel.com', 'heap.io', 'fullstory.com',\n 'moatads.com', 'criteo.com', 'adsrvr.org', 'rubiconproject.com',\n 'pubmatic.com', 'openx.net', 'appnexus.com', 'amazon-adsystem.com',\n 'media.net', 'yieldmo.com', 'triplelift.com', 'sharethrough.com',\n 'prebid.', 'smaato.net', 'indexworm.com', 'casalemedia.com',\n 'outbrain.com', 'taboola.com', 'revcontent.com', 'mgid.com',\n 'tawk.to', 'intercom.io', 'drift.com', 'hs-scripts.com',\n 'zopim.com', 'livechatinc.com', 'userlike.com',\n 'onetrust.com', 'cookielaw.org', 'cookieinformation.com', 'trustarc.com',\n '/ads/', '/ad/', '/banner/', '/banners/', '/pixel/', '/beacon/',\n '/tracking/', '/tracker/', '/remarketing/', '/conversion/',\n '1x1.gif', 'spacer.gif', 'blank.gif', 'transparent.gif',\n]\n\nconst IMAGE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif', '.avif', '.svg', '.tiff'])\nconst VIDEO_EXTS = new Set(['.mp4', '.webm', '.mov', '.avi', '.m4v', '.ogv', '.mkv'])\nconst AUDIO_EXTS = new Set(['.mp3', '.wav', '.ogg', '.aac', '.m4a', '.flac', '.opus'])\n\nexport type MediaType = 'image' | 'video' | 'audio'\n\nexport interface MediaAsset {\n url: string\n type: MediaType\n mimeType: string | null\n filename: string\n savedPath: string | null\n sizeBytes: number | null\n}\n\nexport interface MediaManifest {\n pageUrl: string\n outputDir: string | null\n assets: MediaAsset[]\n filteredCount: number\n totalFound: number\n}\n\nexport interface MediaExtractOptions {\n types?: MediaType[]\n outputDir?: string | null\n}\n\nfunction isAdUrl(url: string): boolean {\n const lower = url.toLowerCase()\n return AD_PATTERNS.some(p => lower.includes(p))\n}\n\nfunction isDataUri(url: string): boolean {\n return url.startsWith('data:')\n}\n\nfunction typeFromUrl(url: string): MediaType | null {\n try {\n const ext = extname(new URL(url).pathname).toLowerCase()\n if (IMAGE_EXTS.has(ext)) return 'image'\n if (VIDEO_EXTS.has(ext)) return 'video'\n if (AUDIO_EXTS.has(ext)) return 'audio'\n } catch { /* non-parseable URL */ }\n return null\n}\n\nfunction typeFromMime(mime: string): MediaType | null {\n const lower = mime.toLowerCase()\n if (lower.startsWith('image/')) return 'image'\n if (lower.startsWith('video/')) return 'video'\n if (lower.startsWith('audio/')) return 'audio'\n return null\n}\n\nfunction resolveUrl(raw: string, base: string): string | null {\n if (!raw || isDataUri(raw)) return null\n try { return new URL(raw, base).href } catch { return null }\n}\n\nfunction safeFilename(url: string, index: number): string {\n try {\n const u = new URL(url)\n const base = basename(u.pathname).replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 80)\n return base || `asset-${index}`\n } catch {\n return `asset-${index}`\n }\n}\n\nexport function extractMediaUrls(html: string, baseUrl: string): string[] {\n const seen = new Set<string>()\n const urls: string[] = []\n\n const add = (raw: string) => {\n const resolved = resolveUrl(raw.trim(), baseUrl)\n if (!resolved || seen.has(resolved) || isAdUrl(resolved)) return\n seen.add(resolved)\n urls.push(resolved)\n }\n\n for (const m of html.matchAll(/<img\\s[^>]*>/gi)) {\n const tag = m[0]\n for (const attr of ['src', 'data-src', 'data-lazy-src', 'data-original']) {\n const v = (tag.match(new RegExp(`\\\\b${attr}\\\\s*=\\\\s*[\"']([^\"']+)[\"']`, 'i')) ?? [])[1]\n if (v) add(v)\n }\n const srcset = (tag.match(/\\bsrcset\\s*=\\s*[\"']([^\"']+)[\"']/i) ?? [])[1]\n if (srcset) {\n for (const part of srcset.split(',')) {\n const u = part.trim().split(/\\s+/)[0]\n if (u) add(u)\n }\n }\n }\n\n for (const m of html.matchAll(/<source\\s[^>]*>/gi)) {\n const tag = m[0]\n const src = (tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']+)[\"']/i) ?? [])[1]\n if (src) add(src)\n const srcset = (tag.match(/\\bsrcset\\s*=\\s*[\"']([^\"']+)[\"']/i) ?? [])[1]\n if (srcset) {\n for (const part of srcset.split(',')) {\n const u = part.trim().split(/\\s+/)[0]\n if (u) add(u)\n }\n }\n }\n\n for (const m of html.matchAll(/<video\\s[^>]*>/gi)) {\n const tag = m[0]\n for (const attr of ['src', 'poster']) {\n const v = (tag.match(new RegExp(`\\\\b${attr}\\\\s*=\\\\s*[\"']([^\"']+)[\"']`, 'i')) ?? [])[1]\n if (v) add(v)\n }\n }\n\n for (const m of html.matchAll(/<audio\\s[^>]*>/gi)) {\n const tag = m[0]\n const v = (tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']+)[\"']/i) ?? [])[1]\n if (v) add(v)\n }\n\n for (const m of html.matchAll(/<meta\\s[^>]*>/gi)) {\n const tag = m[0]\n const prop = (tag.match(/\\b(?:property|name)\\s*=\\s*[\"']([^\"']+)[\"']/i) ?? [])[1]?.toLowerCase()\n if (prop === 'og:image' || prop === 'twitter:image') {\n const content = (tag.match(/\\bcontent\\s*=\\s*[\"']([^\"']+)[\"']/i) ?? [])[1]\n if (content) add(content)\n }\n }\n\n for (const m of html.matchAll(/background(?:-image)?\\s*:\\s*url\\(\\s*[\"']?([^\"')]+)[\"']?\\s*\\)/gi)) {\n add(m[1])\n }\n\n return urls\n}\n\nasync function downloadAsset(\n url: string,\n destDir: string,\n filename: string,\n): Promise<{ savedPath: string; sizeBytes: number; mimeType: string | null }> {\n const res = await fetch(url, {\n headers: { 'User-Agent': 'Mozilla/5.0 (compatible; ThorbitBot/1.0)' },\n signal: AbortSignal.timeout(15_000),\n })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n if (!res.body) throw new Error('Empty response body')\n\n const mimeType = res.headers.get('content-type')?.split(';')[0].trim() ?? null\n\n let dest = join(destDir, filename)\n if (mimeType && !extname(filename)) {\n const mimeExt: Record<string, string> = {\n 'image/jpeg': '.jpg', 'image/png': '.png', 'image/webp': '.webp',\n 'image/gif': '.gif', 'image/svg+xml': '.svg', 'image/avif': '.avif',\n 'video/mp4': '.mp4', 'video/webm': '.webm',\n 'audio/mpeg': '.mp3', 'audio/ogg': '.ogg', 'audio/wav': '.wav',\n }\n const ext = mimeExt[mimeType]\n if (ext) dest = dest + ext\n }\n\n const writer = createWriteStream(dest)\n await pipeline(Readable.fromWeb(res.body as import('stream/web').ReadableStream), writer)\n\n const { statSync } = await import('node:fs')\n const sizeBytes = statSync(dest).size\n\n return { savedPath: dest, sizeBytes, mimeType }\n}\n\nexport async function harvestPageMedia(\n html: string,\n pageUrl: string,\n options: MediaExtractOptions = {},\n): Promise<MediaManifest> {\n const types = options.types ?? ['image', 'video', 'audio']\n const typesSet = new Set(types)\n const rawUrls = extractMediaUrls(html, pageUrl)\n const totalFound = rawUrls.length\n\n const filteredUrls: string[] = []\n const kept: Array<{ url: string; type: MediaType }> = []\n\n for (const url of rawUrls) {\n const type = typeFromUrl(url)\n if (!type || !typesSet.has(type)) { filteredUrls.push(url); continue }\n kept.push({ url, type })\n }\n\n if (options.outputDir === null) {\n return {\n pageUrl,\n outputDir: null,\n assets: kept.map(({ url, type }, i) => ({\n url, type, mimeType: null, filename: safeFilename(url, i), savedPath: null, sizeBytes: null,\n })),\n filteredCount: filteredUrls.length,\n totalFound,\n }\n }\n\n const domain = (() => { try { return new URL(pageUrl).hostname.replace(/^www\\./, '') } catch { return 'unknown' } })()\n const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)\n const outDir = options.outputDir ?? join(homedir(), 'Downloads', 'mcp-scraper', 'media', `${stamp}-${domain}`)\n mkdirSync(outDir, { recursive: true })\n\n const assets: MediaAsset[] = []\n\n await Promise.allSettled(\n kept.map(async ({ url, type }, i) => {\n const filename = safeFilename(url, i)\n try {\n const { savedPath, sizeBytes, mimeType } = await downloadAsset(url, outDir, filename)\n const resolvedType = mimeType ? (typeFromMime(mimeType) ?? type) : type\n assets.push({ url, type: resolvedType, mimeType, filename: basename(savedPath), savedPath, sizeBytes })\n } catch {\n assets.push({ url, type, mimeType: null, filename, savedPath: null, sizeBytes: null })\n }\n })\n )\n\n assets.sort((a, b) => {\n if (a.savedPath && !b.savedPath) return -1\n if (!a.savedPath && b.savedPath) return 1\n return a.url.localeCompare(b.url)\n })\n\n return { pageUrl, outputDir: outDir, assets, filteredCount: filteredUrls.length, totalFound }\n}\n","import { fetchWithKernel } from './kernel-fetch.js'\n\nconst SKIP_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'ico', 'avif', 'bmp', 'tiff',\n 'css', 'js', 'mjs', 'map', 'woff', 'woff2', 'ttf', 'otf', 'eot',\n 'pdf', 'zip', 'tar', 'gz', 'rar', 'dmg', 'exe', 'apk',\n 'mp4', 'webm', 'mov', 'avi', 'mkv', 'mp3', 'wav', 'ogg', 'flac',\n 'xml', 'json', 'csv', 'txt', 'rss', 'atom',\n])\n\nconst SKIP_PATH_SEGMENTS = new Set([\n 'wp-login.php', 'wp-admin', 'wp-content', 'wp-includes', 'wp-json',\n 'login', 'logout', 'signin', 'sign-in', 'sign-up', 'signup', 'register',\n 'cart', 'checkout', 'my-account', 'account', 'dashboard', 'profile',\n 'feed', 'rss', 'sitemap.xml', 'robots.txt',\n 'cdn-cgi', '__', 'static', 'assets', 'dist', 'build',\n 'api', 'graphql', 'admin',\n])\n\nconst SKIP_PATH_PREFIXES = [\n '/wp-content/', '/wp-includes/', '/wp-json/', '/wp-admin/',\n '/cdn-cgi/', '/__/',\n '/feed/', '/rss/',\n '/api/', '/graphql/',\n]\n\nconst SKIP_QUERY_PARAMS = ['replytocom', 'p', 'preview', 'ver', 'v']\n\nconst KERNEL_RETRY_LIMIT = 15\nconst UA = 'Mozilla/5.0 (compatible; ThorbitBot/1.0; +https://thorbit.ai)'\n\nexport interface SpiderOptions {\n startUrl: string\n maxUrls?: number\n concurrency?: number\n timeoutMs?: number\n kernelApiKey?: string\n}\n\nexport interface DiscoveredUrl {\n url: string\n status: number | null\n linkedFrom: string\n via?: 'fetch' | 'browser'\n}\n\nexport interface SpiderResult {\n startUrl: string\n urls: DiscoveredUrl[]\n totalFound: number\n durationMs: number\n truncated: boolean\n browserRetries: number\n}\n\nexport function normalizeUrl(href: string, base: string): string | null {\n try {\n const u = new URL(href.trim(), base)\n if (u.protocol !== 'http:' && u.protocol !== 'https:') return null\n\n u.hash = ''\n\n const ext = u.pathname.split('.').pop()?.toLowerCase() ?? ''\n if (SKIP_EXTENSIONS.has(ext)) return null\n\n for (const prefix of SKIP_PATH_PREFIXES) {\n if (u.pathname.toLowerCase().startsWith(prefix)) return null\n }\n\n const segments = u.pathname.toLowerCase().split('/')\n for (const seg of segments) {\n if (SKIP_PATH_SEGMENTS.has(seg)) return null\n }\n\n for (const param of SKIP_QUERY_PARAMS) {\n u.searchParams.delete(param)\n }\n\n if (u.searchParams.has('s')) return null\n\n return u.href\n } catch {\n return null\n }\n}\n\nexport function extractLinks(html: string, base: string): string[] {\n const seen = new Set<string>()\n const links: string[] = []\n for (const m of html.matchAll(/href\\s*=\\s*[\"']([^\"'\\s>]+)/gi)) {\n const normalized = normalizeUrl(m[1], base)\n if (normalized && !seen.has(normalized)) {\n seen.add(normalized)\n links.push(normalized)\n }\n }\n return links\n}\n\nasync function fetchText(url: string, timeoutMs = 10_000): Promise<string | null> {\n try {\n const res = await fetch(url, {\n headers: { 'User-Agent': UA },\n signal: AbortSignal.timeout(timeoutMs),\n })\n if (!res.ok) return null\n return await res.text()\n } catch {\n return null\n }\n}\n\nfunction extractSitemapLocs(xml: string): string[] {\n const locs: string[] = []\n for (const m of xml.matchAll(/<loc>\\s*(https?:\\/\\/[^<\\s]+)\\s*<\\/loc>/gi)) {\n locs.push(m[1].trim())\n }\n return locs\n}\n\nasync function parseSitemap(url: string, startDomain: string, depth = 0): Promise<string[]> {\n if (depth > 3) return []\n const xml = await fetchText(url)\n if (!xml) return []\n\n if (xml.includes('<sitemapindex')) {\n const subUrls = extractSitemapLocs(xml)\n const nested = await Promise.all(\n subUrls.slice(0, 20).map(u => parseSitemap(u, startDomain, depth + 1))\n )\n return nested.flat()\n }\n\n return extractSitemapLocs(xml).filter(loc => baseDomain(loc) === startDomain)\n}\n\nasync function discoverSitemapUrls(startUrl: string, startDomain: string): Promise<string[]> {\n const origin = new URL(startUrl).origin\n const candidates: string[] = []\n\n const robotsTxt = await fetchText(`${origin}/robots.txt`)\n if (robotsTxt) {\n for (const m of robotsTxt.matchAll(/^Sitemap:\\s*(.+)$/gim)) {\n candidates.push(m[1].trim())\n }\n }\n\n for (const path of ['/sitemap.xml', '/wp-sitemap.xml', '/sitemap_index.xml']) {\n const u = `${origin}${path}`\n if (!candidates.includes(u)) candidates.push(u)\n }\n\n const allLocs = await Promise.all(\n candidates.slice(0, 8).map(u => parseSitemap(u, startDomain))\n )\n const seen = new Set<string>()\n const urls: string[] = []\n for (const loc of allLocs.flat()) {\n const normalized = normalizeUrl(loc, loc)\n if (normalized && !seen.has(normalized)) {\n seen.add(normalized)\n urls.push(normalized)\n }\n }\n return urls\n}\n\nasync function fetchPage(url: string, timeoutMs: number): Promise<{ html: string | null; status: number | null; finalUrl?: string }> {\n try {\n const res = await fetch(url, {\n headers: { 'User-Agent': UA, 'Accept': 'text/html,application/xhtml+xml' },\n signal: AbortSignal.timeout(timeoutMs),\n })\n if (!res.ok) return { html: null, status: res.status }\n const ct = res.headers.get('content-type') ?? ''\n if (!ct.includes('html')) return { html: null, status: res.status }\n const html = await res.text()\n return { html, status: res.status, finalUrl: res.url }\n } catch {\n return { html: null, status: null }\n }\n}\n\nfunction baseDomain(url: string): string {\n try { return new URL(url).hostname.replace(/^www\\./, '') } catch { return '' }\n}\n\nexport async function spiderSite(opts: SpiderOptions): Promise<SpiderResult> {\n const startMs = Date.now()\n const maxUrls = Math.min(opts.maxUrls ?? 500, 2000)\n const concurrency = Math.min(opts.concurrency ?? 12, 20)\n const timeoutMs = opts.timeoutMs ?? 8_000\n\n const startNorm = normalizeUrl(opts.startUrl, opts.startUrl) ?? opts.startUrl\n const startDomain = baseDomain(opts.startUrl)\n\n const visited = new Set<string>()\n const queue: Array<{ url: string; from: string }> = [{ url: startNorm, from: startNorm }]\n const results: DiscoveredUrl[] = []\n\n visited.add(startNorm)\n\n const sitemapUrls = await discoverSitemapUrls(opts.startUrl, startDomain)\n for (const loc of sitemapUrls) {\n if (!visited.has(loc)) {\n visited.add(loc)\n queue.push({ url: loc, from: 'sitemap' })\n }\n }\n\n while (queue.length > 0 && results.length < maxUrls) {\n const batch = queue.splice(0, concurrency)\n\n const settled = await Promise.all(\n batch.map(async ({ url, from }) => {\n const { html, status } = await fetchPage(url, timeoutMs)\n const discovered: DiscoveredUrl = { url, status, linkedFrom: from, via: 'fetch' }\n const children: Array<{ url: string; from: string }> = []\n\n if (html) {\n for (const link of extractLinks(html, url)) {\n if (baseDomain(link) !== startDomain) continue\n if (!visited.has(link)) {\n visited.add(link)\n children.push({ url: link, from: url })\n }\n }\n }\n\n return { discovered, children }\n }),\n )\n\n for (const { discovered, children } of settled) {\n results.push(discovered)\n for (const child of children) {\n if (results.length + queue.length < maxUrls * 2) {\n queue.push(child)\n }\n }\n }\n }\n\n let browserRetries = 0\n\n if (opts.kernelApiKey) {\n const retryTargets = results\n .filter(r => r.status === null || r.status === 403 || r.status === 429)\n .slice(0, KERNEL_RETRY_LIMIT)\n\n for (const target of retryTargets) {\n try {\n const html = await fetchWithKernel(target.url)\n target.status = 200\n target.via = 'browser'\n browserRetries++\n\n for (const link of extractLinks(html, target.url)) {\n if (baseDomain(link) !== startDomain) continue\n if (!visited.has(link) && results.length < maxUrls) {\n visited.add(link)\n results.push({ url: link, status: null, linkedFrom: target.url, via: 'fetch' })\n }\n }\n } catch { /**/ }\n }\n }\n\n return {\n startUrl: opts.startUrl,\n urls: results,\n totalFound: results.length,\n durationMs: Date.now() - startMs,\n truncated: queue.length > 0 || results.length >= maxUrls,\n browserRetries,\n }\n}\n","import { spiderSite, extractLinks } from './site-mapper.js'\nimport { fetchWithKernel } from './kernel-fetch.js'\nimport type { SpiderResult } from './site-mapper.js'\n\nconst UA = 'Mozilla/5.0 (compatible; ThorbitBot/1.0; +https://thorbit.ai)'\nconst EXTRACT_CONCURRENCY = 6\n\nexport interface PageData {\n url: string\n status: number | null\n via: 'fetch' | 'browser'\n title: string | null\n metaDescription: string | null\n h1: string | null\n headings: Array<{ level: number; text: string }>\n wordCount: number\n schemaTypes: string[]\n canonicalUrl: string | null\n internalLinks: number\n externalLinks: number\n}\n\nexport interface ExtractResult {\n startUrl: string\n pages: PageData[]\n totalPages: number\n spider: SpiderResult\n durationMs: number\n truncated: boolean\n browserRetries: number\n}\n\nexport interface ExtractOptions {\n startUrl: string\n maxPages?: number\n concurrency?: number\n kernelApiKey?: string\n}\n\nfunction parsePageData(url: string, html: string, status: number, via: 'fetch' | 'browser'): PageData {\n const origin = new URL(url).origin\n\n const title = html.match(/<title[^>]*>([^<]*)<\\/title>/i)?.[1]?.trim() ?? null\n const metaDescription = html.match(/<meta[^>]+name=[\"']description[\"'][^>]+content=[\"']([^\"']*)[\"']/i)?.[1]?.trim()\n ?? html.match(/<meta[^>]+content=[\"']([^\"']*)[\"'][^>]+name=[\"']description[\"']/i)?.[1]?.trim()\n ?? null\n const canonicalUrl = html.match(/<link[^>]+rel=[\"']canonical[\"'][^>]+href=[\"']([^\"']*)[\"']/i)?.[1]?.trim()\n ?? html.match(/<link[^>]+href=[\"']([^\"']*)[\"'][^>]+rel=[\"']canonical[\"']/i)?.[1]?.trim()\n ?? null\n\n const headings: Array<{ level: number; text: string }> = []\n for (const m of html.matchAll(/<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi)) {\n const text = m[2].replace(/<[^>]+>/g, '').trim()\n if (text) headings.push({ level: Number(m[1]), text: text.slice(0, 200) })\n }\n\n const h1 = headings.find(h => h.level === 1)?.text ?? null\n\n const bodyText = html.replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]+>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n const wordCount = bodyText.split(' ').filter(w => w.length > 2).length\n\n const schemaTypes: string[] = []\n for (const m of html.matchAll(/<script[^>]+type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi)) {\n try {\n const parsed: unknown = JSON.parse(m[1])\n const collect = (obj: unknown) => {\n if (!obj || typeof obj !== 'object') return\n const o = obj as Record<string, unknown>\n if (typeof o['@type'] === 'string') schemaTypes.push(o['@type'])\n else if (Array.isArray(o['@type'])) schemaTypes.push(...o['@type'].filter((t): t is string => typeof t === 'string'))\n if (Array.isArray(o['@graph'])) o['@graph'].forEach(collect)\n }\n collect(parsed)\n } catch { /**/ }\n }\n\n let internalLinks = 0\n let externalLinks = 0\n for (const m of html.matchAll(/href\\s*=\\s*[\"']([^\"'\\s>]+)/gi)) {\n try {\n const abs = new URL(m[1], url)\n if (abs.origin === origin) internalLinks++\n else externalLinks++\n } catch { /**/ }\n }\n\n return { url, status, via, title, metaDescription, h1, headings, wordCount, schemaTypes, canonicalUrl, internalLinks, externalLinks }\n}\n\nasync function fetchAndParse(url: string, kernelApiKey?: string): Promise<PageData> {\n try {\n const res = await fetch(url, {\n headers: { 'User-Agent': UA, 'Accept': 'text/html,application/xhtml+xml' },\n signal: AbortSignal.timeout(10_000),\n redirect: 'manual',\n })\n if (res.ok) {\n const ct = res.headers.get('content-type') ?? ''\n if (ct.includes('html')) {\n const html = await res.text()\n return parsePageData(url, html, res.status, 'fetch')\n }\n return { url, status: res.status, via: 'fetch', title: null, metaDescription: null, h1: null, headings: [], wordCount: 0, schemaTypes: [], canonicalUrl: null, internalLinks: 0, externalLinks: 0 }\n }\n\n if ((res.status === 403 || res.status === 429) && kernelApiKey) {\n const html = await fetchWithKernel(url)\n return parsePageData(url, html, 200, 'browser')\n }\n\n return { url, status: res.status, via: 'fetch', title: null, metaDescription: null, h1: null, headings: [], wordCount: 0, schemaTypes: [], canonicalUrl: null, internalLinks: 0, externalLinks: 0 }\n } catch {\n if (kernelApiKey) {\n try {\n const html = await fetchWithKernel(url)\n return parsePageData(url, html, 200, 'browser')\n } catch { /**/ }\n }\n return { url, status: null, via: 'fetch', title: null, metaDescription: null, h1: null, headings: [], wordCount: 0, schemaTypes: [], canonicalUrl: null, internalLinks: 0, externalLinks: 0 }\n }\n}\n\nasync function runWithConcurrency<T>(items: T[], concurrency: number, fn: (item: T) => Promise<unknown>): Promise<void> {\n let i = 0\n const workers = Array.from({ length: concurrency }, async () => {\n while (i < items.length) {\n const item = items[i++]\n await fn(item)\n }\n })\n await Promise.all(workers)\n}\n\nexport async function extractSite(opts: ExtractOptions): Promise<ExtractResult> {\n const startMs = Date.now()\n const maxPages = Math.min(opts.maxPages ?? 100, 200)\n const concurrency = Math.min(opts.concurrency ?? EXTRACT_CONCURRENCY, 10)\n\n const spider = await spiderSite({\n startUrl: opts.startUrl,\n maxUrls: maxPages,\n concurrency: 12,\n kernelApiKey: opts.kernelApiKey,\n })\n\n const urlsToExtract = spider.urls\n .filter(u => u.status === 200 || u.status === null)\n .slice(0, maxPages)\n .map(u => u.url)\n\n const pages: PageData[] = []\n let browserRetries = spider.browserRetries\n\n await runWithConcurrency(urlsToExtract, concurrency, async (url) => {\n const data = await fetchAndParse(url, opts.kernelApiKey)\n if (data.via === 'browser') browserRetries++\n pages.push(data)\n })\n\n return {\n startUrl: opts.startUrl,\n pages,\n totalPages: pages.length,\n spider,\n durationMs: Date.now() - startMs,\n truncated: spider.truncated || pages.length >= maxPages,\n browserRetries,\n }\n}\n","import { Inngest } from 'inngest';\n\nexport const inngest = new Inngest({ id: 'mcp-scraper' });\n","import Anthropic from '@anthropic-ai/sdk';\nimport { z } from 'zod';\n\nexport function extractJson(raw: string): string {\n let text = raw.replace(/<think>[\\s\\S]*?<\\/think>/gi, '').trim()\n text = text.replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```\\s*$/, '').trim()\n if (text.startsWith('{') || text.startsWith('[')) return text\n\n const objStart = text.indexOf('{')\n const arrStart = text.indexOf('[')\n let start = -1\n let open: string, close: string\n\n if (objStart === -1 && arrStart === -1) return text\n if (objStart === -1) { start = arrStart; open = '['; close = ']' }\n else if (arrStart === -1) { start = objStart; open = '{'; close = '}' }\n else if (arrStart < objStart) { start = arrStart; open = '['; close = ']' }\n else { start = objStart; open = '{'; close = '}' }\n\n let depth = 0; let inStr = false; let escape = false\n for (let i = start; i < text.length; i++) {\n const ch = text[i]\n if (escape) { escape = false; continue }\n if (ch === '\\\\' && inStr){ escape = true; continue }\n if (ch === '\"') { inStr = !inStr; continue }\n if (inStr) continue\n if (ch === open) depth++\n else if (ch === close) { if (--depth === 0) return text.slice(start, i + 1) }\n }\n return text.slice(start)\n}\n\nexport interface ILlmClient {\n completeJson(prompt: string): Promise<unknown>;\n complete(prompt: string): Promise<string>;\n}\n\nexport class AnthropicLlmClient implements ILlmClient {\n private client: Anthropic;\n\n constructor() {\n this.client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });\n }\n\n async complete(prompt: string): Promise<string> {\n const response = await this.client.messages.create({\n model: 'claude-opus-4-7',\n max_tokens: 8192,\n messages: [{ role: 'user', content: prompt }],\n });\n const content = response.content[0];\n if (content.type !== 'text') {\n throw new Error('Unexpected non-text response from LLM');\n }\n return content.text;\n }\n\n async completeJson(prompt: string): Promise<unknown> {\n const raw = await this.complete(prompt);\n try {\n return JSON.parse(extractJson(raw));\n } catch {\n const coerced = await this.complete(\n `Convert the following text to a valid JSON object. Output ONLY the JSON, no markdown, no explanation:\\n\\n${raw}`\n );\n return JSON.parse(extractJson(coerced));\n }\n }\n}\n\nexport class DeepInfraLlmClient implements ILlmClient {\n private apiKey: string;\n private model: string;\n\n constructor(apiKey: string, model = 'Qwen/Qwen3.6-35B-A3B') {\n this.apiKey = apiKey;\n this.model = model;\n }\n\n async complete(prompt: string): Promise<string> {\n const resp = await fetch('https://api.deepinfra.com/v1/openai/chat/completions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}` },\n body: JSON.stringify({ model: this.model, messages: [{ role: 'user', content: prompt }], max_tokens: 8192 }),\n });\n if (!resp.ok) throw new Error(`DeepInfra error ${resp.status}: ${await resp.text()}`);\n const data = await resp.json() as { choices: Array<{ message: { content: string } }> };\n return data.choices[0].message.content;\n }\n\n async completeJson(prompt: string): Promise<unknown> {\n const raw = await this.complete(prompt);\n try {\n return JSON.parse(extractJson(raw));\n } catch {\n const coerced = await this.complete(\n `Convert the following text to a valid JSON object. Output ONLY the JSON, no markdown, no explanation:\\n\\n${raw}`\n );\n return JSON.parse(extractJson(coerced));\n }\n }\n}\n\nexport class OpenRouterLlmClient implements ILlmClient {\n private apiKey: string;\n private model: string;\n\n constructor(apiKey: string, model = 'qwen/qwen3-235b-a22b') {\n this.apiKey = apiKey;\n this.model = model;\n }\n\n async complete(prompt: string): Promise<string> {\n const resp = await fetch('https://openrouter.ai/api/v1/chat/completions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}` },\n body: JSON.stringify({ model: this.model, messages: [{ role: 'user', content: prompt }], max_tokens: 8192 }),\n });\n if (!resp.ok) throw new Error(`OpenRouter error ${resp.status}: ${await resp.text()}`);\n const data = await resp.json() as { choices: Array<{ message: { content: string } }> };\n return data.choices[0].message.content;\n }\n\n async completeJson(prompt: string): Promise<unknown> {\n const raw = await this.complete(prompt);\n try {\n return JSON.parse(extractJson(raw));\n } catch {\n const coerced = await this.complete(\n `Convert the following text to a valid JSON object. Output ONLY the JSON, no markdown, no explanation:\\n\\n${raw}`\n );\n return JSON.parse(extractJson(coerced));\n }\n }\n}\n\nexport class FallbackLlmClient implements ILlmClient {\n public lastServedBy: 'primary' | 'secondary' | null = null;\n public lastPrimaryError: string | null = null;\n\n constructor(private primary: ILlmClient, private secondary: ILlmClient) {}\n\n async complete(prompt: string): Promise<string> {\n try {\n const result = await this.primary.complete(prompt);\n this.lastServedBy = 'primary';\n this.lastPrimaryError = null;\n return result;\n } catch (err) {\n this.lastPrimaryError = String(err);\n console.warn('[FallbackLlmClient] primary failed, using secondary:', err);\n this.lastServedBy = 'secondary';\n return this.secondary.complete(prompt);\n }\n }\n\n async completeJson(prompt: string): Promise<unknown> {\n try {\n const result = await this.primary.completeJson(prompt);\n this.lastServedBy = 'primary';\n this.lastPrimaryError = null;\n return result;\n } catch (err) {\n this.lastPrimaryError = String(err);\n console.warn('[FallbackLlmClient] primary failed, using secondary:', err);\n this.lastServedBy = 'secondary';\n return this.secondary.completeJson(prompt);\n }\n }\n}\n\nexport async function llmParseWithRetry<T>(\n schema: z.ZodSchema<T>,\n promptFn: (attempt: number, error?: string) => string,\n llm: ILlmClient,\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const prompt = attempt === 0\n ? promptFn(attempt)\n : promptFn(attempt, lastError?.message);\n\n let raw: unknown;\n try {\n raw = await llm.completeJson(prompt);\n } catch (e) {\n lastError = e instanceof Error ? e : new Error(String(e));\n continue;\n }\n\n const result = schema.safeParse(raw);\n if (result.success) return result.data;\n lastError = result.error;\n }\n\n const detail = lastError?.message?.slice(0, 200) ?? 'unknown error'\n throw new Error(`LLM failed to produce valid output after 3 attempts: ${detail}`)\n}\n\nexport async function llmParseWithMarkdownRescue<T>(\n schema: z.ZodSchema<T>,\n promptFn: (attempt: number, error?: string) => string,\n llm: ILlmClient,\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const prompt = attempt === 0\n ? promptFn(attempt)\n : promptFn(attempt, lastError?.message);\n\n let rawText: string;\n try {\n rawText = await llm.complete(prompt);\n } catch (e) {\n lastError = e instanceof Error ? e : new Error(String(e));\n continue;\n }\n\n const stripped = rawText.replace(/<think>[\\s\\S]*?<\\/think>/gi, '').trim()\n\n let candidate: Record<string, unknown>;\n try {\n candidate = JSON.parse(extractJson(stripped)) as Record<string, unknown>\n } catch {\n candidate = { markdown: stripped }\n }\n\n if (candidate.markdown === undefined || candidate.markdown === null) {\n const jsonStart = stripped.indexOf('{')\n const jsonEnd = stripped.lastIndexOf('}')\n if (jsonStart > 0) {\n const before = stripped.slice(0, jsonStart).trim()\n if (before.length >= 50) candidate.markdown = before\n }\n if ((candidate.markdown === undefined || (candidate.markdown as string).length < 50) && jsonEnd !== -1 && jsonEnd < stripped.length - 1) {\n const after = stripped.slice(jsonEnd + 1).trim()\n if (after.length >= 50) candidate.markdown = after\n }\n if (candidate.markdown === undefined) candidate.markdown = stripped\n }\n\n const result = schema.safeParse(candidate)\n if (result.success) return result.data\n lastError = result.error\n }\n\n const detail = lastError?.message?.slice(0, 200) ?? 'unknown error'\n throw new Error(`LLM failed to produce valid output after 3 attempts: ${detail}`)\n}\n","export interface IHttpClient {\n get(url: string, headers?: Record<string, string>): Promise<{ status: number; body: string }>;\n post(url: string, body: unknown, headers?: Record<string, string>): Promise<{ status: number; body: string }>;\n}\n\nexport class FetchHttpClient implements IHttpClient {\n async get(url: string, headers?: Record<string, string>): Promise<{ status: number; body: string }> {\n const response = await fetch(url, { headers });\n const body = await response.text();\n return { status: response.status, body };\n }\n\n async post(url: string, body: unknown, headers?: Record<string, string>): Promise<{ status: number; body: string }> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(body),\n });\n const responseBody = await response.text();\n return { status: response.status, body: responseBody };\n }\n}\n","import { z } from 'zod';\nimport pLimit from 'p-limit';\nimport { llmParseWithRetry, type ILlmClient } from '../../lib/llm-parse-with-retry.js';\nimport { type IHttpClient } from '../../lib/http-client.js';\nimport { type ISiteAuditRepository } from './site-audit-repository.js';\nimport { type ISiteAuditPythonRunner } from './python/graph-runner.js';\nimport {\n ValidatedFileManifestSchema,\n EnrichCompetitorOutputSchema,\n PageTypeClassificationRowSchema,\n SiteAuditResultSchema,\n type SiteAuditStartRequest,\n type SiteAuditPhase,\n type SiteAuditResult,\n type SiteAuditIngestPayload,\n type SiteAuditBuildGraphPayload,\n type SiteAuditClassifyPayload,\n type SiteAuditComparePayload,\n type SiteAuditSynthesizePayload,\n} from './schemas.js';\nimport type { SiteAuditJobRow } from '../../api/db.js';\nimport { buildIngestValidatePrompt } from './prompts/ingest-validate.js';\nimport { buildEnrichCompetitorPrompt } from './prompts/enrich-competitor.js';\nimport { buildGraphPerSitePrompt } from './prompts/build-graph-per-site.js';\nimport { buildMetricsClassifyPrompt } from './prompts/metrics-classify.js';\nimport { buildCompareRecommendPrompt } from './prompts/compare-recommend.js';\nimport { buildScoreSynthesizePrompt } from './prompts/score-synthesize.js';\nimport { ARCHITECTURE_SCORE_WEIGHTS, ILES_WEIGHTS } from '../../lib/site-architecture-auditor/rules.js';\n\nconst OrphanAnnotationOutputSchema = z.object({\n site_id: z.string(),\n domain: z.string(),\n confirmed_orphans: z.array(\n z.object({\n url: z.string(),\n reason: z.string(),\n })\n ),\n rejected_candidates: z.array(\n z.object({\n url: z.string(),\n reason: z.string(),\n })\n ),\n data_quality_notes: z.array(z.string()),\n orphan_count: z.number(),\n _annotation_label: z.string(),\n});\n\nconst CompareRecommendOutputSchema = z.object({\n topicClusterGapAnalysis: z.array(z.unknown()).optional(),\n uxNavPatternComparison: z.array(z.unknown()).optional(),\n linkRecommendations: z.array(\n z.object({\n target_url: z.string(),\n source_url: z.string(),\n anchor_text: z.string(),\n placement: z.string(),\n reason: z.string(),\n topic_similarity_score: z.unknown().optional(),\n confidence: z.unknown().optional(),\n })\n ).optional(),\n newPageCreationList: z.array(z.unknown()).optional(),\n});\n\nexport type CompareRecommendOutput = z.infer<typeof CompareRecommendOutputSchema>;\n\nexport interface ISiteAuditService {\n startAudit(userId: number, request: SiteAuditStartRequest): Promise<string>;\n getJob(jobId: string): Promise<SiteAuditJobRow | null>;\n getJobsForUser(userId: number): Promise<SiteAuditJobRow[]>;\n runIngestPhase(jobId: string, payload: SiteAuditIngestPayload): Promise<void>;\n runEnrichPhase(jobId: string, payload: SiteAuditIngestPayload): Promise<void>;\n runBuildGraphPhase(jobId: string, payload: SiteAuditBuildGraphPayload): Promise<void>;\n runClassifyPhase(jobId: string, payload: SiteAuditClassifyPayload): Promise<void>;\n runComparePhase(jobId: string, payload: SiteAuditComparePayload): Promise<CompareRecommendOutput>;\n runSynthesizePhase(jobId: string, payload: SiteAuditSynthesizePayload): Promise<void>;\n dispatchPhase(phase: SiteAuditPhase, jobId: string, payload: unknown): Promise<void>;\n}\n\nexport interface SiteAuditServiceDeps {\n repo: ISiteAuditRepository;\n llm: ILlmClient;\n http: IHttpClient;\n python: ISiteAuditPythonRunner;\n}\n\nexport class SiteAuditService implements ISiteAuditService {\n constructor(private deps: SiteAuditServiceDeps) {}\n\n async startAudit(userId: number, request: SiteAuditStartRequest): Promise<string> {\n const jobId = `saa-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n await this.deps.repo.createSiteAuditJob(jobId, userId, request);\n return jobId;\n }\n\n async getJob(jobId: string): Promise<SiteAuditJobRow | null> {\n return this.deps.repo.getSiteAuditJob(jobId);\n }\n\n async getJobsForUser(userId: number): Promise<SiteAuditJobRow[]> {\n return this.deps.repo.listSiteAuditJobs(userId);\n }\n\n async runIngestPhase(jobId: string, payload: SiteAuditIngestPayload): Promise<void> {\n const { request } = payload;\n const input = {\n clientDomain: request.clientDomain,\n sfInternalPath: request.sfInternalPath,\n sfInlinksPath: request.sfInlinksPath,\n sfOutlinksPath: request.sfOutlinksPath,\n sitemapPath: request.sitemapPath,\n competitorDomainsCsvPath: request.competitorDomainsCsvPath,\n visualSitemapsPath: request.visualSitemapsPath,\n gscPath: request.gscPath,\n ga4Path: request.ga4Path,\n ahrefsPath: request.ahrefsPath,\n businessContext: request.businessContext,\n priorAuditMemoryEnabled: request.priorAuditMemoryEnabled,\n };\n const result = await llmParseWithRetry(\n ValidatedFileManifestSchema,\n (attempt, error) => {\n void attempt;\n void error;\n return buildIngestValidatePrompt(input);\n },\n this.deps.llm,\n );\n await this.deps.repo.logSiteAuditPhaseComplete(jobId, 'phase1-ingest', result);\n }\n\n async runEnrichPhase(jobId: string, payload: SiteAuditIngestPayload): Promise<void> {\n const { request } = payload;\n const limit = pLimit(3);\n const job = await this.deps.repo.getSiteAuditJob(jobId);\n if (!job) throw new Error(`[site-audit-service] runEnrichPhase: job ${jobId} not found`);\n\n const sessionPath = request.sessionPath;\n const competitorDomain = request.clientDomain;\n void competitorDomain;\n\n const competitorDomainsCsvPath = request.competitorDomainsCsvPath;\n void competitorDomainsCsvPath;\n\n const competitors: string[] = await this._readCompetitorDomains(request);\n\n const results = await Promise.all(\n competitors.map((domain, idx) =>\n limit(async () => {\n const competitorIndex = idx + 1;\n const rawPrompt = buildEnrichCompetitorPrompt({ domain, sessionPath });\n const prompt = rawPrompt.replace('<competitor-index>', String(competitorIndex));\n const result = await llmParseWithRetry(\n EnrichCompetitorOutputSchema,\n (attempt, error) => {\n void attempt;\n void error;\n return prompt;\n },\n this.deps.llm,\n );\n return result;\n })\n )\n );\n\n await this.deps.repo.logSiteAuditPhaseComplete(jobId, 'phase1-ingest', { competitors: results });\n }\n\n async runBuildGraphPhase(jobId: string, payload: SiteAuditBuildGraphPayload): Promise<void> {\n void payload;\n const job = await this.deps.repo.getSiteAuditJob(jobId);\n if (!job) throw new Error(`[site-audit-service] runBuildGraphPhase: job ${jobId} not found`);\n\n const request = JSON.parse(job.request) as SiteAuditStartRequest;\n const sessionPath = request.sessionPath;\n const clientDomain = request.clientDomain;\n\n const urlInventory: Array<{ url: string; sitemap_present: boolean; crawl_depth?: number }> = [];\n const directedEdgeCount = 0;\n\n const orphanAnnotation = await llmParseWithRetry(\n OrphanAnnotationOutputSchema,\n (attempt, error) => {\n void attempt;\n void error;\n return buildGraphPerSitePrompt({\n siteId: 'client',\n domain: clientDomain,\n urlInventory,\n directedEdgeCount,\n });\n },\n this.deps.llm,\n );\n\n const graphMetrics = await this.deps.python.runGraphMetrics({\n directedLinkGraphPath: `${sessionPath}/phase1/directed_link_graph.json`,\n masterUrlInventoryPath: `${sessionPath}/phase1/master_url_inventory.json`,\n outputPath: `${sessionPath}/phase2/graph_metrics.json`,\n });\n\n await this.deps.repo.logSiteAuditPhaseComplete(jobId, 'phase2-build-graph', {\n orphanAnnotation,\n graphMetrics,\n });\n }\n\n async runClassifyPhase(jobId: string, payload: SiteAuditClassifyPayload): Promise<void> {\n void payload;\n const job = await this.deps.repo.getSiteAuditJob(jobId);\n if (!job) throw new Error(`[site-audit-service] runClassifyPhase: job ${jobId} not found`);\n\n const request = JSON.parse(job.request) as SiteAuditStartRequest;\n const sessionPath = request.sessionPath;\n const clientDomain = request.clientDomain;\n\n const allUrls: Array<{\n url: string;\n title?: string;\n h1?: string;\n meta_description?: string;\n }> = [];\n\n const BATCH_SIZE = 50;\n const MAX_PAYLOAD_CHARS = 8000;\n const allClassifications: z.infer<typeof PageTypeClassificationRowSchema>[] = [];\n\n const trimmedUrls = allUrls.map(u => ({\n ...u,\n h1: u.h1 ? u.h1.slice(0, 100) : u.h1,\n meta_description: u.meta_description ? u.meta_description.slice(0, 160) : u.meta_description,\n }));\n\n const batches: typeof trimmedUrls[] = [];\n let currentBatch: typeof trimmedUrls = [];\n let currentPayloadChars = 0;\n\n for (const urlItem of trimmedUrls) {\n const itemChars =\n urlItem.url.length +\n (urlItem.title?.length ?? 0) +\n (urlItem.h1?.length ?? 0) +\n (urlItem.meta_description?.length ?? 0);\n\n const wouldExceedBatchSize = currentBatch.length >= BATCH_SIZE;\n const wouldExceedPayloadLimit = currentPayloadChars + itemChars > MAX_PAYLOAD_CHARS;\n\n if ((wouldExceedBatchSize || wouldExceedPayloadLimit) && currentBatch.length > 0) {\n batches.push(currentBatch);\n currentBatch = [];\n currentPayloadChars = 0;\n }\n\n currentBatch.push(urlItem);\n currentPayloadChars += itemChars;\n }\n if (currentBatch.length > 0) batches.push(currentBatch);\n\n const usZipsCsvPath = process.env['US_ZIPS_CSV_PATH'] ?? '/Users/vilovieta/Downloads/sales-magician-api-leads-magician-01c6cff78e31/tools/analytics/data/uszips.csv';\n\n for (const batch of batches) {\n let classified: Array<{ url: string; pageType: string; confidence: number; locationSignals: string[] }> = [];\n let unclassifiedUrls: string[] = batch.map(u => u.url);\n\n try {\n const pythonResult = await this.deps.python.runLocationClassifier({\n urls: batch.map(u => u.url),\n usZipsCsvPath,\n });\n classified = pythonResult.classified;\n unclassifiedUrls = pythonResult.unclassified;\n } catch (err) {\n console.warn(\n '[site-audit-service] runClassifyPhase: python.runLocationClassifier failed (falling back to LLM):',\n err instanceof Error ? err.message : String(err)\n );\n unclassifiedUrls = batch.map(u => u.url);\n }\n\n for (const c of classified) {\n allClassifications.push({\n url: c.url,\n page_type: c.pageType as z.infer<typeof PageTypeClassificationRowSchema>['page_type'],\n confidence: c.confidence > 0.6 ? 'high' : c.confidence > 0.4 ? 'medium' : 'low',\n _classification_label: '[COMPUTED:python/location-classifier]',\n topic_cluster: null,\n location_signals: c.locationSignals,\n });\n }\n\n if (unclassifiedUrls.length > 0) {\n const unclassifiedBatch = batch.filter(u => unclassifiedUrls.includes(u.url));\n const llmResults = await llmParseWithRetry(\n z.array(PageTypeClassificationRowSchema),\n (attempt, error) => {\n void attempt;\n void error;\n return buildMetricsClassifyPrompt({\n urls: unclassifiedBatch,\n siteDomain: clientDomain,\n });\n },\n this.deps.llm,\n );\n allClassifications.push(...llmResults);\n }\n }\n\n void sessionPath;\n\n await this.deps.repo.logSiteAuditPhaseComplete(jobId, 'phase3-classify', {\n classifications: allClassifications,\n totalClassified: allClassifications.length,\n });\n }\n\n async runComparePhase(jobId: string, payload: SiteAuditComparePayload): Promise<CompareRecommendOutput> {\n void payload;\n const job = await this.deps.repo.getSiteAuditJob(jobId);\n if (!job) throw new Error(`[site-audit-service] runComparePhase: job ${jobId} not found`);\n\n const request = JSON.parse(job.request) as SiteAuditStartRequest;\n const clientDomain = request.clientDomain;\n\n const result = await llmParseWithRetry(\n CompareRecommendOutputSchema,\n (attempt, error) => {\n void attempt;\n void error;\n return buildCompareRecommendPrompt({\n clientDomain,\n clientMoneyPages: [],\n competitorSites: [],\n clientUrlMetrics: [],\n clientPageTypes: [],\n });\n },\n this.deps.llm,\n );\n\n await this.deps.repo.logSiteAuditPhaseComplete(jobId, 'phase4-compare', result);\n return result;\n }\n\n async runSynthesizePhase(jobId: string, payload: SiteAuditSynthesizePayload): Promise<void> {\n void payload;\n const job = await this.deps.repo.getSiteAuditJob(jobId);\n if (!job) throw new Error(`[site-audit-service] runSynthesizePhase: job ${jobId} not found`);\n\n const request = JSON.parse(job.request) as SiteAuditStartRequest;\n const clientDomain = request.clientDomain;\n const sessionPath = request.sessionPath;\n\n const architectureScoreComponents: Record<string, number> = {};\n for (const [dim, weight] of Object.entries(ARCHITECTURE_SCORE_WEIGHTS)) {\n architectureScoreComponents[dim] = weight * 50;\n }\n const architectureTotal = Object.values(architectureScoreComponents).reduce((s, v) => s + v, 0);\n\n const ilesComponents: Record<string, number> = {};\n for (const [dim, weight] of Object.entries(ILES_WEIGHTS)) {\n ilesComponents[dim] = weight * 50;\n }\n const ilesTotal = Object.values(ilesComponents).reduce((s, v) => s + v, 0);\n\n const preComputedArchitectureScore = {\n total: architectureTotal,\n components: architectureScoreComponents,\n };\n const preComputedIlesScore = {\n total: ilesTotal,\n components: ilesComponents,\n };\n\n const reportMarkdown = await llmParseWithRetry(\n z.string(),\n (attempt, error) => {\n void attempt;\n void error;\n return buildScoreSynthesizePrompt({\n preComputedArchitectureScore,\n preComputedIlesScore,\n rankedBacklog: [],\n clientDomain,\n sessionPath,\n });\n },\n this.deps.llm,\n );\n\n const auditResult: SiteAuditResult = SiteAuditResultSchema.parse({\n jobId,\n clientDomain,\n architectureScore: preComputedArchitectureScore,\n ilesScore: preComputedIlesScore,\n rankedBacklog: [],\n linkRecommendations: [],\n newPagesRecommended: [],\n guardrailFlags: [],\n reportMarkdown,\n });\n\n await this.deps.repo.completeSiteAuditJob(jobId, auditResult);\n await this.deps.repo.logSiteAuditPhaseComplete(jobId, 'phase5-synthesize', {\n architectureScore: auditResult.architectureScore,\n ilesScore: auditResult.ilesScore,\n });\n }\n\n dispatchPhase(_phase: SiteAuditPhase, _jobId: string, _payload: unknown): Promise<void> {\n throw new Error('dispatchPhase delegates to phases.ts — see saa-011');\n }\n\n private async _readCompetitorDomains(request: SiteAuditStartRequest): Promise<string[]> {\n try {\n const response = await this.deps.http.get(`file://${request.competitorDomainsCsvPath}`);\n const lines = response.body\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0 && !l.startsWith('#'));\n const domains: string[] = [];\n for (const line of lines) {\n if (line.includes(',')) {\n domains.push(...line.split(',').map(d => d.trim()).filter(d => d.length > 0));\n } else {\n domains.push(line);\n }\n }\n return domains.filter(d => d.includes('.'));\n } catch {\n return [];\n }\n }\n}\n","import { z } from 'zod';\nimport {\n PageTypeSchema,\n ClassificationConfidenceSchema,\n GuardrailFlagNameSchema,\n PriorityTierSchema,\n LocationArchitecturePatternSchema,\n} from '../../lib/site-architecture-auditor/rules.js';\n\nexport {\n PageTypeSchema,\n ClassificationConfidenceSchema,\n GuardrailFlagNameSchema,\n PriorityTierSchema,\n LocationArchitecturePatternSchema,\n};\n\nexport const SiteAuditJobStatusSchema = z.enum([\n 'pending',\n 'running',\n 'complete',\n 'failed',\n]);\nexport type SiteAuditJobStatus = z.infer<typeof SiteAuditJobStatusSchema>;\n\nexport const SiteAuditPhaseSchema = z.enum([\n 'phase1-ingest',\n 'phase2-build-graph',\n 'phase3-classify',\n 'phase4-compare',\n 'phase5-synthesize',\n]);\nexport type SiteAuditPhase = z.infer<typeof SiteAuditPhaseSchema>;\n\nexport const SiteAuditJobStateSchema = z.object({\n jobId: z.string(),\n userId: z.number(),\n status: SiteAuditJobStatusSchema,\n currentPhase: SiteAuditPhaseSchema.nullable(),\n clientDomain: z.string(),\n sessionPath: z.string(),\n});\nexport type SiteAuditJobState = z.infer<typeof SiteAuditJobStateSchema>;\n\nexport const SiteAuditPhaseLogEntrySchema = z.object({\n jobId: z.string(),\n phase: SiteAuditPhaseSchema,\n completedAt: z.string(),\n outputSummary: z.unknown(),\n});\nexport type SiteAuditPhaseLogEntry = z.infer<typeof SiteAuditPhaseLogEntrySchema>;\n\nexport const SiteAuditStartRequestSchema = z.object({\n clientDomain: z.string(),\n sessionPath: z.string(),\n sfInternalPath: z.string(),\n sfInlinksPath: z.string(),\n sfOutlinksPath: z.string(),\n sitemapPath: z.string(),\n competitorDomainsCsvPath: z.string(),\n visualSitemapsPath: z.string().optional(),\n gscPath: z.string().optional(),\n ga4Path: z.string().optional(),\n ahrefsPath: z.string().optional(),\n businessContext: z.string().optional(),\n priorAuditMemoryEnabled: z.boolean().default(false),\n});\nexport type SiteAuditStartRequest = z.infer<typeof SiteAuditStartRequestSchema>;\n\nexport const SiteAuditIngestRequestSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditIngestRequest = z.infer<typeof SiteAuditIngestRequestSchema>;\n\nexport const SiteAuditBuildGraphRequestSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditBuildGraphRequest = z.infer<typeof SiteAuditBuildGraphRequestSchema>;\n\nexport const SiteAuditClassifyRequestSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditClassifyRequest = z.infer<typeof SiteAuditClassifyRequestSchema>;\n\nexport const SiteAuditCompareRequestSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditCompareRequest = z.infer<typeof SiteAuditCompareRequestSchema>;\n\nexport const SiteAuditScoreRequestSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditScoreRequest = z.infer<typeof SiteAuditScoreRequestSchema>;\n\nexport const SiteAuditReportRequestSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditReportRequest = z.infer<typeof SiteAuditReportRequestSchema>;\n\nexport const SiteAuditIngestPayloadSchema = z.object({\n jobId: z.string(),\n request: SiteAuditStartRequestSchema,\n});\nexport type SiteAuditIngestPayload = z.infer<typeof SiteAuditIngestPayloadSchema>;\n\nexport const SiteAuditBuildGraphPayloadSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditBuildGraphPayload = z.infer<typeof SiteAuditBuildGraphPayloadSchema>;\n\nexport const SiteAuditClassifyPayloadSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditClassifyPayload = z.infer<typeof SiteAuditClassifyPayloadSchema>;\n\nexport const SiteAuditComparePayloadSchema = z.object({\n jobId: z.string(),\n});\nexport type SiteAuditComparePayload = z.infer<typeof SiteAuditComparePayloadSchema>;\n\nexport const SiteAuditSynthesizePayloadSchema = z.object({\n jobId: z.string(),\n scoringOnly: z.boolean().default(false),\n reportOnly: z.boolean().default(false),\n});\nexport type SiteAuditSynthesizePayload = z.infer<typeof SiteAuditSynthesizePayloadSchema>;\n\nexport const ValidatedFileManifestSchema = z.object({\n clientDomain: z.string(),\n sites: z.array(\n z.object({\n site_id: z.string(),\n role: z.enum(['client', 'competitor']),\n domain: z.string(),\n sf_internal_path: z.string().nullable(),\n sf_inlinks_path: z.string().nullable(),\n sf_outlinks_path: z.string().nullable(),\n sitemap_path: z.string().nullable(),\n has_screaming_frog: z.boolean(),\n has_sitemap: z.boolean(),\n schema_version: z.string(),\n url_count_sitemap: z.number(),\n requires_firecrawl_enrichment: z.boolean(),\n })\n ),\n blocking_errors: z.array(z.string()),\n warnings: z.array(z.string()),\n python_deps_verified: z.boolean(),\n});\nexport type ValidatedFileManifest = z.infer<typeof ValidatedFileManifestSchema>;\n\nexport const EnrichCompetitorOutputSchema = z.object({\n domain: z.string(),\n pages: z.array(\n z.object({\n url: z.string(),\n title: z.string().optional(),\n depth: z.number().optional(),\n page_type: z.string().optional(),\n })\n ),\n});\nexport type EnrichCompetitorOutput = z.infer<typeof EnrichCompetitorOutputSchema>;\n\nexport const MasterUrlInventoryItemSchema = z.object({\n site_id: z.string(),\n url: z.string(),\n normalized_url: z.string(),\n status_code: z.number(),\n indexability: z.string(),\n canonical_url: z.string().nullable(),\n title: z.string().optional(),\n h1: z.string().optional(),\n meta_description: z.string().optional(),\n crawl_depth: z.number().optional(),\n sitemap_present: z.boolean(),\n visual_sitemap_present: z.boolean(),\n page_type: PageTypeSchema.nullable(),\n topic_cluster: z.string().nullable(),\n orphan_status: z.boolean(),\n internal_pagerank: z.number().optional(),\n screaming_frog_link_score: z.number().optional(),\n inlink_count: z.number().default(0),\n content_inlink_count: z.number().default(0),\n});\nexport type MasterUrlInventoryItem = z.infer<typeof MasterUrlInventoryItemSchema>;\n\nexport const DirectedLinkGraphEdgeSchema = z.object({\n site_id: z.string(),\n source_url: z.string(),\n target_url: z.string(),\n anchor_text: z.string(),\n follow: z.boolean(),\n link_type: z.string(),\n link_position: z.string(),\n weight: z.number().default(1.0),\n is_sitewide: z.boolean().default(false),\n is_duplicate_edge: z.boolean().default(false),\n});\nexport type DirectedLinkGraphEdge = z.infer<typeof DirectedLinkGraphEdgeSchema>;\n\nexport const UrlMetricRowSchema = z.object({\n url: z.string(),\n site_id: z.string(),\n internal_pagerank: z.number(),\n crawl_depth: z.number(),\n cluster_cohesion: z.number().optional(),\n inlink_count: z.number(),\n content_inlink_count: z.number(),\n});\nexport type UrlMetricRow = z.infer<typeof UrlMetricRowSchema>;\n\nexport const PageTypeClassificationRowSchema = z.object({\n url: z.string(),\n page_type: PageTypeSchema,\n confidence: ClassificationConfidenceSchema,\n _classification_label: z.enum([\n '[COMPUTED:python/location-classifier]',\n '[INTERPRETED:claude]',\n ]),\n topic_cluster: z.string().nullable(),\n location_signals: z.array(z.string()).optional(),\n});\nexport type PageTypeClassificationRow = z.infer<typeof PageTypeClassificationRowSchema>;\n\nexport const TopicClusterAssignmentSchema = z.object({\n url: z.string(),\n topic_cluster: z.string(),\n money_page_candidate: z.boolean(),\n});\nexport type TopicClusterAssignment = z.infer<typeof TopicClusterAssignmentSchema>;\n\nexport const MoneyPageItemSchema = z.object({\n url: z.string(),\n site_id: z.string(),\n page_type: PageTypeSchema,\n topic_cluster: z.string(),\n business_value: z.number().min(0).max(5),\n current_inlink_count: z.number(),\n current_internal_pagerank: z.number(),\n});\nexport type MoneyPageItem = z.infer<typeof MoneyPageItemSchema>;\n\nexport const LinkRecommendationItemSchema = z.object({\n target_url: z.string(),\n target_page_type: PageTypeSchema,\n source_url: z.string(),\n anchor_text: z.string(),\n placement: z.string(),\n reason: z.string(),\n estimated_pagerank_lift: z.number().optional(),\n});\nexport type LinkRecommendationItem = z.infer<typeof LinkRecommendationItemSchema>;\n\nexport const NewPageCreationItemSchema = z.object({\n url_slug: z.string(),\n page_type: PageTypeSchema,\n priority: PriorityTierSchema,\n topic_cluster: z.string(),\n links_to: z.array(z.string()),\n});\nexport type NewPageCreationItem = z.infer<typeof NewPageCreationItemSchema>;\n\nexport const GuardrailFlagItemSchema = z.object({\n item: z.unknown(),\n flags: z.array(GuardrailFlagNameSchema),\n});\nexport type GuardrailFlagItem = z.infer<typeof GuardrailFlagItemSchema>;\n\nexport const ArchitectureScoreSchema = z.object({\n total: z.number().min(0).max(100),\n components: z.record(z.string(), z.number()),\n});\nexport type ArchitectureScore = z.infer<typeof ArchitectureScoreSchema>;\n\nexport const IlesScoreSchema = z.object({\n total: z.number().min(0).max(100),\n components: z.record(z.string(), z.number()),\n});\nexport type IlesScore = z.infer<typeof IlesScoreSchema>;\n\nexport const RankedBacklogItemSchema = z.object({\n priority: PriorityTierSchema,\n task: z.string(),\n owner: z.string().optional(),\n confidence: z.string(),\n priority_score: z.number(),\n});\nexport type RankedBacklogItem = z.infer<typeof RankedBacklogItemSchema>;\n\nexport const SiteAuditResultSchema = z.object({\n jobId: z.string(),\n clientDomain: z.string(),\n architectureScore: ArchitectureScoreSchema,\n ilesScore: IlesScoreSchema,\n rankedBacklog: z.array(RankedBacklogItemSchema),\n linkRecommendations: z.array(LinkRecommendationItemSchema),\n newPagesRecommended: z.array(NewPageCreationItemSchema),\n guardrailFlags: z.array(GuardrailFlagItemSchema),\n reportMarkdown: z.string().optional(),\n});\nexport type SiteAuditResult = z.infer<typeof SiteAuditResultSchema>;\n\nexport const PythonGraphInputSchema = z.object({\n directedLinkGraphPath: z.string(),\n masterUrlInventoryPath: z.string(),\n outputPath: z.string(),\n});\nexport type PythonGraphInput = z.infer<typeof PythonGraphInputSchema>;\n\nexport const PythonGraphOutputSchema = z.object({\n pagerank: z.record(z.string(), z.number()),\n crawlDepth: z.record(z.string(), z.number()),\n clusterCohesion: z.record(z.string(), z.number()),\n success: z.boolean(),\n error: z.string().optional(),\n});\nexport type PythonGraphOutput = z.infer<typeof PythonGraphOutputSchema>;\n\nexport const LocationClassifierInputSchema = z.object({\n urls: z.array(z.string()),\n usZipsCsvPath: z.string(),\n});\nexport type LocationClassifierInput = z.infer<typeof LocationClassifierInputSchema>;\n\nexport const LocationClassifierOutputSchema = z.object({\n classified: z.array(\n z.object({\n url: z.string(),\n pageType: z.string(),\n confidence: z.number(),\n locationSignals: z.array(z.string()),\n })\n ),\n unclassified: z.array(z.string()),\n success: z.boolean(),\n errorMessage: z.string().nullable(),\n});\nexport type LocationClassifierOutput = z.infer<typeof LocationClassifierOutputSchema>;\n\nexport const SiteAuditAuthAndDeductRequestSchema = z.object({\n operation: z.literal('site-audit'),\n userId: z.number(),\n email: z.string().optional(),\n});\nexport type SiteAuditAuthAndDeductRequest = z.infer<typeof SiteAuditAuthAndDeductRequestSchema>;\n","import { z } from \"zod\";\n\nexport const PageTypeSchema = z.enum([\n \"homepage\",\n \"core_service\",\n \"sub_service\",\n \"location\",\n \"service_location\",\n \"industry\",\n \"product\",\n \"pricing\",\n \"faq\",\n \"blog_post\",\n \"guide_resource\",\n \"case_study\",\n \"comparison\",\n \"competitor\",\n \"about\",\n \"contact\",\n \"team\",\n \"careers\",\n \"privacy_policy\",\n \"terms\",\n \"html_sitemap\",\n \"login_portal\",\n \"support_help_center\",\n]);\n\nexport type PageType = z.infer<typeof PageTypeSchema>;\n\nexport const MONEY_PAGE_ELIGIBLE_TYPES: readonly PageType[] = [\n \"core_service\",\n \"sub_service\",\n \"service_location\",\n \"product\",\n \"pricing\",\n \"comparison\",\n \"competitor\",\n \"industry\",\n] as const;\n\nexport const ClassificationConfidenceSchema = z.enum([\n \"high\",\n \"medium\",\n \"low\",\n \"uncertain\",\n]);\n\nexport type ClassificationConfidence = z.infer<\n typeof ClassificationConfidenceSchema\n>;\n\nexport const ARCHITECTURE_SCORE_WEIGHTS = {\n url_inventory_health: 0.20,\n crawlability: 0.20,\n important_page_depth: 0.20,\n page_type_coverage: 0.15,\n topical_cluster_completeness: 0.15,\n ux_nav_clarity: 0.10,\n} as const satisfies Record<string, number>;\n\nexport const ILES_WEIGHTS = {\n target_page_internal_pagerank: 0.30,\n content_inlinks: 0.20,\n anchor_relevance: 0.15,\n link_position_quality: 0.15,\n support_cluster_strength: 0.10,\n low_value_link_waste_reduction: 0.10,\n} as const satisfies Record<string, number>;\n\nexport const PriorityTierSchema = z.enum([\"P0\", \"P1\", \"P2\"]);\n\nexport type PriorityTier = z.infer<typeof PriorityTierSchema>;\n\nexport function computePriorityScore(factors: Record<string, number>): number {\n const {\n business_value,\n competitor_consensus,\n seo_leverage,\n implementation_ease,\n confidence,\n risk,\n } = factors;\n\n const safeRisk = risk === 0 ? 0.1 : risk;\n\n return (\n (business_value *\n competitor_consensus *\n seo_leverage *\n implementation_ease *\n confidence) /\n safeRisk\n );\n}\n\nexport function assignPriorityTier(\n score: number,\n allScores: number[]\n): PriorityTier {\n if (allScores.length === 0) return \"P2\";\n\n const sorted = [...allScores].sort((a, b) => a - b);\n const n = sorted.length;\n\n const p80Index = Math.floor(n * 0.8);\n const p50Index = Math.floor(n * 0.5);\n\n const p80Threshold = sorted[p80Index] ?? sorted[n - 1];\n const p50Threshold = sorted[p50Index] ?? sorted[n - 1];\n\n if (score >= p80Threshold) return \"P0\";\n if (score >= p50Threshold) return \"P1\";\n return \"P2\";\n}\n\nexport const POSITION_WEIGHTS: Readonly<Record<string, number>> = {\n Content: 1.0,\n Breadcrumb: 0.7,\n Navigation: 0.6,\n \"Navigation > Secondary\": 0.5,\n Sidebar: 0.5,\n Footer: 0.25,\n Sitewide: 0.15,\n} as const;\n\nexport const EXCESSIVE_OUTLINKS_THRESHOLD = 100;\n\nexport const VAGUE_ANCHOR_BLOCKLIST: readonly string[] = [\n \"click here\",\n \"read more\",\n \"learn more\",\n \"here\",\n \"this page\",\n] as const;\n\nexport const GuardrailFlagNameSchema = z.enum([\n \"seo_only_link\",\n \"vague_anchor\",\n \"exact_match_repetition\",\n \"excessive_outlinks\",\n \"orphaned_support_page\",\n \"crawl_depth_regression\",\n \"low_content_source\",\n \"circular_authority_trap\",\n]);\n\nexport type GuardrailFlagName = z.infer<typeof GuardrailFlagNameSchema>;\n\nexport function applyGuardrailRules(\n raw: unknown[],\n context: unknown\n): {\n passed: unknown[];\n flagged: Array<{ item: unknown; flags: GuardrailFlagName[] }>;\n} {\n const passed: unknown[] = [];\n const flagged: Array<{ item: unknown; flags: GuardrailFlagName[] }> = [];\n\n for (const item of raw) {\n const flags: GuardrailFlagName[] = [];\n const rec = item as Record<string, unknown>;\n\n const anchor =\n typeof rec[\"anchor_text\"] === \"string\"\n ? rec[\"anchor_text\"].trim().toLowerCase()\n : null;\n\n if (anchor !== null) {\n const isVague = VAGUE_ANCHOR_BLOCKLIST.some(\n (blocked) => blocked.toLowerCase() === anchor\n );\n if (isVague) flags.push(\"vague_anchor\");\n }\n\n const outlinks =\n typeof rec[\"outlink_count\"] === \"number\" ? rec[\"outlink_count\"] : null;\n\n if (outlinks !== null && outlinks >= EXCESSIVE_OUTLINKS_THRESHOLD) {\n flags.push(\"excessive_outlinks\");\n }\n\n if (\n context !== null &&\n context !== undefined &&\n typeof (context as Record<string, unknown>)[\"resolveFlags\"] === \"function\"\n ) {\n const resolveFlags = (context as Record<string, unknown>)[\n \"resolveFlags\"\n ] as (\n item: unknown,\n candidates: GuardrailFlagName[]\n ) => GuardrailFlagName[];\n\n const contextualCandidates: GuardrailFlagName[] = [\n \"seo_only_link\",\n \"exact_match_repetition\",\n \"orphaned_support_page\",\n \"crawl_depth_regression\",\n \"low_content_source\",\n \"circular_authority_trap\",\n ];\n\n const contextFlags = resolveFlags(item, contextualCandidates);\n flags.push(...contextFlags);\n }\n\n if (flags.length === 0) {\n passed.push(item);\n } else {\n flagged.push({ item, flags });\n }\n }\n\n return { passed, flagged };\n}\n\nexport const RequiredReportSectionSchema = z.enum([\n \"executive_summary\",\n \"url_inventory\",\n \"competitor_page_gap_matrix\",\n \"internal_link_graph\",\n \"architecture_score\",\n \"reverse_silo_plan\",\n \"implementation_backlog\",\n \"guardrail_flag_summary\",\n \"graph_visualization_views\",\n]);\n\nexport type RequiredReportSection = z.infer<typeof RequiredReportSectionSchema>;\n\nexport const LINK_RECOMMENDATION_REQUIRED_FIELDS: readonly string[] = [\n \"target_url\",\n \"source_url\",\n \"anchor_text\",\n \"placement\",\n \"reason\",\n] as const;\n\nexport const BACKLOG_ITEM_REQUIRED_FIELDS: readonly string[] = [\n \"priority_tier\",\n \"priority_score\",\n \"task\",\n \"owner\",\n \"input_needed\",\n \"output\",\n \"confidence\",\n \"confidence_basis\",\n \"business_value\",\n \"competitor_consensus\",\n \"seo_leverage\",\n \"implementation_ease\",\n \"risk\",\n \"formula\",\n] as const;\n\nexport const LocationArchitecturePatternSchema = z.enum([\n \"pattern_a_location_hub_only\",\n \"pattern_b_service_city_pages\",\n \"pattern_c_city_hub_nested_services\",\n \"pattern_d_state_first_hierarchy\",\n \"pattern_e_near_me_landing_strategy\",\n \"pattern_none_generic_service_pages\",\n]);\n\nexport type LocationArchitecturePattern = z.infer<\n typeof LocationArchitecturePatternSchema\n>;\n\nexport const NEAR_ME_PATTERNS: readonly string[] = [\n \"-near-me\",\n \"near-me-\",\n \"/near-me\",\n \"nearme\",\n] as const;\n\nexport const AMBIGUOUS_CITIES: readonly string[] = [\n \"mobile\",\n \"orange\",\n \"commerce\",\n \"industry\",\n \"normal\",\n \"reading\",\n \"frisco\",\n \"enterprise\",\n \"concord\",\n \"spring\",\n \"page\",\n \"cary\",\n \"dublin\",\n \"paris\",\n \"oxford\",\n \"clinton\",\n \"franklin\",\n \"madison\",\n \"portland\",\n \"aurora\",\n \"florence\",\n \"lincoln\",\n \"jackson\",\n \"monroe\",\n \"jefferson\",\n \"washington\",\n] as const;\n\nexport const AMBIGUOUS_STATE_ABBRS: readonly string[] = [\n \"me\",\n \"in\",\n \"or\",\n \"pa\",\n \"la\",\n \"ok\",\n] as const;\n\nexport const CONFIDENCE_THRESHOLDS_LOCATION = {\n very_high: 80,\n high: 60,\n medium: 40,\n} as const;\n","import { z } from 'zod';\nimport { ValidatedFileManifestSchema } from '../schemas.js';\n\nexport { ValidatedFileManifestSchema };\n\nexport const IngestValidateInputSchema = z.object({\n clientDomain: z.string(),\n sfInternalPath: z.string(),\n sfInlinksPath: z.string(),\n sfOutlinksPath: z.string(),\n sitemapPath: z.string(),\n competitorDomainsCsvPath: z.string(),\n visualSitemapsPath: z.string().optional(),\n gscPath: z.string().optional(),\n ga4Path: z.string().optional(),\n ahrefsPath: z.string().optional(),\n businessContext: z.string().optional(),\n priorAuditMemoryEnabled: z.boolean().default(false),\n});\n\nexport type IngestValidateInput = z.infer<typeof IngestValidateInputSchema>;\n\nconst H1 = '#';\nconst H2 = '##';\nconst H3 = '###';\n\nexport function buildIngestValidatePrompt(input: IngestValidateInput): string {\n const sessionPath = `<session-path-from-job>`;\n const outputPath = `${sessionPath}/phase1`;\n const memoryPath = `${sessionPath}/memory/${input.clientDomain}.json`;\n\n const lines: string[] = [\n `${H1} Phase 1: File Ingestion and Validation`,\n ``,\n `You are the **ingest-validate agent** for the **Site Architecture Auditor** skill ecosystem.`,\n ``,\n `This is a sequential phase. You run once per audit session and are the hard gate for all downstream phases.`,\n ``,\n `Output path: ${outputPath}`,\n `Session: ${sessionPath}`,\n `Client domain: ${input.clientDomain}`,\n ``,\n `Input files:`,\n `- SF Internal CSV: ${input.sfInternalPath}`,\n `- SF All Inlinks CSV: ${input.sfInlinksPath}`,\n `- SF All Outlinks CSV: ${input.sfOutlinksPath}`,\n `- XML Sitemap: ${input.sitemapPath}`,\n `- Competitor domains CSV: ${input.competitorDomainsCsvPath}`,\n `- VisualSitemaps (optional): ${input.visualSitemapsPath ?? 'null'}`,\n `- GSC export (optional): ${input.gscPath ?? 'null'}`,\n `- GA4 export (optional): ${input.ga4Path ?? 'null'}`,\n `- Ahrefs export (optional): ${input.ahrefsPath ?? 'null'}`,\n `- Business context (optional): ${input.businessContext ?? 'null'}`,\n `- Prior audit memory enabled: ${input.priorAuditMemoryEnabled}`,\n ``,\n `---`,\n ``,\n `${H2} Reference`,\n ``,\n `No external reference files required for this phase.`,\n ``,\n `---`,\n ``,\n `${H2} Task`,\n ``,\n `Validate all user-uploaded files for this audit session, detect the Screaming Frog schema version per file, verify required Python dependencies are installed, query memory for prior audit records for this client domain, and emit three outputs: \\`validated_file_manifest.json\\`, \\`schema_version_map.json\\`, and \\`ingestion_error_log.jsonl\\`. Good output means every required column is confirmed present for each file type, every site has a schema version recorded, the manifest accurately reflects which optional sources are available, and any blocking errors halt the process immediately with a clear remediation message. Missing this phase produces incorrect cross-source comparisons, bad URL normalization, and silent graph construction failures downstream.`,\n ``,\n `---`,\n ``,\n `${H2} Step 1 — Verify Python Environment`,\n ``,\n `Run the following Python check using the Bash tool:`,\n ``,\n `\\`\\`\\`bash`,\n `python3 -c \"import networkx, pandas, lxml; print('deps:ok')\"`,\n `\\`\\`\\``,\n ``,\n `If the command fails or returns an import error, write one entry to \\`ingestion_error_log.jsonl\\`:`,\n ``,\n `\\`\\`\\`json`,\n `{\"error_severity\":\"blocking\",\"error_code\":\"dep-001\",\"message\":\"Required Python package missing: {package}. Install with: pip install networkx pandas lxml\",\"file\":null,\"remediation\":\"Run: pip install networkx pandas lxml\"}`,\n `\\`\\`\\``,\n ``,\n `Also verify uszips.csv is accessible (required for Phase 3 location classification):`,\n ``,\n `\\`\\`\\`bash`,\n `python3 -c \"`,\n `import csv, os`,\n `path = '/Users/vilovieta/Downloads/sales-magician-api-leads-magician-01c6cff78e31/tools/analytics/data/uszips.csv'`,\n `if not os.path.exists(path):`,\n ` print('uszips:missing')`,\n `else:`,\n ` with open(path) as f:`,\n ` cols = next(csv.reader(f))`,\n ` expected = ['state_fips','state','state_abbr','zipcode','county','city']`,\n ` print('uszips:ok' if cols == expected else f'uszips:schema-mismatch:{cols}')`,\n `\"`,\n `\\`\\`\\``,\n ``,\n `If \\`uszips:missing\\`, write a **warning** (non-blocking) to \\`ingestion_error_log.jsonl\\`:`,\n ``,\n `\\`\\`\\`json`,\n `{\"error_severity\":\"warning\",\"error_code\":\"dep-002\",\"message\":\"uszips.csv not found at expected path. Location classifier will be disabled — all URL classification will use Claude. Phase 3 LLM costs will be higher.\",\"file\":\"/Users/vilovieta/Downloads/sales-magician-api-leads-magician-01c6cff78e31/tools/analytics/data/uszips.csv\",\"remediation\":\"Verify the sales-magician codebase is present at the expected path. See reference/05-location-classifier.md.\"}`,\n `\\`\\`\\``,\n ``,\n `If \\`uszips:schema-mismatch\\`, write the same warning with the mismatch detail in the message. The audit continues without the location classifier — all URLs fall through to Claude classification.`,\n ``,\n `Proceed only if the check passes.`,\n ``,\n `---`,\n ``,\n `${H2} Step 2 — Validate Screaming Frog Internal CSV`,\n ``,\n `Read the file at ${input.sfInternalPath}.`,\n ``,\n `**Required columns (must all be present):**`,\n `- \\`Address\\``,\n `- \\`Status Code\\``,\n `- \\`Indexability\\``,\n ``,\n `**Schema version detection — inspect the header row:**`,\n `- If column \\`Canonicalised URL\\` is present → schema version \\`v3.17+\\``,\n `- If column \\`Link Score\\` is present → schema version \\`v3.15–3.16\\``,\n `- If neither advanced column is present → schema version \\`v3.x-legacy\\``,\n ``,\n `Record the detected version. If any required column is absent, add a blocking error to the error log:`,\n ``,\n `\\`\\`\\`json`,\n `{\"error_severity\":\"blocking\",\"error_code\":\"sf-internal-001\",\"message\":\"SF Internal CSV missing required column: {column}\",\"file\":\"${input.sfInternalPath}\",\"remediation\":\"Re-export from Screaming Frog: Internal tab → Export after crawl analysis completes\"}`,\n `\\`\\`\\``,\n ``,\n `---`,\n ``,\n `${H2} Step 3 — Validate Screaming Frog Inlinks CSV`,\n ``,\n `Read the file at ${input.sfInlinksPath}.`,\n ``,\n `**Required columns (must all be present):**`,\n `- \\`Source\\``,\n `- \\`Destination\\``,\n `- \\`Anchor\\``,\n `- \\`Follow\\``,\n `- \\`Type\\``,\n `- \\`Position\\``,\n ``,\n `If any are missing, add a blocking error using code \\`sf-inlinks-001\\`. If the \\`Position\\` column is present but contains only \\`N/A\\` values throughout, add a warning (non-blocking):`,\n ``,\n `\\`\\`\\`json`,\n `{\"error_severity\":\"warning\",\"error_code\":\"sf-inlinks-002\",\"message\":\"Position column present but all values are N/A — edge weight assignment will default to Content weight for all links\",\"file\":\"${input.sfInlinksPath}\",\"remediation\":\"Re-crawl with Screaming Frog Crawl Analysis enabled to populate link positions\"}`,\n `\\`\\`\\``,\n ``,\n `---`,\n ``,\n `${H2} Step 4 — Validate Screaming Frog Outlinks CSV`,\n ``,\n `Read the file at ${input.sfOutlinksPath}.`,\n ``,\n `**Required columns (must all be present):**`,\n `- \\`Source\\``,\n `- \\`Destination\\``,\n `- \\`Anchor\\``,\n `- \\`Follow\\``,\n `- \\`Type\\``,\n `- \\`Position\\``,\n ``,\n `If any are missing, add a blocking error using code \\`sf-outlinks-001\\`.`,\n ``,\n `---`,\n ``,\n `${H2} Step 5 — Validate XML Sitemap`,\n ``,\n `Read the file at ${input.sitemapPath}.`,\n ``,\n `Parse the XML. Accept either of two root element types:`,\n `- \\`<urlset>\\` — standard sitemap`,\n `- \\`<sitemapindex>\\` — sitemap index referencing child sitemaps`,\n ``,\n `If the file is not valid XML, add a blocking error:`,\n ``,\n `\\`\\`\\`json`,\n `{\"error_severity\":\"blocking\",\"error_code\":\"sitemap-001\",\"message\":\"XML sitemap is not parseable XML\",\"file\":\"${input.sitemapPath}\",\"remediation\":\"Validate XML at https://www.xmlvalidation.com/ and fix malformed tags\"}`,\n `\\`\\`\\``,\n ``,\n `If the root element is neither \\`<urlset>\\` nor \\`<sitemapindex>\\`, add a blocking error with code \\`sitemap-002\\`.`,\n ``,\n `If valid, record: root element type, URL count (count of \\`<loc>\\` elements), and whether any \\`<loc>\\` values return HTTP redirects or 4xx (do not fetch at this stage — flag for Phase 2 detection).`,\n ``,\n `---`,\n ``,\n `${H2} Step 6 — Validate Optional Files (if paths provided)`,\n ``,\n `For each optional file path that is not empty or null:`,\n ``,\n `**VisualSitemaps (${input.visualSitemapsPath ?? 'not provided'}):**`,\n `- Accept \\`.csv\\` or \\`.json\\` extension`,\n `- If CSV: confirm \\`URL\\` column present`,\n `- If JSON: confirm top-level array with \\`url\\` field in first element`,\n `- On failure: add warning (non-blocking), code \\`vs-001\\``,\n ``,\n `**GSC export (${input.gscPath ?? 'not provided'}):**`,\n `- Confirm \\`Page\\` or \\`URL\\` column present plus at least one metric column (\\`Clicks\\`, \\`Impressions\\`, \\`CTR\\`, \\`Position\\`)`,\n `- On failure: add warning (non-blocking), code \\`gsc-001\\``,\n ``,\n `**GA4 export (${input.ga4Path ?? 'not provided'}):**`,\n `- Confirm \\`Page path\\` or \\`Landing page\\` column present`,\n `- On failure: add warning (non-blocking), code \\`ga4-001\\``,\n ``,\n `**Ahrefs export (${input.ahrefsPath ?? 'not provided'}):**`,\n `- Confirm \\`URL\\` column present`,\n `- On failure: add warning (non-blocking), code \\`ahrefs-001\\``,\n ``,\n `---`,\n ``,\n `${H2} Step 7 — Parse Competitor Domains`,\n ``,\n `Read ${input.competitorDomainsCsvPath}. Extract all competitor domains (one per row or comma-separated in a single field — detect which format is used).`,\n ``,\n `For each competitor domain:`,\n `- Check whether an SF Internal CSV, Inlinks CSV, and Outlinks CSV have been uploaded for that domain in \\`${sessionPath}\\``,\n `- Set \\`has_screaming_frog: true\\` if all three SF files are present, \\`false\\` otherwise`,\n `- Set \\`requires_firecrawl_enrichment: true\\` if \\`has_screaming_frog\\` is \\`false\\``,\n `- Check for companion sitemap file`,\n `- Set \\`has_sitemap: true\\` if an XML sitemap for this domain is present`,\n ``,\n `Competitor entries with no SF exports and no sitemap will be flagged for Firecrawl enrichment in the manifest.`,\n ``,\n `---`,\n ``,\n `${H2} Step 8 — Query Prior Audit Memory`,\n ``,\n `If \\`${input.priorAuditMemoryEnabled}\\` is \\`true\\`, read the memory file for this domain at \\`${memoryPath}\\`.`,\n ``,\n `Extract:`,\n `- \\`last_audit_date\\` (ISO date string)`,\n `- \\`architecture_score\\` (float)`,\n `- \\`link_equity_score\\` (float)`,\n `- \\`top_findings\\` (array, up to 5 strings)`,\n ``,\n `If no prior record is found, set \\`prior_audit_delta: null\\` in the manifest.`,\n ``,\n `If a prior record is found, set \\`prior_audit_delta\\` with those fields so Phase 5 can compute improvement deltas.`,\n ``,\n `---`,\n ``,\n `${H2} Step 9 — Hard Gate Check`,\n ``,\n `Collect all entries in the error log where \\`error_severity\\` is \\`\"blocking\"\\`.`,\n ``,\n `If the blocking errors array is non-empty:`,\n ``,\n `\\`\\`\\``,\n `GATE BLOCKED: The following blocking errors prevent Phase 2 from starting:`,\n ` - [error_code]: [message]`,\n ` - [error_code]: [message]`,\n ``,\n `Remediation steps:`,\n ` 1. [remediation for first error]`,\n ` 2. [remediation for second error]`,\n ``,\n `Re-run /site-architecture-auditor ingest after correcting these files.`,\n `\\`\\`\\``,\n ``,\n `Print this message and stop. Do not write a \\`validated_file_manifest.json\\` with \\`status: complete\\`. Write \\`ingestion_error_log.jsonl\\` and \\`schema_version_map.json\\` only.`,\n ``,\n `---`,\n ``,\n `${H2} Step 10 — Write Outputs`,\n ``,\n `${H3} \\`${outputPath}/validated_file_manifest.json\\``,\n ``,\n `Write a single JSON object (not JSONL) with this structure:`,\n ``,\n `\\`\\`\\`json`,\n `{`,\n ` \"client_domain\": \"${input.clientDomain}\",`,\n ` \"session_path\": \"${sessionPath}\",`,\n ` \"audit_timestamp\": \"<ISO timestamp>\",`,\n ` \"prior_audit_delta\": null,`,\n ` \"sites\": [`,\n ` {`,\n ` \"site_id\": \"client\",`,\n ` \"role\": \"client\",`,\n ` \"domain\": \"${input.clientDomain}\",`,\n ` \"sf_internal_path\": \"${input.sfInternalPath}\",`,\n ` \"sf_inlinks_path\": \"${input.sfInlinksPath}\",`,\n ` \"sf_outlinks_path\": \"${input.sfOutlinksPath}\",`,\n ` \"sitemap_path\": \"${input.sitemapPath}\",`,\n ` \"visual_sitemap_path\": null,`,\n ` \"gsc_path\": null,`,\n ` \"ga4_path\": null,`,\n ` \"ahrefs_path\": null,`,\n ` \"has_screaming_frog\": true,`,\n ` \"has_sitemap\": true,`,\n ` \"has_visual_sitemap\": false,`,\n ` \"has_gsc\": false,`,\n ` \"schema_version\": \"<detected version>\",`,\n ` \"url_count_sitemap\": 0,`,\n ` \"requires_firecrawl_enrichment\": false`,\n ` }`,\n ` ],`,\n ` \"blocking_errors\": [],`,\n ` \"warnings\": [],`,\n ` \"python_deps_verified\": true`,\n `}`,\n `\\`\\`\\``,\n ``,\n `Add one entry per competitor domain to the \\`sites\\` array using the data collected in Step 7.`,\n ``,\n `Fill optional path fields with the validated path if the file was provided and passed validation, or \\`null\\` if not provided or failed validation.`,\n ``,\n `${H3} \\`${outputPath}/schema_version_map.json\\``,\n ``,\n `Write a JSON object mapping each site domain to its detected SF schema version:`,\n ``,\n `\\`\\`\\`json`,\n `{`,\n ` \"${input.clientDomain}\": \"v3.17+\",`,\n ` \"competitor1.com\": \"v3.15-3.16\"`,\n `}`,\n `\\`\\`\\``,\n ``,\n `Competitors with \\`has_screaming_frog: false\\` map to \\`\"none\"\\`.`,\n ``,\n `${H3} \\`${outputPath}/ingestion_error_log.jsonl\\``,\n ``,\n `Write one JSON object per line for every error and warning collected across all steps. End with a summary record:`,\n ``,\n `\\`\\`\\`json`,\n `{\"_summary\":true,\"entry_type\":\"ingestion_error\",\"total_count\":<N>,\"blocking_count\":<B>,\"warning_count\":<W>,\"status\":\"complete\"}`,\n `\\`\\`\\``,\n ``,\n `Where \\`N\\` is total error+warning count (not including the summary line), \\`B\\` is count of blocking errors, \\`W\\` is count of warnings.`,\n ``,\n `---`,\n ``,\n `Status: COMPLETE`,\n ];\n\n return lines.join('\\n');\n}\n","import { z } from 'zod';\n\nexport const EnrichCompetitorInputSchema = z.object({\n domain: z.string(),\n sessionPath: z.string(),\n});\n\nexport type EnrichCompetitorInput = z.infer<typeof EnrichCompetitorInputSchema>;\n\nconst H1 = '#';\nconst H2 = '##';\n\nexport function buildEnrichCompetitorPrompt(input: EnrichCompetitorInput): string {\n const outputPath = `${input.sessionPath}/phase1`;\n const validatedFileManifestPath = `${input.sessionPath}/phase1/validated_file_manifest.json`;\n const competitorIndex = `<competitor-index>`;\n\n const lines: string[] = [\n `${H1} Phase 1 (Conditional): Competitor Firecrawl Enrichment`,\n ``,\n `You are the **enrich-competitor agent** for the **Site Architecture Auditor** skill ecosystem.`,\n ``,\n `This template runs **only when** \\`validated_file_manifest.json\\` marks a competitor with \\`requires_firecrawl_enrichment: true\\`. One invocation per affected competitor. You build a partial page inventory using Firecrawl in place of a Screaming Frog export. All output is annotated as partial quality — downstream agents must treat this data as incomplete relative to a full SF crawl.`,\n ``,\n `Output path: ${outputPath}`,\n `Session: ${input.sessionPath}`,\n `Competitor domain: ${input.domain}`,\n `Competitor index: ${competitorIndex}`,\n `Validated file manifest: ${validatedFileManifestPath}`,\n ``,\n `---`,\n ``,\n `${H2} Reference`,\n ``,\n `Read before acting:`,\n `- \\`~/.claude/skills/site-architecture-auditor/reference/01-page-type-taxonomy.md\\``,\n ``,\n `Use the 22-type taxonomy for preliminary page type hints only. Do not classify with high confidence from Firecrawl data — all classifications from this output carry \\`enrichment_quality: \"partial\"\\`.`,\n ``,\n `---`,\n ``,\n `${H2} Task`,\n ``,\n `Use Firecrawl to crawl the competitor site and extract all discoverable URLs, page titles, H1 headings, and meta descriptions, building a partial URL inventory that Phase 3 can use for page type classification. This enrichment substitutes for a Screaming Frog export where SF data was not provided — it will not produce link graph edges, status codes, or crawl depth data, so all downstream agents that consume this output must treat it as incomplete. Good output covers the homepage, all top-level navigation destinations, and as many service/product/content pages as possible within the 20-page limit. Poor output would silently omit major page categories or fail to annotate the data quality gap.`,\n ``,\n `**Page budget cap: 20 pages maximum.** Do not exceed 20 total pages scraped across all steps.`,\n ``,\n `---`,\n ``,\n `${H2} Step 1 — Read and Confirm Manifest Entry`,\n ``,\n `Read \\`${validatedFileManifestPath}\\`.`,\n ``,\n `Locate the entry in the \\`sites\\` array where \\`domain\\` matches \\`${input.domain}\\`.`,\n ``,\n `Confirm:`,\n `- \\`requires_firecrawl_enrichment\\` is \\`true\\``,\n `- \\`has_screaming_frog\\` is \\`false\\``,\n ``,\n `If the entry shows \\`has_screaming_frog: true\\`, stop immediately and print:`,\n ``,\n `\\`\\`\\``,\n `ENRICHMENT SKIPPED: ${input.domain} has Screaming Frog data. No Firecrawl enrichment needed.`,\n `\\`\\`\\``,\n ``,\n `Do not write any output file.`,\n ``,\n `---`,\n ``,\n `${H2} Step 2 — Scrape Homepage`,\n ``,\n `Use the Bash tool to invoke Firecrawl scrape on the competitor homepage:`,\n ``,\n `\\`\\`\\`bash`,\n `uv run --with firecrawl-py ~/.claude/skills/research/scripts/scrape.py \"https://${input.domain}\"`,\n `\\`\\`\\``,\n ``,\n `If the homepage returns a 404 or connection error, fall back to Firecrawl search:`,\n ``,\n `\\`\\`\\`bash`,\n `uv run --with firecrawl-py ~/.claude/skills/research/scripts/scrape.py \"site:${input.domain}\"`,\n `\\`\\`\\``,\n ``,\n `From the homepage response extract:`,\n `- \\`url\\` — canonical URL found in response headers or \\`<link rel=\"canonical\">\\`; fall back to the requested URL`,\n `- \\`title\\` — \\`<title>\\` tag content`,\n `- \\`h1\\` — first \\`<h1>\\` text content; null if absent`,\n `- \\`meta_description\\` — \\`<meta name=\"description\">\\` content; null if absent`,\n `- \\`depth_estimate\\` — 0 (homepage is always depth 0)`,\n `- Navigation links — all \\`<a href>\\` values in \\`<nav>\\`, header, and primary menu elements. These seed the crawl queue in Step 3.`,\n ``,\n `Record this as one JSONL entry with \\`page_type_hint: \"homepage\"\\`.`,\n ``,\n `**Running page budget:** 1 of 20 used.`,\n ``,\n `---`,\n ``,\n `${H2} Step 3 — Crawl Top Navigation Pages`,\n ``,\n `From the navigation links discovered in Step 2, identify up to 10 distinct internal URLs (same domain, non-hash, non-anchor-only).`,\n ``,\n `Prioritize in this order:`,\n `1. URLs that appear to be service or product pages (contain \\`/services/\\`, \\`/products/\\`, \\`/solutions/\\`, \\`/what-we-do/\\` in path)`,\n `2. URLs that appear to be location pages (contain \\`/location/\\`, \\`/areas/\\`, city/state patterns)`,\n `3. URLs that appear to be pricing pages (contain \\`/pricing/\\`, \\`/plans/\\`, \\`/cost/\\`)`,\n `4. Remaining nav URLs in order of appearance`,\n ``,\n `For each selected URL, invoke Firecrawl scrape:`,\n ``,\n `\\`\\`\\`bash`,\n `uv run --with firecrawl-py ~/.claude/skills/research/scripts/scrape.py \"https://${input.domain}{path}\"`,\n `\\`\\`\\``,\n ``,\n `Extract per page: \\`url\\`, \\`title\\`, \\`h1\\`, \\`meta_description\\`. Set \\`depth_estimate: 1\\`.`,\n ``,\n `Stop when budget reaches 11 pages (1 homepage + 10 nav pages) or nav links are exhausted.`,\n ``,\n `**Running page budget:** up to 11 of 20 used after this step.`,\n ``,\n `---`,\n ``,\n `${H2} Step 4 — Discover Additional Pages via Firecrawl Search`,\n ``,\n `Use Firecrawl search to discover indexed pages not found through navigation:`,\n ``,\n `\\`\\`\\`bash`,\n `uv run --with firecrawl-py ~/.claude/skills/research/scripts/scrape.py \"site:${input.domain} -inurl:(login OR cart OR checkout OR account)\"`,\n `\\`\\`\\``,\n ``,\n `From search results, extract URLs not already collected. Scrape each unique URL not yet in inventory up to the remaining budget (20 − pages already collected).`,\n ``,\n `For each page scraped: extract \\`url\\`, \\`title\\`, \\`h1\\`, \\`meta_description\\`. Set \\`depth_estimate: 2\\` (approximate — these are search-discovered, not navigation-discovered).`,\n ``,\n `Stop when total page count reaches 20.`,\n ``,\n `---`,\n ``,\n `${H2} Step 5 — Assign Preliminary Page Type Hints`,\n ``,\n `For each collected URL, assign a \\`page_type_hint\\` using URL path patterns and title signals from \\`01-page-type-taxonomy.md\\`.`,\n ``,\n `Rules:`,\n `- Do not use high-confidence classification — these are hints only`,\n `- Set \\`classification_confidence: \"low\"\\` on every record (Firecrawl data lacks status codes, link context, and crawl depth)`,\n `- Use path-pattern matching first (e.g., \\`/blog/\\` → \\`blog_post\\`, \\`/about\\` → \\`about\\`)`,\n `- Use title matching second (e.g., title contains \"pricing\" → \\`pricing\\`)`,\n `- If neither pattern matches: set \\`page_type_hint: \"unknown\"\\``,\n ``,\n `---`,\n ``,\n `${H2} Step 6 — Write Output`,\n ``,\n `Write \\`${input.sessionPath}/phase1/competitor-${competitorIndex}-enrichment.jsonl\\`.`,\n ``,\n `One JSON object per line. Every record must include these fields:`,\n ``,\n `\\`\\`\\`json`,\n `{`,\n ` \"competitor_domain\": \"${input.domain}\",`,\n ` \"competitor_index\": ${competitorIndex},`,\n ` \"url\": \"https://${input.domain}/example-page/\",`,\n ` \"title\": \"Example Page Title\",`,\n ` \"h1\": \"Example H1 Heading\",`,\n ` \"meta_description\": \"Page meta description text.\",`,\n ` \"depth_estimate\": 1,`,\n ` \"page_type_hint\": \"service_page\",`,\n ` \"classification_confidence\": \"low\",`,\n ` \"source\": \"firecrawl\",`,\n ` \"data_quality\": \"partial — firecrawl enrichment\",`,\n ` \"enrichment_quality\": \"partial\"`,\n `}`,\n `\\`\\`\\``,\n ``,\n `Set \\`h1\\` and \\`meta_description\\` to \\`null\\` if not found. Do not omit these fields.`,\n ``,\n `After all page records, write the summary line:`,\n ``,\n `\\`\\`\\`json`,\n `{\"_summary\":true,\"entry_type\":\"competitor_enrichment\",\"total_count\":<N>,\"competitor_domain\":\"${input.domain}\",\"competitor_index\":${competitorIndex},\"enrichment_quality\":\"partial\",\"pages_scraped\":<N>,\"budget_limit\":20,\"source\":\"firecrawl\",\"status\":\"complete\"}`,\n `\\`\\`\\``,\n ``,\n `Where \\`total_count\\` and \\`pages_scraped\\` equal the number of page records written (not including the summary line).`,\n ``,\n `---`,\n ``,\n `${H2} Step 7 — Print Completion Notice`,\n ``,\n `Print:`,\n ``,\n `\\`\\`\\``,\n `Competitor enrichment complete: ${input.domain} (index ${competitorIndex})`,\n ` Pages scraped: <N> / 20`,\n ` Output: ${input.sessionPath}/phase1/competitor-${competitorIndex}-enrichment.jsonl`,\n ` Data quality: partial — firecrawl enrichment`,\n ` NOTE: This competitor has no Screaming Frog data. Phase 3 classification will run`,\n ` with reduced confidence. Phase 4 recommendations involving this competitor`,\n ` will carry data_quality: partial annotations.`,\n `\\`\\`\\``,\n ``,\n `---`,\n ``,\n `Status: COMPLETE`,\n ];\n\n return lines.join('\\n');\n}\n","import { z } from 'zod';\n\nexport const BuildGraphPerSiteInputSchema = z.object({\n siteId: z.string(),\n domain: z.string(),\n urlInventory: z.array(\n z.object({\n url: z.string(),\n sitemap_present: z.boolean(),\n crawl_depth: z.number().optional(),\n })\n ),\n directedEdgeCount: z.number(),\n});\nexport type BuildGraphPerSiteInput = z.infer<typeof BuildGraphPerSiteInputSchema>;\n\nexport function buildGraphPerSitePrompt(input: BuildGraphPerSiteInput): string {\n const orphanCandidates = input.urlInventory.filter(\n (u) => !u.sitemap_present && (u.crawl_depth === undefined || u.crawl_depth === null)\n );\n\n const inventoryBlock = input.urlInventory\n .map((u) => {\n const depth =\n u.crawl_depth !== undefined && u.crawl_depth !== null\n ? `crawl_depth=${u.crawl_depth}`\n : 'crawl_depth=MISSING';\n const sitemap = u.sitemap_present ? 'in_sitemap=true' : 'in_sitemap=false';\n return ` - ${u.url} [${depth}, ${sitemap}]`;\n })\n .join('\\n');\n\n const orphanBlock =\n orphanCandidates.length > 0\n ? orphanCandidates.map((u) => ` - ${u.url}`).join('\\n')\n : ' (none identified from input data)';\n\n return `You are a site architecture graph analyst performing Phase 2 orphan annotation for the Site Architecture Auditor.\n\nSITE: ${input.domain} (site_id: ${input.siteId})\nDIRECTED EDGES IN GRAPH: ${input.directedEdgeCount}\nTOTAL URLS IN INVENTORY: ${input.urlInventory.length}\n\n---\n\nTASK: Confirm orphan candidates using the two-source rule.\n\nTWO-SOURCE RULE (definitive):\nA URL is confirmed orphaned if and only if ALL of the following hold:\n1. It is NOT reachable from the crawl (crawl_depth is absent or undefined)\n2. It is NOT present in the XML sitemap (in_sitemap=false)\nA URL present in at least one of those two sources (crawl OR sitemap) is NOT an orphan.\n\nIMPORTANT: Do NOT use graph metrics for this determination. Orphan status is a structural\nreachability question, not a PageRank or link-count question. Python handles all numeric\ngraph computation. Your only job here is to apply the two-source rule to the inventory below.\n\n---\n\nURL INVENTORY:\n${inventoryBlock}\n\n---\n\nPRE-IDENTIFIED ORPHAN CANDIDATES (absent from both crawl and sitemap):\n${orphanBlock}\n\n---\n\nINSTRUCTIONS:\n1. Review the pre-identified orphan candidates above.\n2. For each candidate, confirm or reject orphan status by re-applying the two-source rule.\n - Confirm: crawl_depth is MISSING and in_sitemap=false -> confirmed orphan\n - Reject: if either crawl_depth is present OR in_sitemap=true -> not an orphan (re-check source data)\n3. Note any URLs where the inventory data appears inconsistent (e.g. has a crawl_depth but also flagged as candidate).\n4. Do NOT modify PageRank, crawl_depth, or any numeric field — those are [COMPUTED:python/networkx].\n5. Do NOT classify page types here — that is Phase 3.\n\n---\n\nOUTPUT FORMAT:\nReturn ONLY a valid JSON object — no markdown fences, no commentary.\n\n{\n \"site_id\": \"${input.siteId}\",\n \"domain\": \"${input.domain}\",\n \"confirmed_orphans\": [\n {\n \"url\": \"https://example.com/orphaned-page/\",\n \"reason\": \"absent from crawl and sitemap\"\n }\n ],\n \"rejected_candidates\": [\n {\n \"url\": \"https://example.com/not-really-orphaned/\",\n \"reason\": \"present in sitemap despite missing crawl_depth\"\n }\n ],\n \"data_quality_notes\": [\n \"Optional: note any inventory inconsistencies found during review\"\n ],\n \"orphan_count\": 0,\n \"_annotation_label\": \"[INTERPRETED:claude]\"\n}\n\nRULES:\n- confirmed_orphans must be a subset of the pre-identified candidates above — do not add new orphans not in that list.\n- rejected_candidates may be empty array if all candidates are confirmed.\n- data_quality_notes may be empty array if no issues found.\n- orphan_count must equal confirmed_orphans.length.\n- All decisions must be based solely on the two-source rule — do not infer orphan status from URL path patterns.`;\n}\n","import { z } from 'zod';\n\nexport const MetricsClassifyInputSchema = z.object({\n urls: z.array(\n z.object({\n url: z.string(),\n title: z.string().optional(),\n h1: z.string().optional(),\n meta_description: z.string().optional(),\n _location_hint: z.unknown().optional(),\n })\n ),\n siteDomain: z.string(),\n});\nexport type MetricsClassifyInput = z.infer<typeof MetricsClassifyInputSchema>;\n\nexport function buildMetricsClassifyPrompt(input: MetricsClassifyInput): string {\n const urlsBlock = input.urls\n .map((u) => {\n const lines: string[] = [` \"url\": \"${u.url}\"`];\n if (u.title) lines.push(` \"title\": ${JSON.stringify(u.title.slice(0, 160))}`);\n if (u.h1) lines.push(` \"h1\": ${JSON.stringify(u.h1.slice(0, 100))}`);\n if (u.meta_description)\n lines.push(` \"meta_description\": ${JSON.stringify(u.meta_description.slice(0, 160))}`);\n if (u._location_hint !== undefined && u._location_hint !== null) {\n lines.push(` \"_location_hint\": ${JSON.stringify(u._location_hint)}`);\n }\n return ` {\\n${lines.join(',\\n')}\\n }`;\n })\n .join(',\\n');\n\n const locationHintPresent = input.urls.some(\n (u) => u._location_hint !== undefined && u._location_hint !== null\n );\n const locationHintNote = locationHintPresent\n ? `\nLOCATION HINTS: Some URLs include a \"_location_hint\" field. This is a partial result from the\ndeterministic location classifier that could not reach high-confidence classification on its own.\nUse the hint as supporting context — it may indicate a likely location-related page type — but\napply your own judgment from the full 22-type taxonomy. The hint does not override your analysis.\n`\n : '';\n\n return `You are classifying URLs for a site architecture audit of ${input.siteDomain}.\nApply the 22-type taxonomy exactly as defined below. Return one JSON object per URL.\n\nBatch constraints enforced by caller: max 50 URLs per call, total payload <=8000 chars\n(url + title + h1 + meta_description per record).\n\nNote: location/service_location URLs already classified deterministically by the Python location\nclassifier are excluded from this batch. Only non-location or low-confidence location URLs appear here.\n${locationHintNote}\nTAXONOMY TYPES (22 canonical slugs):\n homepage, core_service, sub_service, location, service_location, industry, product, pricing,\n faq, blog_article, guide_resource, case_study, comparison, about, contact, team, careers,\n privacy_policy, terms, html_sitemap, login_portal, support_help_center\n\nDISAMBIGUATION RULES:\n- Apply the 22-type taxonomy exactly. Do NOT infer page type from URL path alone when title/h1 contradict the path.\n- A URL with \"blog\" in the path but a service-focused title and no article date is core_service or sub_service, not blog_article.\n- service_location = a page targeting a specific service in a specific city/area (e.g. \"Emergency Plumbing Dallas TX\").\n- location = a pure location hub page with no specific service focus (e.g. \"We Serve Dallas\").\n- core_service = primary service offered; sub_service = a more specific variant of a core service.\n- pricing = page whose primary purpose is showing pricing, costs, or quotes.\n- comparison = page comparing the business to competitors or comparing service options.\n- industry = page targeting a vertical/industry audience (e.g. \"Plumbing for Commercial Buildings\").\n- guide_resource = long-form educational content; faq = question-answer format page.\n- case_study = project or customer story with specific outcome data.\n- support_help_center = post-sale support, troubleshooting, or knowledge base content.\n- login_portal = login, account, dashboard, or authenticated app pages.\n- html_sitemap = a human-readable HTML sitemap page (not XML sitemap).\n- about, contact, team, careers, privacy_policy, terms = exactly as labeled.\n- homepage = the site root URL only.\n- If uncertain between two types, pick the one that best matches the primary conversion intent of the page.\n\nMONEY PAGE DEFINITION:\nA money page is any URL where the primary purpose is to generate revenue or capture a service lead.\nSet is_money_page=true for: core_service, sub_service, service_location, pricing, product, comparison.\nSet is_money_page=false for all others unless the content clearly contradicts the type assignment.\n\nCLASSIFICATION CONFIDENCE:\n- high: clear signals from URL + title + h1 all agree on the same type\n- medium: URL and one signal agree; other signals absent or ambiguous\n- low: signals conflict or all metadata is absent (URL path only)\n\nTOPIC CLUSTER:\nAssign a lowercase hyphenated topic cluster name that groups semantically related pages.\nExamples: \"emergency-plumbing\", \"commercial-roofing\", \"hvac-installation\", \"location-dallas-tx\".\nUse the service or topic focus — not the page type — as the cluster name.\nFor utility pages (contact, about, privacy_policy, etc.), use \"site-utility\".\n\n---\n\nURLS TO CLASSIFY:\n[\n${urlsBlock}\n]\n\n---\n\nOUTPUT FORMAT:\nReturn ONLY a valid JSON array — no markdown fences, no commentary, no trailing commas.\n\n[\n {\n \"url\": \"https://example.com/services/emergency-plumbing/\",\n \"page_type\": \"core_service\",\n \"topic_cluster_name\": \"emergency-plumbing\",\n \"is_money_page\": true,\n \"classification_confidence\": \"high\",\n \"_classification_label\": \"[INTERPRETED:claude]\"\n }\n]\n\nRULES:\n- Return exactly one object per input URL — same count, same order.\n- page_type must be one of the 22 canonical slugs listed above — no custom values.\n- topic_cluster_name must be lowercase, hyphenated, and descriptive — no spaces.\n- _classification_label must always be \"[INTERPRETED:claude]\" for every record in your output.\n- Do NOT include any field not listed in the output format above.\n- Do NOT include _location_hint in your output — it is input context only.`;\n}\n","import { z } from 'zod';\n\nexport const CompareRecommendInputSchema = z.object({\n clientDomain: z.string(),\n clientMoneyPages: z.array(z.unknown()),\n competitorSites: z.array(\n z.object({\n domain: z.string(),\n role: z.string(),\n urlInventory: z.array(z.unknown()),\n })\n ),\n clientUrlMetrics: z.array(z.unknown()),\n clientPageTypes: z.array(z.unknown()),\n});\nexport type CompareRecommendInput = z.infer<typeof CompareRecommendInputSchema>;\n\nconst GUARDRAIL_NOTICE =\n 'GUARDRAIL NOTICE: Do NOT recommend a link solely because it passes PageRank or ' +\n 'link equity to a target page, or for any SEO manipulation purpose. Every ' +\n 'recommendation must have a genuine topical relevance or user-value rationale. ' +\n 'Do NOT apply guardrail rule filtering inside your response — the caller ' +\n '(handleComparePhase) runs applyGuardrailRules() on your output before persisting. ' +\n 'Generate the full candidate set; the caller filters violations.';\n\nconst SECTION_GUIDE = [\n '## Step 5 — Topic Cluster Gap Analysis',\n 'For each client money page, identify its topic cluster and the number of support pages in that cluster.',\n 'Compare client support page count against the competitor median for the same cluster.',\n 'For each cluster where the gap > 0, write one sentence explaining the ranking impact.',\n 'Label computed values [COMPUTED:python] and your interpretation [INTERPRETED:claude].',\n '',\n '## Step 6 — UX/Nav Pattern Comparison',\n 'Identify primary navigation pages for each site (link_position includes \"Primary Navigation\", crawl_depth ≤ 2).',\n 'Compare which money page types are in competitor nav but absent from client nav.',\n 'Identify location architecture patterns and gaps.',\n 'Write one summary paragraph per site describing nav pattern and location architecture differences.',\n 'Label all LLM-generated prose [INTERPRETED:claude].',\n '',\n '## Step 8 — Link Recommendations',\n 'For each target page, identify existing client pages in the same topic cluster that do NOT currently link to it.',\n 'For each candidate source, write:',\n ' - A specific anchor text (not vague: avoid \"click here\", \"read more\", \"learn more\", \"here\", \"this page\")',\n ' - A specific placement instruction (section, position within page, after which element)',\n ' - A clear reason citing topical overlap and the inlink gap',\n 'All 5 required fields must be present: target_url, source_url, anchor_text, placement, reason.',\n 'Label anchor text [INTERPRETED:claude].',\n '',\n '## Step 9 — New Page Creation List',\n 'For each page type present in ≥2 competitors but absent from the client:',\n ' - Propose a specific url_slug following the client site\\'s URL pattern',\n ' - Assign nav_placement based on the client\\'s existing nav structure',\n ' - Identify which money page this new page will support (supports_target_url)',\n 'Label placement decisions [INTERPRETED:claude].',\n '',\n '## Output Format',\n 'Return a valid JSON object with these top-level keys:',\n ' \"topicClusterGapAnalysis\": array of records per money page',\n ' \"uxNavPatternComparison\": array of records per site',\n ' \"linkRecommendations\": array of link-add recommendations (all 5 fields required each)',\n ' \"newPageCreationList\": array of new page recommendations',\n '',\n 'Every link recommendation record must include:',\n ' target_url, source_url, anchor_text, placement, reason, topic_similarity_score, confidence',\n '',\n 'Every new page creation record must include:',\n ' url_slug, page_type, competitor_count, nav_placement, supports_target_url',\n].join('\\n');\n\nexport function buildCompareRecommendPrompt(input: CompareRecommendInput): string {\n const truncate = (s: string, n: number) =>\n s.length > n ? s.slice(0, n) + '\\n[...truncated...]' : s;\n\n const competitorSummary = input.competitorSites\n .map((c) => ` - ${c.domain} (${c.role}): ${(c.urlInventory as unknown[]).length} URLs`)\n .join('\\n');\n\n const moneyPageUrls = (input.clientMoneyPages as Array<{ url?: string }>)\n .map((p) => p.url ?? '(unknown)')\n .slice(0, 20)\n .join('\\n ');\n\n const pageTypeSummary = truncate(JSON.stringify(input.clientPageTypes), 4000);\n const metricsSummary = truncate(JSON.stringify(input.clientUrlMetrics), 4000);\n\n const parts = [\n 'You are a senior site architecture analyst performing Phase 4 of a Site Architecture Audit.',\n '',\n `Client domain: ${input.clientDomain}`,\n '',\n 'COMPETITOR SITES:',\n competitorSummary,\n '',\n 'CLIENT MONEY PAGES (up to 20 shown):',\n ` ${moneyPageUrls}`,\n '',\n 'CLIENT PAGE TYPE CLASSIFICATIONS:',\n pageTypeSummary,\n '',\n 'CLIENT URL METRICS (PageRank, depth, inlink counts):',\n metricsSummary,\n '',\n 'YOUR TASK:',\n '',\n 'Perform steps 5, 6, 8, and 9 of the Site Architecture Phase 4 analysis.',\n 'The page_type_coverage_matrix, architecture_depth_comparison, and target_page_list are',\n 'pre-computed by Python and provided as context. Your role is to add the interpretation',\n 'and recommendation layers that require natural language reasoning.',\n '',\n GUARDRAIL_NOTICE,\n '',\n SECTION_GUIDE,\n '',\n 'RULES:',\n '- Return ONLY a valid JSON object — no markdown fences, no commentary.',\n '- Every link recommendation must have all 5 required fields: target_url, source_url, anchor_text, placement, reason.',\n '- Anchor text must not be from the vague blocklist: \"click here\", \"read more\", \"learn more\", \"here\", \"this page\".',\n '- Label your interpretations [INTERPRETED:claude] and Python-computed values [COMPUTED:python].',\n '- Do not recompute PageRank, crawl depth, or inlink counts — use the values provided.',\n '- Do not hallucinate URLs — only use URLs present in the client URL metrics or competitor inventories.',\n ];\n\n return parts.join('\\n');\n}\n","import { z } from 'zod';\n\nexport const ScoreSynthesizeInputSchema = z.object({\n preComputedArchitectureScore: z.unknown(),\n preComputedIlesScore: z.unknown(),\n rankedBacklog: z.array(z.unknown()),\n clientDomain: z.string(),\n sessionPath: z.string(),\n});\nexport type ScoreSynthesizeInput = z.infer<typeof ScoreSynthesizeInputSchema>;\n\nconst REPORT_SECTIONS = [\n '## Executive Summary',\n 'Write the top 5 architecture gaps, top 5 internal link opportunities, top 5 pages to create,',\n 'top 5 pages to elevate in navigation, and top 5 orphan/crawl coverage issues.',\n 'Each entry is one sentence with the impact and the action. Label [INTERPRETED:claude].',\n '',\n '## Architecture Score: {score}/100 [COMPUTED:python]',\n 'Present the pre-computed score in a table with all 6 sub-dimensions:',\n 'url_inventory_health (20%), crawlability (20%), important_page_depth (20%),',\n 'page_type_coverage (15%), topical_cluster_completeness (15%), ux_nav_clarity (10%).',\n 'Columns: Sub-dimension | Weight | Raw Score | Weighted | Data Source.',\n 'Add a 2-3 sentence interpretation of what the score reveals. Label [INTERPRETED:claude].',\n 'Do NOT recompute the score — use the preComputedArchitectureScore values exactly.',\n '',\n '## Internal Link Equity Score: {score}/100 [COMPUTED:python]',\n 'Present the pre-computed ILES in a table with all 6 sub-dimensions:',\n 'target_page_internal_pagerank (30%), content_inlinks_to_target_pages (20%),',\n 'anchor_relevance (15%), link_position_quality (15%), support_cluster_strength (10%),',\n 'low_value_link_waste_reduction (10%).',\n 'Columns: Sub-dimension | Weight | Raw Score | Weighted | Data Source.',\n 'Add a 2-3 sentence interpretation of the biggest leverage points. Label [INTERPRETED:claude].',\n 'Do NOT recompute the score — use the preComputedIlesScore values exactly.',\n '',\n '## URL Inventory',\n 'Present a summary table: total crawled, indexable, non-indexable, sitemap URLs, orphan candidates,',\n 'sitemap-only (not crawled), crawl-only (not in sitemap), non-indexable in sitemap, canonical conflicts,',\n 'redirects in sitemap, 404s in sitemap.',\n 'Source each row from the pre-computed phase data. Add 1-2 sentences on notable inventory patterns.',\n '',\n '## Competitor Page Gap Matrix',\n 'Present all 22 page types in a table. Columns: Page Type | Client | Competitor 1 | Competitor 2 | Gap Flag | Recommendation.',\n 'Values: present / absent. Gap Flag: GAP (absent for client, present in ≥1 competitor),',\n 'PARTIAL GAP, or — (no gap). Source: pre-computed phase-4 coverage matrix.',\n '',\n '## Architecture Depth Comparison',\n 'Present a table with columns: Page Type | Client Median Depth | Competitor Median Depth | Delta | Action.',\n 'Positive delta = client pages are buried deeper. Source: pre-computed phase-4 depth comparison.',\n '',\n '## Topic Cluster Analysis',\n 'For each client money page, write a subsection with: page type, topic cluster, current content inlinks,',\n 'competitor median content inlinks, crawl depth, internal PageRank, current support pages,',\n 'support gap, gap interpretation, and recommended new support pages.',\n 'Label computed values [COMPUTED:python] and interpretations [INTERPRETED:claude].',\n '',\n '## Internal Link Recommendations',\n 'For each item in the link recommendations (ordered by confidence descending), write a subsection:',\n '→ {target_url}, Add link from: {source_url}, Anchor text, Placement, Reason,',\n 'Topic similarity score, Confidence.',\n 'All 5 required fields must appear. Label anchor and placement [INTERPRETED:claude].',\n '',\n '## New Page Creation Plan',\n 'For each item in the new page creation list (P0 first, then P1, then P2), write a subsection:',\n '{url_slug} — Priority: {tier}. Include: page type, competitor consensus, gap priority score',\n 'with all 5 formula factors shown, nav placement, supports target URL, required inbound link.',\n '',\n '## Guardrail Flags',\n 'Present a table of all rejected recommendations with columns:',\n 'Rule # | Rule Triggered | Item Type | Target URL | Source URL | Reason Excluded.',\n 'Do NOT silently omit this section even if the flag list is empty (write \"No guardrail flags.\").',\n '',\n '## Implementation Backlog',\n 'Present all backlog tasks ranked by priority score (descending). P0 = execute immediately,',\n 'P1 = next sprint, P2 = roadmap. Columns:',\n 'Priority | Tier | Task | Owner | Input Needed | Output | Confidence | Gap Score.',\n 'Each row must name a specific URL and specific deliverable. Source: rankedBacklog.',\n].join('\\n');\n\nexport function buildScoreSynthesizePrompt(input: ScoreSynthesizeInput): string {\n const truncate = (s: string, n: number) =>\n s.length > n ? s.slice(0, n) + '\\n[...truncated...]' : s;\n\n const architectureScoreJson = truncate(JSON.stringify(input.preComputedArchitectureScore, null, 2), 3000);\n const ilesScoreJson = truncate(JSON.stringify(input.preComputedIlesScore, null, 2), 3000);\n const backlogJson = truncate(JSON.stringify(input.rankedBacklog), 5000);\n\n const parts = [\n 'You are the synthesis agent for the Site Architecture Auditor.',\n 'All numeric scoring is pre-computed. Your task is to assemble the 9-section',\n 'Site Architecture Intelligence Report as a Markdown document.',\n '',\n 'CRITICAL: Do NOT recompute the Architecture Score or the Internal Link Equity Score.',\n 'Use the preComputedArchitectureScore and preComputedIlesScore values exactly as provided.',\n '',\n `Client domain: ${input.clientDomain}`,\n `Session path: ${input.sessionPath}`,\n '',\n 'PRE-COMPUTED ARCHITECTURE SCORE:',\n architectureScoreJson,\n '',\n 'PRE-COMPUTED INTERNAL LINK EQUITY SCORE (ILES):',\n ilesScoreJson,\n '',\n 'RANKED IMPLEMENTATION BACKLOG:',\n backlogJson,\n '',\n 'YOUR TASK:',\n '',\n 'Write the complete Site Architecture Intelligence Report in Markdown.',\n 'The report must contain all 9 sections listed below, in exact order.',\n 'Each section must have a level-2 heading (##) matching the name shown.',\n 'The report ends with the line: Status: COMPLETE',\n '',\n REPORT_SECTIONS,\n '',\n 'OUTPUT RULES:',\n '- Output ONLY the Markdown report. No preamble, no trailing commentary.',\n '- Section headings must match exactly as shown above.',\n '- Use the pre-computed scores as the sole source of numeric facts. Do not hallucinate metrics.',\n '- Label all LLM-generated prose [INTERPRETED:claude].',\n '- Label all formula/graph outputs [COMPUTED:python] or [COMPUTED:python/formula].',\n '- The report must end with: Status: COMPLETE',\n '- The report must be at least 1000 words.',\n '',\n 'Begin the report now.',\n ];\n\n return parts.join('\\n');\n}\n","import { type Client } from '@libsql/client/http'\nimport { getDb } from '../../api/db.js'\nimport { SiteAuditJobRowSchema } from '../../api/db.js'\nimport type { SiteAuditJobRow } from '../../api/db.js'\nimport type {\n SiteAuditStartRequest,\n SiteAuditJobStatus,\n SiteAuditResult,\n SiteAuditPhase,\n} from './schemas.js'\n\nexport interface ISiteAuditRepository {\n createSiteAuditJob(jobId: string, userId: number, request: SiteAuditStartRequest): Promise<void>\n claimPendingSiteAuditJob(): Promise<SiteAuditJobRow | null>\n getSiteAuditJob(jobId: string): Promise<SiteAuditJobRow | null>\n listSiteAuditJobs(userId?: number): Promise<SiteAuditJobRow[]>\n updateSiteAuditJobState(jobId: string, status: SiteAuditJobStatus): Promise<void>\n completeSiteAuditJob(jobId: string, result: SiteAuditResult): Promise<void>\n failSiteAuditJob(jobId: string, error: string): Promise<void>\n logSiteAuditPhaseComplete(jobId: string, phase: SiteAuditPhase, output: unknown): Promise<void>\n}\n\nfunction rowToSiteAuditJobRow(row: Record<string, unknown>): SiteAuditJobRow {\n return SiteAuditJobRowSchema.parse({\n id: String(row.id),\n user_id: Number(row.user_id),\n status: row.status != null ? String(row.status) : 'pending',\n client_domain: String(row.client_domain),\n session_path: String(row.session_path),\n request: String(row.request),\n result: row.result != null ? String(row.result) : null,\n error: row.error != null ? String(row.error) : null,\n created_at: String(row.created_at),\n updated_at: String(row.updated_at),\n })\n}\n\nexport class SiteAuditRepository implements ISiteAuditRepository {\n private db: Client\n\n constructor() {\n this.db = getDb()\n }\n\n async createSiteAuditJob(jobId: string, userId: number, request: SiteAuditStartRequest): Promise<void> {\n await this.db.execute({\n sql: `INSERT INTO site_audit_jobs\n (id, user_id, status, client_domain, session_path, request, created_at, updated_at)\n VALUES (?, ?, 'pending', ?, ?, ?, datetime('now'), datetime('now'))`,\n args: [\n jobId,\n userId,\n request.clientDomain,\n request.sessionPath,\n JSON.stringify(request),\n ],\n })\n }\n\n async claimPendingSiteAuditJob(): Promise<SiteAuditJobRow | null> {\n const upd = await this.db.execute(\n `UPDATE site_audit_jobs\n SET status = 'running', updated_at = datetime('now')\n WHERE id = (\n SELECT id FROM site_audit_jobs\n WHERE status = 'pending'\n ORDER BY created_at ASC\n LIMIT 1\n )\n RETURNING *`\n )\n if (upd.rowsAffected === 0) return null\n const raw = upd.rows[0]\n if (!raw) return null\n return rowToSiteAuditJobRow(raw as unknown as Record<string, unknown>)\n }\n\n async getSiteAuditJob(jobId: string): Promise<SiteAuditJobRow | null> {\n const res = await this.db.execute({\n sql: `SELECT * FROM site_audit_jobs WHERE id = ?`,\n args: [jobId],\n })\n const raw = res.rows[0]\n if (!raw) return null\n return rowToSiteAuditJobRow(raw as unknown as Record<string, unknown>)\n }\n\n async listSiteAuditJobs(userId?: number): Promise<SiteAuditJobRow[]> {\n const res = userId != null\n ? await this.db.execute({\n sql: `SELECT * FROM site_audit_jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50`,\n args: [userId],\n })\n : await this.db.execute(\n `SELECT * FROM site_audit_jobs ORDER BY created_at DESC LIMIT 50`\n )\n return res.rows.map(r => rowToSiteAuditJobRow(r as unknown as Record<string, unknown>))\n }\n\n async updateSiteAuditJobState(jobId: string, status: SiteAuditJobStatus): Promise<void> {\n await this.db.execute({\n sql: `UPDATE site_audit_jobs SET status = ?, updated_at = datetime('now') WHERE id = ?`,\n args: [status, jobId],\n })\n }\n\n async completeSiteAuditJob(jobId: string, result: SiteAuditResult): Promise<void> {\n await this.db.execute({\n sql: `UPDATE site_audit_jobs\n SET status = 'complete', result = ?, updated_at = datetime('now')\n WHERE id = ?`,\n args: [JSON.stringify(result), jobId],\n })\n }\n\n async failSiteAuditJob(jobId: string, error: string): Promise<void> {\n await this.db.execute({\n sql: `UPDATE site_audit_jobs\n SET status = 'failed', error = ?, updated_at = datetime('now')\n WHERE id = ?`,\n args: [error, jobId],\n })\n }\n\n async logSiteAuditPhaseComplete(jobId: string, phase: SiteAuditPhase, output: unknown): Promise<void> {\n await this.db.execute({\n sql: `INSERT OR IGNORE INTO site_audit_phase_log (job_id, phase, output_summary, completed_at)\n VALUES (?, ?, ?, datetime('now'))`,\n args: [jobId, phase, JSON.stringify(output)],\n }).catch((err: unknown) => {\n console.warn(\n '[site-audit-repository] logSiteAuditPhaseComplete failed:',\n err instanceof Error ? err.message : String(err)\n )\n })\n }\n}\n","import * as childProcess from 'node:child_process';\nimport * as path from 'node:path';\nimport * as util from 'node:util';\nimport { fileURLToPath } from 'node:url';\nimport {\n PythonGraphInputSchema,\n PythonGraphOutputSchema,\n LocationClassifierInputSchema,\n LocationClassifierOutputSchema,\n type PythonGraphInput,\n type PythonGraphOutput,\n type LocationClassifierInput,\n type LocationClassifierOutput,\n} from '../schemas.js';\n\nconst execFile = util.promisify(childProcess.execFile);\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface ISiteAuditPythonRunner {\n runGraphMetrics(input: PythonGraphInput): Promise<PythonGraphOutput>;\n runLocationClassifier(input: LocationClassifierInput): Promise<LocationClassifierOutput>;\n}\n\nexport class SiteAuditPythonRunner implements ISiteAuditPythonRunner {\n private readonly graphMetricsScript: string;\n private readonly locationClassifierScript: string;\n\n constructor() {\n try {\n childProcess.execFileSync('python3', ['-c', 'import networkx']);\n } catch {\n throw new Error('SiteAuditPythonRunner: networkx not available');\n }\n\n this.graphMetricsScript = path.join(__dirname, 'scripts', 'compute_graph_metrics.py');\n this.locationClassifierScript = path.join(__dirname, 'scripts', 'location_classifier.py');\n }\n\n async runGraphMetrics(input: PythonGraphInput): Promise<PythonGraphOutput> {\n const validatedInput = PythonGraphInputSchema.parse(input);\n\n let stdout: string;\n let stderr: string;\n\n try {\n ({ stdout, stderr } = await execFile(\n 'python3',\n [this.graphMetricsScript, '--input', JSON.stringify(validatedInput)],\n { timeout: 120_000 },\n ));\n } catch (err: unknown) {\n const execError = err as { stderr?: string; code?: number | string };\n throw new Error(`python-subprocess-001: ${execError.stderr ?? String(err)}`);\n }\n\n void stderr;\n\n return PythonGraphOutputSchema.parse(JSON.parse(stdout));\n }\n\n async runLocationClassifier(input: LocationClassifierInput): Promise<LocationClassifierOutput> {\n const validatedInput = LocationClassifierInputSchema.parse(input);\n\n let stdout: string;\n let stderr: string;\n\n try {\n ({ stdout, stderr } = await execFile(\n 'python3',\n [this.locationClassifierScript, '--input', JSON.stringify(validatedInput)],\n { timeout: 120_000 },\n ));\n } catch (err: unknown) {\n const execError = err as { stderr?: string; code?: number | string };\n throw new Error(`python-subprocess-001: ${execError.stderr ?? String(err)}`);\n }\n\n void stderr;\n\n return LocationClassifierOutputSchema.parse(JSON.parse(stdout));\n }\n}\n","import { DeepInfraLlmClient, OpenRouterLlmClient, FallbackLlmClient } from '../../lib/llm-parse-with-retry.js';\nimport { FetchHttpClient } from '../../lib/http-client.js';\nimport { type ISiteAuditService, SiteAuditService } from './site-audit-service.js';\nimport { SiteAuditRepository } from './site-audit-repository.js';\nimport { SiteAuditPythonRunner } from './python/graph-runner.js';\n\nexport function makeSiteAuditService(): ISiteAuditService {\n const deepInfraKey = process.env['DEEPINFRA_API_KEY'];\n if (!deepInfraKey) throw new Error('DEEPINFRA_API_KEY is required');\n const primary = new DeepInfraLlmClient(deepInfraKey);\n const llm = process.env['OPENROUTER_API_KEY']\n ? new FallbackLlmClient(primary, new OpenRouterLlmClient(process.env['OPENROUTER_API_KEY']))\n : primary;\n\n const repo = new SiteAuditRepository();\n const http = new FetchHttpClient();\n const python = new SiteAuditPythonRunner();\n\n return new SiteAuditService({ repo, llm, http, python });\n}\n","import pLimit from 'p-limit';\nimport { type ISiteAuditService, type CompareRecommendOutput } from './site-audit-service.js';\nimport {\n type SiteAuditPhase,\n SiteAuditIngestPayloadSchema,\n SiteAuditBuildGraphPayloadSchema,\n SiteAuditClassifyPayloadSchema,\n SiteAuditComparePayloadSchema,\n SiteAuditSynthesizePayloadSchema,\n} from './schemas.js';\nimport { applyGuardrailRules, type GuardrailFlagName } from '../../lib/site-architecture-auditor/rules.js';\n\nexport async function dispatchSiteAuditPhase(\n service: ISiteAuditService,\n phase: SiteAuditPhase,\n raw: unknown,\n): Promise<void> {\n switch (phase) {\n case 'phase1-ingest':\n return handleIngestPhase(service, raw);\n case 'phase2-build-graph':\n return handleBuildGraphPhase(service, raw);\n case 'phase3-classify':\n return handleClassifyPhase(service, raw);\n case 'phase4-compare':\n return handleComparePhase(service, raw);\n case 'phase5-synthesize':\n return handleSynthesizePhase(service, raw);\n default: {\n const _exhaustive: never = phase;\n throw new Error('Unknown phase: ' + String(_exhaustive));\n }\n }\n}\n\nexport async function handleIngestPhase(\n service: ISiteAuditService,\n raw: unknown,\n): Promise<void> {\n const payload = SiteAuditIngestPayloadSchema.parse(raw);\n await service.runIngestPhase(payload.jobId, payload);\n await service.runEnrichPhase(payload.jobId, payload);\n}\n\nexport async function handleBuildGraphPhase(\n service: ISiteAuditService,\n raw: unknown,\n): Promise<void> {\n const payload = SiteAuditBuildGraphPayloadSchema.parse(raw);\n const limit = pLimit(3);\n const tasks = [\n limit(() => service.runBuildGraphPhase(payload.jobId, payload)),\n ];\n const results = await Promise.allSettled(tasks);\n for (const result of results) {\n if (result.status === 'rejected') {\n console.warn(\n '[phases] handleBuildGraphPhase: phase failed for job',\n payload.jobId,\n result.reason instanceof Error ? result.reason.message : String(result.reason),\n );\n }\n }\n}\n\nexport async function handleClassifyPhase(\n service: ISiteAuditService,\n raw: unknown,\n): Promise<void> {\n const payload = SiteAuditClassifyPayloadSchema.parse(raw);\n const limit = pLimit(3);\n const tasks = [\n limit(() => service.runClassifyPhase(payload.jobId, payload)),\n ];\n const results = await Promise.allSettled(tasks);\n for (const result of results) {\n if (result.status === 'rejected') {\n console.warn(\n '[phases] handleClassifyPhase: phase failed for job',\n payload.jobId,\n result.reason instanceof Error ? result.reason.message : String(result.reason),\n );\n }\n }\n}\n\nexport async function handleComparePhase(\n service: ISiteAuditService,\n raw: unknown,\n): Promise<void> {\n const payload = SiteAuditComparePayloadSchema.parse(raw);\n const compareResult: CompareRecommendOutput = await service.runComparePhase(payload.jobId, payload);\n\n const rawRecommendations: unknown[] = compareResult.linkRecommendations ?? [];\n\n const resolveFlagsStub = (_item: unknown, _candidates: GuardrailFlagName[]): GuardrailFlagName[] => {\n return [];\n };\n\n const guardrailResult = applyGuardrailRules(rawRecommendations, { resolveFlags: resolveFlagsStub });\n console.warn(\n '[phases] handleComparePhase: guardrail check complete — passed:',\n guardrailResult.passed.length,\n 'flagged:',\n guardrailResult.flagged.length,\n '(context-dependent flags require site-graph state not yet exposed by runComparePhase)',\n );\n}\n\nexport async function handleSynthesizePhase(\n service: ISiteAuditService,\n raw: unknown,\n): Promise<void> {\n const payload = SiteAuditSynthesizePayloadSchema.parse(raw);\n await service.runSynthesizePhase(payload.jobId, payload);\n}\n","import { type InngestFunction } from 'inngest';\nimport { inngest } from '../client.js';\nimport { makeSiteAuditService } from '../../services/site-architecture-auditor/factory.js';\nimport { dispatchSiteAuditPhase } from '../../services/site-architecture-auditor/phases.js';\nimport { type SiteAuditPhase } from '../../services/site-architecture-auditor/schemas.js';\n\nexport const siteAuditFn: InngestFunction<any, any, any, any, any> = inngest.createFunction(\n { id: 'site-audit', triggers: [{ event: 'site-architecture-auditor/audit.requested' }] },\n async ({ event, step }: { event: { data: { phase: SiteAuditPhase; payload: unknown } }; step: any }) => {\n const { phase, payload } = event.data;\n\n await step.run('dispatch-phase', async () => {\n const service = makeSiteAuditService();\n await dispatchSiteAuditPhase(service, phase, payload);\n });\n\n return { phase, status: 'done' };\n },\n);\n","import path from 'node:path';\nimport os from 'node:os';\nimport { Hono } from 'hono';\nimport { ZodError } from 'zod';\nimport { siteAuditAuth, type SiteAuditEnv } from './site-audit-middleware.js';\nimport { makeSiteAuditService } from '../services/site-architecture-auditor/factory.js';\nimport { dispatchSiteAuditPhase } from '../services/site-architecture-auditor/phases.js';\nimport {\n SiteAuditStartRequestSchema,\n SiteAuditIngestRequestSchema,\n SiteAuditBuildGraphRequestSchema,\n SiteAuditClassifyRequestSchema,\n SiteAuditCompareRequestSchema,\n SiteAuditScoreRequestSchema,\n SiteAuditReportRequestSchema,\n} from '../services/site-architecture-auditor/schemas.js';\n\nexport const siteAuditApp = new Hono<SiteAuditEnv>();\n\nsiteAuditApp.onError((err, c) => {\n if (err instanceof ZodError) {\n return c.json({ error: 'Invalid request body', issues: err.issues }, 400);\n }\n const msg = err instanceof Error ? err.message : String(err);\n return c.json({ error: msg }, 500);\n});\n\nsiteAuditApp.use('*', siteAuditAuth);\n\nfunction isPathInside(root: string, target: string): boolean {\n const relative = path.relative(root, target);\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nsiteAuditApp.post('/audit', async (c) => {\n const body = SiteAuditStartRequestSchema.parse(await c.req.json());\n\n const allowedRoot = path.resolve(process.env['SESSION_ROOT'] ?? os.tmpdir());\n const sessionPath = path.resolve(body.sessionPath);\n if (!isPathInside(allowedRoot, sessionPath)) {\n return c.json({ error: 'sessionPath must be inside the configured session root' }, 400);\n }\n\n const pathFields: Array<string | undefined> = [\n body.sessionPath,\n body.sfInternalPath,\n body.sfInlinksPath,\n body.sfOutlinksPath,\n body.sitemapPath,\n body.competitorDomainsCsvPath,\n body.visualSitemapsPath,\n body.gscPath,\n body.ga4Path,\n body.ahrefsPath,\n ];\n\n for (const filePath of pathFields) {\n if (filePath != null && !isPathInside(allowedRoot, path.resolve(filePath))) {\n return c.json({ error: 'Path traversal attempt' }, 400);\n }\n }\n\n const userId = c.get('siteAuditUserId');\n const service = makeSiteAuditService();\n const jobId = await service.startAudit(userId, body);\n\n void dispatchSiteAuditPhase(service, 'phase1-ingest', { jobId, request: body }).catch(\n (err: unknown) => {\n console.error(\n '[site-audit-routes] phase1-ingest dispatch failed for job',\n jobId,\n err instanceof Error ? err.message : String(err),\n );\n },\n );\n\n return c.json({ jobId }, 202);\n});\n\nsiteAuditApp.post('/ingest', async (c) => {\n const body = SiteAuditIngestRequestSchema.parse(await c.req.json());\n const service = makeSiteAuditService();\n const job = await service.getJob(body.jobId);\n if (!job) return c.json({ error: 'Not found' }, 404);\n if (job.user_id !== c.get('siteAuditUserId')) return c.json({ error: 'Not found' }, 404);\n\n void dispatchSiteAuditPhase(service, 'phase1-ingest', {\n jobId: body.jobId,\n request: JSON.parse(job.request) as unknown,\n }).catch((err: unknown) => {\n console.error(\n '[site-audit-routes] phase1-ingest re-dispatch failed for job',\n body.jobId,\n err instanceof Error ? err.message : String(err),\n );\n });\n\n return c.json({ jobId: body.jobId }, 202);\n});\n\nsiteAuditApp.post('/build-graph', async (c) => {\n const body = SiteAuditBuildGraphRequestSchema.parse(await c.req.json());\n const service = makeSiteAuditService();\n const job = await service.getJob(body.jobId);\n if (!job) return c.json({ error: 'Not found' }, 404);\n if (job.user_id !== c.get('siteAuditUserId')) return c.json({ error: 'Not found' }, 404);\n\n void dispatchSiteAuditPhase(service, 'phase2-build-graph', { jobId: body.jobId }).catch(\n (err: unknown) => {\n console.error(\n '[site-audit-routes] phase2-build-graph failed for job',\n body.jobId,\n err instanceof Error ? err.message : String(err),\n );\n },\n );\n\n return c.json({ jobId: body.jobId }, 202);\n});\n\nsiteAuditApp.post('/classify', async (c) => {\n const body = SiteAuditClassifyRequestSchema.parse(await c.req.json());\n const service = makeSiteAuditService();\n const job = await service.getJob(body.jobId);\n if (!job) return c.json({ error: 'Not found' }, 404);\n if (job.user_id !== c.get('siteAuditUserId')) return c.json({ error: 'Not found' }, 404);\n\n void dispatchSiteAuditPhase(service, 'phase3-classify', { jobId: body.jobId }).catch(\n (err: unknown) => {\n console.error(\n '[site-audit-routes] phase3-classify failed for job',\n body.jobId,\n err instanceof Error ? err.message : String(err),\n );\n },\n );\n\n return c.json({ jobId: body.jobId }, 202);\n});\n\nsiteAuditApp.post('/compare', async (c) => {\n const body = SiteAuditCompareRequestSchema.parse(await c.req.json());\n const service = makeSiteAuditService();\n const job = await service.getJob(body.jobId);\n if (!job) return c.json({ error: 'Not found' }, 404);\n if (job.user_id !== c.get('siteAuditUserId')) return c.json({ error: 'Not found' }, 404);\n\n void dispatchSiteAuditPhase(service, 'phase4-compare', { jobId: body.jobId }).catch(\n (err: unknown) => {\n console.error(\n '[site-audit-routes] phase4-compare failed for job',\n body.jobId,\n err instanceof Error ? err.message : String(err),\n );\n },\n );\n\n return c.json({ jobId: body.jobId }, 202);\n});\n\nsiteAuditApp.post('/score', async (c) => {\n const body = SiteAuditScoreRequestSchema.parse(await c.req.json());\n const service = makeSiteAuditService();\n const job = await service.getJob(body.jobId);\n if (!job) return c.json({ error: 'Not found' }, 404);\n if (job.user_id !== c.get('siteAuditUserId')) return c.json({ error: 'Not found' }, 404);\n\n void dispatchSiteAuditPhase(service, 'phase5-synthesize', {\n jobId: body.jobId,\n scoringOnly: true,\n reportOnly: false,\n }).catch((err: unknown) => {\n console.error(\n '[site-audit-routes] phase5-synthesize(scoringOnly) failed for job',\n body.jobId,\n err instanceof Error ? err.message : String(err),\n );\n });\n\n return c.json({ jobId: body.jobId }, 202);\n});\n\nsiteAuditApp.post('/report', async (c) => {\n const body = SiteAuditReportRequestSchema.parse(await c.req.json());\n const service = makeSiteAuditService();\n const job = await service.getJob(body.jobId);\n if (!job) return c.json({ error: 'Not found' }, 404);\n if (job.user_id !== c.get('siteAuditUserId')) return c.json({ error: 'Not found' }, 404);\n\n void dispatchSiteAuditPhase(service, 'phase5-synthesize', {\n jobId: body.jobId,\n scoringOnly: false,\n reportOnly: true,\n }).catch((err: unknown) => {\n console.error(\n '[site-audit-routes] phase5-synthesize(reportOnly) failed for job',\n body.jobId,\n err instanceof Error ? err.message : String(err),\n );\n });\n\n return c.json({ jobId: body.jobId }, 202);\n});\n\nsiteAuditApp.get('/jobs/:id', async (c) => {\n const jobId = c.req.param('id');\n const userId = c.get('siteAuditUserId');\n const service = makeSiteAuditService();\n const job = await service.getJob(jobId);\n if (!job) return c.json({ error: 'Not found' }, 404);\n if (job.user_id !== userId) return c.json({ error: 'Not found' }, 404);\n return c.json(job);\n});\n\nsiteAuditApp.get('/jobs', async (c) => {\n const userId = c.get('siteAuditUserId');\n const service = makeSiteAuditService();\n const jobs = await service.getJobsForUser(userId);\n return c.json(jobs);\n});\n","import { createMiddleware } from 'hono/factory'\nimport { getUserByApiKey } from './db.js'\n\nexport type SiteAuditEnv = {\n Variables: {\n siteAuditUserId: number\n siteAuditEmail?: string\n }\n}\n\nexport const siteAuditAuth = createMiddleware<SiteAuditEnv>(async (c, next) => {\n const apiKey = c.req.header('x-api-key')\n\n if (apiKey) {\n const user = await getUserByApiKey(apiKey)\n if (!user) return c.json({ error: 'Unauthorized' }, 401)\n c.set('siteAuditUserId', user.id)\n return next()\n }\n\n return c.json({ error: 'Unauthorized' }, 401)\n})\n","import { Hono } from 'hono'\nimport { debitMc, creditMc, logRequestEvent } from './db.js'\nimport { MC_COSTS, insufficientBalanceResponse, LedgerOperation } from './rates.js'\nimport { ytHarvest } from '../youtube/youtube-harvest.js'\nimport { fetchCaptions } from '../youtube/CaptionFetcher.js'\nimport { buildYouTubeChannelVideosUrl } from '../driver/BrowserDriver.js'\nimport { CaptchaError } from '../errors.js'\nimport { createApiKeyAuth, type ApiKeyEnv } from './api-auth.js'\nimport { YoutubeHarvestBodySchema, YoutubeTranscribeBodySchema } from './server-schemas.js'\n\ntype YTEnv = ApiKeyEnv\n\nexport const youtubeApp = new Hono<YTEnv>()\n\nyoutubeApp.post('/harvest', createApiKeyAuth<YTEnv>(), async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = YoutubeHarvestBodySchema.safeParse(raw)\n if (!parsed.success) return c.json({ error: parsed.error.issues[0]?.message ?? 'Invalid request' }, 400)\n const { mode, query, channelHandle, maxVideos = 50 } = parsed.data\n\n if (mode === 'search' && !query?.trim()) {\n return c.json({ error: 'search mode requires query' }, 400)\n }\n if (mode === 'channel' && !channelHandle?.trim()) {\n return c.json({ error: 'channel mode requires channelHandle (@handle or UC... ID)' }, 400)\n }\n\n const user = c.get('user')\n const { ok: ytOk, balance_mc: ytBal } = await debitMc(user.id, MC_COSTS.yt_channel, LedgerOperation.YT_CHANNEL, query ?? channelHandle ?? '')\n if (!ytOk) return c.json(insufficientBalanceResponse(ytBal, MC_COSTS.yt_channel), 402)\n\n try {\n const normalizedChannelUrl = mode === 'channel'\n ? buildYouTubeChannelVideosUrl(channelHandle!.trim())\n : undefined\n const result = await ytHarvest({\n mode,\n query: query?.trim(),\n channelHandle: normalizedChannelUrl,\n maxVideos,\n downloadMp3: false,\n outputDir: '/tmp/yt-output',\n })\n await logRequestEvent({\n userId: user.id,\n source: 'youtube_harvest',\n status: 'done',\n query: mode === 'search' ? (query?.trim() ?? '') : (channelHandle?.trim() ?? ''),\n resultCount: Array.isArray((result as any).videos) ? (result as any).videos.length : null,\n result,\n })\n return c.json(result)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (msg.startsWith('channel ') || msg.includes('channelHandle')) {\n return c.json({ error: msg }, 400)\n }\n if (err instanceof CaptchaError || msg.includes('CAPTCHA') || msg.includes('blocked')) {\n await creditMc(user.id, MC_COSTS.yt_channel, LedgerOperation.YT_CHANNEL_REFUND, 'failed call')\n await logRequestEvent({ userId: user.id, source: 'youtube_harvest', status: 'failed', query: mode === 'search' ? (query?.trim() ?? '') : (channelHandle?.trim() ?? ''), error: msg })\n return c.json({ error: msg }, 503)\n }\n await creditMc(user.id, MC_COSTS.yt_channel, LedgerOperation.YT_CHANNEL_REFUND, 'failed call')\n await logRequestEvent({ userId: user.id, source: 'youtube_harvest', status: 'failed', query: mode === 'search' ? (query?.trim() ?? '') : (channelHandle?.trim() ?? ''), error: msg })\n return c.json({ error: msg }, 500)\n }\n})\n\nyoutubeApp.post('/transcribe', createApiKeyAuth<YTEnv>(), async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = YoutubeTranscribeBodySchema.safeParse(raw)\n if (!parsed.success) return c.json({ error: parsed.error.issues[0]?.message ?? 'Invalid request' }, 400)\n const { videoId } = parsed.data\n const id = videoId.trim()\n const user = c.get('user')\n const holdMins = 5\n const holdMc = MC_COSTS.yt_transcription * holdMins\n const { ok: trOk, balance_mc: trBal } = await debitMc(user.id, holdMc, LedgerOperation.TRANSCRIPTION_HOLD, id)\n if (!trOk) return c.json(insufficientBalanceResponse(trBal, holdMc), 402)\n\n try {\n const result = await fetchCaptions(id)\n const lastChunk = result.chunks?.[result.chunks.length - 1]\n const chunkSecs = lastChunk && Number.isFinite(lastChunk.timestamp[1]) ? lastChunk.timestamp[1] : 0\n const durationSecs = Number.isFinite(result.durationMs) ? result.durationMs / 1000 : 0\n const videoSecs = Math.max(chunkSecs, durationSecs) || 60\n const actualMins = Math.max(1, Math.ceil(videoSecs / 60))\n const actualMc = MC_COSTS.yt_transcription * actualMins\n const diff = holdMc - actualMc\n if (diff > 0) await creditMc(user.id, diff, LedgerOperation.TRANSCRIPTION_REFUND, 'overestimate refund')\n else if (diff < 0) await debitMc(user.id, -diff, LedgerOperation.TRANSCRIPTION, id)\n await logRequestEvent({\n userId: user.id,\n source: 'youtube_transcribe',\n status: 'done',\n query: id,\n resultCount: Array.isArray(result.chunks) ? result.chunks.length : null,\n result,\n })\n return c.json({\n ...result,\n markdown: buildTranscriptMarkdown(result),\n html: buildTranscriptHtml(result),\n })\n } catch (err) {\n await creditMc(user.id, holdMc, LedgerOperation.TRANSCRIPTION_REFUND, 'failed call')\n const msg = err instanceof Error ? err.message : String(err)\n await logRequestEvent({ userId: user.id, source: 'youtube_transcribe', status: 'failed', query: id, error: msg })\n return c.json({ error: msg }, 500)\n }\n})\n\nfunction buildTranscriptMarkdown(result: {\n videoId: string\n text: string\n chunks: Array<{ timestamp: [number, number]; text: string }>\n durationMs: number\n}): string {\n const lines: string[] = []\n lines.push(`# Transcript — ${result.videoId}`)\n lines.push('')\n lines.push(`*Extracted in ${(result.durationMs / 1000).toFixed(1)}s*`)\n lines.push('')\n lines.push('## Full Text')\n lines.push('')\n lines.push(result.text)\n lines.push('')\n if (result.chunks?.length) {\n lines.push('## Timestamped Segments')\n lines.push('')\n for (const chunk of result.chunks) {\n const start = fmtTs(chunk.timestamp[0])\n const end = fmtTs(chunk.timestamp[1])\n lines.push(`**[${start} → ${end}]** ${chunk.text.trim()}`)\n lines.push('')\n }\n }\n return lines.join('\\n')\n}\n\nfunction buildTranscriptHtml(result: {\n videoId: string\n text: string\n chunks: Array<{ timestamp: [number, number]; text: string }>\n durationMs: number\n}): string {\n const esc = (s: string) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')\n const fmtSecs = (n: number) => `${Math.floor(n / 60)}:${String(Math.floor(n % 60)).padStart(2, '0')}`\n\n const chunks = (result.chunks ?? []).map(ch =>\n `<div class=\"chunk\"><span class=\"ts\">${fmtSecs(ch.timestamp[0])} → ${fmtSecs(ch.timestamp[1])}</span><span class=\"ct\">${esc(ch.text.trim())}</span></div>`\n ).join('\\n')\n\n return `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<style>body{font-family:system-ui,sans-serif;max-width:800px;margin:1.5rem auto;padding:0 1rem;color:#111;line-height:1.7}\nh1{font-size:1.2rem;margin-bottom:.25rem}.meta{color:#666;font-size:.85rem;margin-bottom:1.5rem}\n.full-text{background:#f5f5f3;border-radius:8px;padding:1rem 1.25rem;font-size:.875rem;white-space:pre-wrap;margin-bottom:2rem;word-break:break-word}\n.chunk{padding:.5rem 0;border-bottom:1px solid #eee;display:flex;gap:.75rem;flex-wrap:wrap}\n.ts{font-family:monospace;font-size:.75rem;color:#888;white-space:nowrap;flex-shrink:0}\n.ct{font-size:.875rem;flex:1;min-width:0;word-break:break-word}\n</style></head><body>\n<h1>Transcript — ${esc(result.videoId)}</h1>\n<div class=\"meta\">Transcribed in ${(result.durationMs / 1000).toFixed(1)}s</div>\n<h2 style=\"font-size:1.05rem\">Full Text</h2>\n<div class=\"full-text\">${esc(result.text)}</div>\n${chunks.length ? `<h2 style=\"font-size:1.05rem\">Timestamped Segments</h2>\\n${chunks}` : ''}\n</body></html>`\n}\n\nfunction fmtTs(secs: number): string {\n return `${Math.floor(secs / 60)}:${String(Math.floor(secs % 60)).padStart(2, '0')}`\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport Papa from 'papaparse'\nimport { YouTubeHarvestOptionsSchema } from './schemas.js'\nimport type { YTHarvestResult, YouTubeHarvestOptions } from './types.js'\nimport { BrowserDriver } from '../driver/BrowserDriver.js'\nimport { YouTubeExtractor } from './YouTubeExtractor.js'\nimport { MP3Downloader } from './MP3Downloader.js'\nimport { YTProgressReporter } from './YTProgressReporter.js'\nimport { CaptchaError } from '../errors.js'\n\nconst MAX_ATTEMPTS = 3\nconst RETRY_DELAY_MS = 1_000\n\nasync function extractOnce(options: YouTubeHarvestOptions): Promise<YTHarvestResult> {\n const driver = new BrowserDriver()\n const reporter = new YTProgressReporter()\n const extractor = new YouTubeExtractor(driver, reporter)\n return extractor.extract(options)\n}\n\nasync function writeOutputs(result: YTHarvestResult, outputDir: string): Promise<void> {\n await fs.mkdir(outputDir, { recursive: true })\n const slug = result.target.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n const ts = Date.now()\n\n await fs.writeFile(\n path.join(outputDir, `${slug}-${ts}.json`),\n JSON.stringify(result, null, 2),\n 'utf8',\n )\n\n if (result.videos.length > 0) {\n await fs.writeFile(\n path.join(outputDir, `${slug}-videos-${ts}.csv`),\n Papa.unparse(result.videos, { header: true }),\n 'utf8',\n )\n }\n\n if (result.downloads.length > 0) {\n await fs.writeFile(\n path.join(outputDir, `${slug}-downloads-${ts}.csv`),\n Papa.unparse(result.downloads, { header: true }),\n 'utf8',\n )\n }\n}\n\nexport async function ytHarvest(rawOptions: unknown): Promise<YTHarvestResult> {\n const kernelApiKey = process.env.KERNEL_API_KEY\n if (!kernelApiKey) {\n throw new Error('A browser backend API key is required — YouTube harvesting requires a stealth session.')\n }\n\n const raw = typeof rawOptions === 'object' && rawOptions !== null ? rawOptions : {}\n const options = YouTubeHarvestOptionsSchema.parse({ kernelApiKey, ...raw })\n\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n try {\n const result = await extractOnce(options)\n\n if (options.downloadMp3 && result.videos.length > 0) {\n const reporter = new YTProgressReporter()\n const downloader = new MP3Downloader(options.outputDir, reporter)\n result.downloads = await downloader.downloadAll(result.videos)\n result.stats.totalDownloads = result.downloads.filter((d) => d.success).length\n }\n\n await writeOutputs(result, options.outputDir)\n return result\n } catch (err) {\n if (err instanceof CaptchaError && attempt < MAX_ATTEMPTS) {\n await new Promise((r) => setTimeout(r, RETRY_DELAY_MS))\n continue\n }\n throw err\n }\n }\n\n throw new CaptchaError(\n `YouTube blocked all ${MAX_ATTEMPTS} attempts. Try again in a few minutes.`,\n )\n}\n","import { z } from 'zod'\n\nexport const YouTubeHarvestOptionsSchema = z.object({\n mode: z.enum(['search', 'channel']),\n query: z.string().optional(),\n channelHandle: z.string().optional(),\n maxVideos: z.number().int().min(1).max(5000).default(100),\n downloadMp3: z.boolean().default(false),\n outputDir: z.string().default('./yt-output'),\n kernelApiKey: z.string().optional(),\n headless: z.boolean().default(false),\n}).refine(\n (o) => o.mode === 'search' ? !!o.query : !!o.channelHandle,\n { message: 'search mode requires query; channel mode requires channelHandle' },\n)\n","import type { Page } from 'playwright'\nimport type { IBrowserDriver } from '../driver/IBrowserDriver.js'\nimport type { IYTProgressReporter } from './YTProgressReporter.js'\nimport { CaptchaError } from '../errors.js'\nimport type { DriverConfig } from '../types.js'\nimport type {\n YTVideoResult, YTChannelResult, YTChannelMeta,\n YTHarvestResult, YTHarvestStats, YTInnerTubeContext, YouTubeHarvestOptions,\n} from './types.js'\n\nexport class YouTubeExtractor {\n constructor(\n private readonly driver: IBrowserDriver,\n private readonly reporter: IYTProgressReporter,\n ) {}\n\n private async waitForInitialData(page: Page): Promise<unknown> {\n try {\n await page.waitForFunction(\n () => !!(window as unknown as { ytInitialData?: unknown }).ytInitialData,\n { timeout: 20_000 },\n )\n } catch {\n const url = page.url()\n const title = await page.title().catch(() => '')\n const isBlocked = /consent\\.youtube\\.|accounts\\.google\\.|sorry\\.google\\./i.test(url)\n || /before you continue|sign in|unusual traffic|are you a robot/i.test(title)\n if (isBlocked) {\n throw new CaptchaError('YouTube blocked this session — retrying with a fresh session.')\n }\n throw new CaptchaError(`ytInitialData not found after 20s (url=${url}, title=\"${title}\") — retrying.`)\n }\n return page.evaluate(() => (window as unknown as { ytInitialData: unknown }).ytInitialData)\n }\n\n private async getInnerTubeContext(page: Page): Promise<YTInnerTubeContext> {\n return page.evaluate(() => {\n const cfg = (window as unknown as { ytcfg?: { data_?: Record<string, unknown> } }).ytcfg?.data_ ?? {}\n return {\n clientName: (cfg.INNERTUBE_CLIENT_NAME as string | undefined) ?? 'WEB',\n clientVersion: (cfg.INNERTUBE_CLIENT_VERSION as string | undefined) ?? '2.20260515.01.00',\n hl: (cfg.HL as string | undefined) ?? 'en',\n gl: (cfg.GL as string | undefined) ?? 'US',\n visitorData: (cfg.VISITOR_DATA as string | undefined) ?? '',\n }\n })\n }\n\n private parseVideoRenderer(v: Record<string, unknown>): YTVideoResult | null {\n const videoId = v.videoId as string | undefined\n if (!videoId) return null\n\n const titleRuns = (v.title as { runs?: Array<{ text: string }> } | undefined)?.runs ?? []\n const title = titleRuns[0]?.text ?? ''\n\n const ownerRuns = (v.ownerText as { runs?: Array<{\n text: string\n navigationEndpoint?: { browseEndpoint?: { browseId?: string; canonicalBaseUrl?: string } }\n }> } | undefined)?.runs ?? []\n const channelName = ownerRuns[0]?.text ?? ''\n const channelId = ownerRuns[0]?.navigationEndpoint?.browseEndpoint?.browseId ?? null\n const channelHandle = ownerRuns[0]?.navigationEndpoint?.browseEndpoint?.canonicalBaseUrl ?? null\n\n const lengthText = v.lengthText as { simpleText?: string } | undefined\n const viewCount = v.viewCountText as { simpleText?: string } | undefined\n const publishedAt = v.publishedTimeText as { simpleText?: string } | undefined\n\n const snippetRuns = (v.detailedMetadataSnippets as Array<{ snippetText?: { runs?: Array<{ text: string }> } }> | undefined)\n ?.[0]?.snippetText?.runs?.map((r) => r.text).join('') ?? null\n\n const thumbs = (v.thumbnail as { thumbnails?: Array<{ url: string }> } | undefined)?.thumbnails ?? []\n const thumbnailUrl = thumbs[thumbs.length - 1]?.url ?? null\n\n return {\n videoId,\n url: `https://www.youtube.com/watch?v=${videoId}`,\n title,\n channelName,\n channelId,\n channelHandle,\n duration: lengthText?.simpleText ?? null,\n views: viewCount?.simpleText ?? null,\n publishedAt: publishedAt?.simpleText ?? null,\n descriptionSnippet: snippetRuns,\n thumbnailUrl,\n }\n }\n\n private parseLockupViewModel(lockup: Record<string, unknown>): YTVideoResult | null {\n const videoId = lockup.contentId as string | undefined\n if (!videoId || (lockup.contentType as string | undefined) !== 'LOCKUP_CONTENT_TYPE_VIDEO') return null\n\n const meta = lockup.metadata as {\n lockupMetadataViewModel?: {\n title?: { content?: string }\n metadata?: {\n contentMetadataViewModel?: {\n metadataRows?: Array<{ metadataParts?: Array<{ text?: { content?: string } }> }>\n }\n }\n }\n } | undefined\n const vmMeta = meta?.lockupMetadataViewModel\n const title = vmMeta?.title?.content ?? ''\n const rows = vmMeta?.metadata?.contentMetadataViewModel?.metadataRows ?? []\n const parts = rows[0]?.metadataParts ?? []\n\n const thumbVM = (lockup.contentImage as Record<string, unknown> | undefined)\n ?.thumbnailViewModel as Record<string, unknown> | undefined\n const overlays = (thumbVM?.overlays as unknown[] | undefined) ?? []\n const badges = (\n (overlays[0] as Record<string, unknown> | undefined)\n ?.thumbnailBottomOverlayViewModel as Record<string, unknown> | undefined\n )?.badges as unknown[] | undefined\n const duration = (\n (badges?.[0] as Record<string, unknown> | undefined)\n ?.thumbnailBadgeViewModel as Record<string, unknown> | undefined\n )?.text as string | null ?? null\n\n return {\n videoId,\n url: `https://www.youtube.com/watch?v=${videoId}`,\n title,\n channelName: '',\n channelId: null,\n channelHandle: null,\n duration,\n views: parts[0]?.text?.content ?? null,\n publishedAt: parts[1]?.text?.content ?? null,\n descriptionSnippet: null,\n thumbnailUrl: null,\n }\n }\n\n private parseChannelRenderer(c: Record<string, unknown>): YTChannelResult | null {\n const channelId = c.channelId as string | undefined\n if (!channelId) return null\n\n const titleText = (c.title as { simpleText?: string } | undefined)?.simpleText ?? ''\n const subsText = (c.subscriberCountText as { simpleText?: string } | undefined)?.simpleText ?? null\n const descText = (c.descriptionSnippet as { runs?: Array<{ text: string }> } | undefined)\n ?.runs?.map((r) => r.text).join('') ?? null\n const canonical = (c.navigationEndpoint as { browseEndpoint?: { canonicalBaseUrl?: string } } | undefined)\n ?.browseEndpoint?.canonicalBaseUrl ?? null\n\n return {\n channelId,\n channelHandle: canonical,\n title: titleText,\n subscriberCount: subsText,\n description: descText,\n channelUrl: `https://www.youtube.com${canonical ?? '/channel/' + channelId}`,\n }\n }\n\n private extractContinuationToken(items: unknown[]): string | null {\n for (const item of items) {\n const rec = item as Record<string, unknown>\n const cont = rec.continuationItemRenderer as Record<string, unknown> | undefined\n if (!cont) continue\n const ep = cont.continuationEndpoint as Record<string, unknown> | undefined\n const cmd = ep?.continuationCommand as Record<string, unknown> | undefined\n if (cmd?.token) return cmd.token as string\n }\n return null\n }\n\n async extractSearchResults(page: Page): Promise<{\n videos: YTVideoResult[]\n channels: YTChannelResult[]\n continuationToken: string | null\n }> {\n const data = await this.waitForInitialData(page)\n const d = data as Record<string, unknown>\n\n const slr = (\n (d.contents as Record<string, unknown> | undefined)\n ?.twoColumnSearchResultsRenderer as Record<string, unknown> | undefined\n )?.primaryContents\n ? (\n (\n (d.contents as Record<string, unknown>)\n .twoColumnSearchResultsRenderer as Record<string, unknown>\n ).primaryContents as Record<string, unknown>\n ).sectionListRenderer as Record<string, unknown> | undefined\n : undefined\n\n const sections: unknown[] = (slr?.contents as unknown[] | undefined) ?? []\n const videos: YTVideoResult[] = []\n const channels: YTChannelResult[] = []\n\n for (const section of sections) {\n const contents: unknown[] = (\n (section as Record<string, unknown>).itemSectionRenderer as Record<string, unknown> | undefined\n )?.contents as unknown[] ?? []\n for (const item of contents) {\n const rec = item as Record<string, unknown>\n if (rec.videoRenderer) {\n const v = this.parseVideoRenderer(rec.videoRenderer as Record<string, unknown>)\n if (v) videos.push(v)\n }\n if (rec.channelRenderer) {\n const c = this.parseChannelRenderer(rec.channelRenderer as Record<string, unknown>)\n if (c) channels.push(c)\n }\n }\n }\n\n return { videos, channels, continuationToken: this.extractContinuationToken(sections) }\n }\n\n async extractChannelVideos(page: Page): Promise<{\n videos: YTVideoResult[]\n channelMeta: YTChannelMeta | null\n continuationToken: string | null\n }> {\n const data = await this.waitForInitialData(page)\n const d = data as Record<string, unknown>\n\n const channelMetaRaw = (\n (d.metadata as Record<string, unknown> | undefined)?.channelMetadataRenderer\n ) as Record<string, unknown> | undefined\n\n const channelMeta: YTChannelMeta | null = channelMetaRaw\n ? {\n externalId: (channelMetaRaw.externalId as string | undefined) ?? '',\n title: (channelMetaRaw.title as string | undefined) ?? '',\n description: (channelMetaRaw.description as string | undefined) ?? null,\n channelUrl: (channelMetaRaw.channelUrl as string | undefined) ?? '',\n vanityChannelUrl: (channelMetaRaw.vanityChannelUrl as string | undefined) ?? null,\n rssUrl: (channelMetaRaw.rssUrl as string | undefined) ?? null,\n }\n : null\n\n const tabs: unknown[] = (\n (d.contents as Record<string, unknown> | undefined)\n ?.twoColumnBrowseResultsRenderer as Record<string, unknown> | undefined\n )?.tabs as unknown[] ?? []\n\n const videosTab = tabs.find(\n (t) => ((t as Record<string, unknown>).tabRenderer as Record<string, unknown> | undefined)?.title === 'Videos',\n ) as Record<string, unknown> | undefined\n\n const richGrid = (\n (videosTab?.tabRenderer as Record<string, unknown> | undefined)\n ?.content as Record<string, unknown> | undefined\n )?.richGridRenderer as Record<string, unknown> | undefined\n\n const gridContents: unknown[] = (richGrid?.contents as unknown[] | undefined) ?? []\n\n const videos: YTVideoResult[] = []\n for (const item of gridContents) {\n const rec = item as Record<string, unknown>\n const inner = (rec.richItemRenderer as Record<string, unknown> | undefined)\n ?.content as Record<string, unknown> | undefined\n if (inner?.lockupViewModel) {\n const v = this.parseLockupViewModel(inner.lockupViewModel as Record<string, unknown>)\n if (v) videos.push(v)\n }\n }\n\n return { videos, channelMeta, continuationToken: this.extractContinuationToken(gridContents) }\n }\n\n private async fetchContinuation(\n page: Page,\n endpoint: 'search' | 'browse',\n contToken: string,\n context: YTInnerTubeContext,\n ): Promise<{ items: unknown[]; nextToken: string | null }> {\n const result = await page.evaluate(\n async ({ ep, token, ctx }) => {\n const body = {\n continuation: token,\n context: {\n client: {\n clientName: ctx.clientName,\n clientVersion: ctx.clientVersion,\n hl: ctx.hl,\n gl: ctx.gl,\n visitorData: ctx.visitorData,\n },\n },\n }\n const resp = await fetch(`/youtubei/v1/${ep}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-YouTube-Client-Name': '1',\n 'X-YouTube-Client-Version': ctx.clientVersion,\n },\n body: JSON.stringify(body),\n })\n return resp.json()\n },\n { ep: endpoint, token: contToken, ctx: context },\n ) as Record<string, unknown>\n\n const actionKey = endpoint === 'search' ? 'onResponseReceivedCommands' : 'onResponseReceivedActions'\n const actions = (result[actionKey] as unknown[] | undefined) ?? []\n const firstAction = (actions[0] as Record<string, unknown> | undefined) ?? {}\n const appendAction = firstAction.appendContinuationItemsAction as Record<string, unknown> | undefined\n const items: unknown[] = (appendAction?.continuationItems as unknown[] | undefined) ?? []\n\n return { items, nextToken: this.extractContinuationToken(items) }\n }\n\n private parseSearchContinuationItems(items: unknown[]): { videos: YTVideoResult[]; channels: YTChannelResult[] } {\n const videos: YTVideoResult[] = []\n const channels: YTChannelResult[] = []\n for (const item of items) {\n const rec = item as Record<string, unknown>\n const contents: unknown[] = (\n rec.itemSectionRenderer as Record<string, unknown> | undefined\n )?.contents as unknown[] ?? [rec]\n for (const c of contents) {\n const ci = c as Record<string, unknown>\n if (ci.videoRenderer) {\n const v = this.parseVideoRenderer(ci.videoRenderer as Record<string, unknown>)\n if (v) videos.push(v)\n }\n if (ci.channelRenderer) {\n const ch = this.parseChannelRenderer(ci.channelRenderer as Record<string, unknown>)\n if (ch) channels.push(ch)\n }\n }\n }\n return { videos, channels }\n }\n\n private parseChannelContinuationItems(items: unknown[]): YTVideoResult[] {\n const videos: YTVideoResult[] = []\n for (const item of items) {\n const rec = item as Record<string, unknown>\n const inner = (rec.richItemRenderer as Record<string, unknown> | undefined)\n ?.content as Record<string, unknown> | undefined\n if (inner?.videoRenderer) {\n const v = this.parseVideoRenderer(inner.videoRenderer as Record<string, unknown>)\n if (v) videos.push(v)\n }\n if (inner?.lockupViewModel) {\n const v = this.parseLockupViewModel(inner.lockupViewModel as Record<string, unknown>)\n if (v) videos.push(v)\n }\n }\n return videos\n }\n\n async extract(options: YouTubeHarvestOptions): Promise<YTHarvestResult> {\n const startMs = Date.now()\n const config: DriverConfig = {\n headless: options.headless,\n kernelApiKey: options.kernelApiKey,\n viewport: { width: 1280, height: 800 },\n locale: 'en-US',\n }\n\n try {\n await this.driver.launch(config)\n const page = this.driver.getPage() as Page\n\n const allVideos: YTVideoResult[] = []\n const allChannels: YTChannelResult[] = []\n let channelMeta: YTChannelMeta | null = null\n\n if (options.mode === 'search') {\n const url = `https://www.youtube.com/results?search_query=${encodeURIComponent(options.query!)}`\n await this.driver.navigateTo(url)\n\n const first = await this.extractSearchResults(page)\n allVideos.push(...first.videos)\n allChannels.push(...first.channels)\n this.reporter.onVideos(first.videos)\n\n let contToken: string | null = first.continuationToken\n const ctx = await this.getInnerTubeContext(page)\n\n while (contToken && allVideos.length < options.maxVideos) {\n const { items, nextToken } = await this.fetchContinuation(page, 'search', contToken, ctx)\n const parsed = this.parseSearchContinuationItems(items)\n allVideos.push(...parsed.videos)\n allChannels.push(...parsed.channels)\n this.reporter.onVideos(parsed.videos)\n contToken = nextToken\n await page.waitForTimeout(500)\n }\n } else {\n await this.driver.navigateToChannel(options.channelHandle!)\n\n const first = await this.extractChannelVideos(page)\n allVideos.push(...first.videos)\n channelMeta = first.channelMeta\n this.reporter.onVideos(first.videos)\n\n let contToken: string | null = first.continuationToken\n const ctx = await this.getInnerTubeContext(page)\n\n while (contToken && allVideos.length < options.maxVideos) {\n await page.waitForTimeout(750)\n const { items, nextToken } = await this.fetchContinuation(page, 'browse', contToken, ctx)\n const parsed = this.parseChannelContinuationItems(items)\n allVideos.push(...parsed)\n this.reporter.onVideos(parsed)\n contToken = nextToken\n }\n }\n\n const capped = allVideos.slice(0, options.maxVideos)\n const stats: YTHarvestStats = {\n mode: options.mode,\n target: options.mode === 'search' ? options.query! : options.channelHandle!,\n totalVideos: capped.length,\n totalDownloads: 0,\n durationMs: Date.now() - startMs,\n }\n this.reporter.onComplete(stats)\n\n return {\n mode: options.mode,\n target: stats.target,\n extractedAt: new Date().toISOString(),\n channelMeta,\n videos: capped,\n channels: allChannels,\n downloads: [],\n stats,\n }\n } catch (err) {\n this.reporter.onError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n await this.driver.close()\n }\n }\n}\n","import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { mkdir } from 'node:fs/promises'\nimport path from 'node:path'\nimport type { YTVideoResult, YTDownloadResult } from './types.js'\nimport type { IYTProgressReporter } from './YTProgressReporter.js'\n\nconst execFileAsync = promisify(execFile)\n\nexport class MP3Downloader {\n constructor(\n private readonly outputDir: string,\n private readonly reporter: IYTProgressReporter,\n private readonly concurrency = 3,\n ) {}\n\n private async ensureYtDlp(): Promise<void> {\n try {\n await execFileAsync('yt-dlp', ['--version'])\n } catch {\n throw new Error(\n 'yt-dlp not found. Install it: brew install yt-dlp or pip install yt-dlp',\n )\n }\n }\n\n async downloadOne(video: YTVideoResult): Promise<YTDownloadResult> {\n const url = video.url\n const outputTemplate = path.join(this.outputDir, '%(title)s.%(ext)s')\n\n try {\n const { stdout } = await execFileAsync('yt-dlp', [\n '--extract-audio',\n '--audio-format', 'mp3',\n '--audio-quality', '0',\n '--embed-thumbnail',\n '--add-metadata',\n '--output', outputTemplate,\n '--print', 'after_move:filepath',\n '--no-playlist',\n '--no-warnings',\n url,\n ])\n\n const outputPath = stdout.trim().split('\\n').pop() ?? null\n this.reporter.onDownload(video.videoId, true, outputPath)\n return { videoId: video.videoId, title: video.title, outputPath, success: true, error: null }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.reporter.onDownload(video.videoId, false, null)\n return { videoId: video.videoId, title: video.title, outputPath: null, success: false, error: message }\n }\n }\n\n async downloadAll(videos: YTVideoResult[]): Promise<YTDownloadResult[]> {\n await mkdir(this.outputDir, { recursive: true })\n await this.ensureYtDlp()\n\n const results: YTDownloadResult[] = []\n for (let i = 0; i < videos.length; i += this.concurrency) {\n const batch = videos.slice(i, i + this.concurrency)\n const batchResults = await Promise.all(batch.map((v) => this.downloadOne(v)))\n results.push(...batchResults)\n }\n return results\n }\n}\n","import type { YTVideoResult, YTHarvestStats } from './types.js'\n\nexport interface IYTProgressReporter {\n onVideos(videos: YTVideoResult[]): void\n onDownload(videoId: string, success: boolean, path: string | null): void\n onComplete(stats: YTHarvestStats): void\n onError(err: Error): void\n}\n\nexport class YTProgressReporter implements IYTProgressReporter {\n onVideos(videos: YTVideoResult[]): void {\n for (const v of videos) {\n process.stdout.write(\n JSON.stringify({ event: 'video', videoId: v.videoId, title: v.title, channel: v.channelName, views: v.views, publishedAt: v.publishedAt, url: v.url }) + '\\n',\n )\n }\n }\n\n onDownload(videoId: string, success: boolean, path: string | null): void {\n process.stdout.write(\n JSON.stringify({ event: 'download', videoId, success, path }) + '\\n',\n )\n }\n\n onComplete(stats: YTHarvestStats): void {\n process.stdout.write(JSON.stringify({ event: 'complete', ...stats }) + '\\n')\n }\n\n onError(err: Error): void {\n process.stderr.write(JSON.stringify({ event: 'error', type: err.constructor.name, message: err.message }) + '\\n')\n }\n}\n","import { BrowserDriver } from '../driver/BrowserDriver.js'\nimport { fal } from '@fal-ai/client'\n\nexport interface CaptionChunk {\n timestamp: [number, number]\n text: string\n}\n\nexport interface CaptionResult {\n videoId: string\n text: string\n chunks: CaptionChunk[]\n durationMs: number\n method: 'youtube-transcript' | 'browser-innertube' | 'browser-whisper'\n}\n\nasync function fetchViaYoutubeTranscript(videoId: string): Promise<CaptionResult | null> {\n try {\n const { YoutubeTranscript } = await import('youtube-transcript')\n const entries = await YoutubeTranscript.fetchTranscript(videoId, { lang: 'en' })\n .catch(() => YoutubeTranscript.fetchTranscript(videoId))\n if (!entries?.length) return null\n const chunks: CaptionChunk[] = entries.map((e) => ({\n timestamp: [e.offset / 1000, (e.offset + e.duration) / 1000] as [number, number],\n text: e.text.trim(),\n }))\n const text = chunks.map((c) => c.text).join(' ')\n const last = chunks[chunks.length - 1]\n return { videoId, text, chunks, durationMs: last ? last.timestamp[1] * 1000 : 0, method: 'youtube-transcript' }\n } catch {\n return null\n }\n}\n\nexport function parseTimedtextJson3(jsonText: string): Array<{ start: number; dur: number; text: string }> {\n let data: { events?: Array<{ tStartMs?: number; dDurationMs?: number; segs?: Array<{ utf8?: string }> }> }\n try {\n data = JSON.parse(jsonText)\n } catch {\n return []\n }\n const results: Array<{ start: number; dur: number; text: string }> = []\n for (const ev of data.events ?? []) {\n if (!ev.segs) continue\n const text = ev.segs.map((s) => s.utf8 ?? '').join('').replace(/\\s+/g, ' ').trim()\n if (!text) continue\n const start = typeof ev.tStartMs === 'number' ? ev.tStartMs : NaN\n const dur = typeof ev.dDurationMs === 'number' ? ev.dDurationMs : 0\n if (!Number.isFinite(start)) continue\n results.push({ start, dur, text })\n }\n return results\n}\n\nfunction parseTimedtextXml(xml: string): Array<{ start: number; dur: number; text: string }> {\n const results: Array<{ start: number; dur: number; text: string }> = []\n\n const pRegex = /<p\\s+t=\"(\\d+)\"\\s+d=\"(\\d+)\"[^>]*>([\\s\\S]*?)<\\/p>/g\n let m: RegExpExecArray | null\n while ((m = pRegex.exec(xml)) !== null) {\n const startMs = parseInt(m[1], 10)\n const durMs = parseInt(m[2], 10)\n const inner = m[3]\n let text = inner.replace(/<[^>]+>/g, '').trim()\n if (!text) {\n const sMatch = /<s[^>]*>([^<]*)<\\/s>/g\n let sm: RegExpExecArray | null\n const parts: string[] = []\n while ((sm = sMatch.exec(inner)) !== null) parts.push(sm[1])\n text = parts.join('').trim()\n }\n text = text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '\"').replace(/'/g, \"'\")\n if (text) results.push({ start: startMs, dur: durMs, text })\n }\n if (results.length > 0) return results\n\n const classicRegex = /<text start=\"([^\"]*)\" dur=\"([^\"]*)\">([^<]*)<\\/text>/g\n while ((m = classicRegex.exec(xml)) !== null) {\n const text = m[3].replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '\"').replace(/'/g, \"'\")\n results.push({ start: parseFloat(m[1]) * 1000, dur: parseFloat(m[2]) * 1000, text })\n }\n return results\n}\n\nasync function fetchViaKernelInnertube(videoId: string): Promise<CaptionResult | null> {\n const kernelApiKey = process.env.KERNEL_API_KEY\n if (!kernelApiKey) return null\n\n const driver = new BrowserDriver()\n const start = Date.now()\n\n try {\n await driver.launch({ kernelApiKey, headless: true, viewport: { width: 1280, height: 800 }, locale: 'en-US' })\n const page = driver.getPage()\n\n await driver.navigateTo(`https://www.youtube.com/watch?v=${videoId}`)\n await page.waitForFunction(\n () => !!(window as unknown as { ytcfg?: unknown }).ytcfg,\n { timeout: 20_000 },\n )\n\n const xml = await page.evaluate(async (vid: string) => {\n const ANDROID_VERSION = '20.10.38'\n const resp = await fetch('/youtubei/v1/player?prettyPrint=false', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': `com.google.android.youtube/${ANDROID_VERSION} (Linux; U; Android 14)`,\n },\n body: JSON.stringify({\n context: { client: { clientName: 'ANDROID', clientVersion: ANDROID_VERSION } },\n videoId: vid,\n }),\n })\n if (!resp.ok) return null\n const data = await resp.json() as {\n captions?: { playerCaptionsTracklistRenderer?: { captionTracks?: Array<{ languageCode: string; baseUrl: string }> } }\n }\n const tracks = data?.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? []\n if (tracks.length === 0) return null\n const track = tracks.find((t) => t.languageCode === 'en') ?? tracks[0]\n const sep = track.baseUrl.includes('?') ? '&' : '?'\n const xmlResp = await fetch(`${track.baseUrl}${sep}fmt=json3`)\n return xmlResp.ok ? xmlResp.text() : null\n }, videoId)\n\n if (!xml) return null\n\n let entries = parseTimedtextJson3(xml)\n if (entries.length === 0) entries = parseTimedtextXml(xml)\n if (entries.length === 0) return null\n\n const chunks: CaptionChunk[] = entries.map((e) => ({\n timestamp: [e.start / 1000, (e.start + e.dur) / 1000] as [number, number],\n text: e.text,\n }))\n const text = chunks.map((c) => c.text).join(' ')\n const last = chunks[chunks.length - 1]\n\n return { videoId, text, chunks, durationMs: Date.now() - start, method: 'browser-innertube' }\n } catch {\n return null\n } finally {\n await driver.close()\n }\n}\n\nconst WHISPER_RECORD_SECONDS = 90\n\nasync function captureAudioViaMediaRecorder(\n page: import('playwright-core').Page,\n): Promise<{ bytes: Buffer; mimeType: string } | null> {\n type RecordResult = { ok: boolean; base64?: string; size?: number; mimeType?: string; error?: string }\n const result: RecordResult = await page.evaluate((recordSecs: number): Promise<RecordResult> => {\n return new Promise((resolve) => {\n const video = document.querySelector('video') as HTMLVideoElement\n if (!video) { resolve({ ok: false, error: 'no video element' }); return }\n video.muted = false\n video.volume = 1.0\n try {\n const ctx = new AudioContext()\n const src = ctx.createMediaElementSource(video)\n const dest = ctx.createMediaStreamDestination()\n src.connect(dest)\n src.connect(ctx.destination)\n const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm')\n ? 'audio/webm'\n : 'audio/ogg'\n const recorder = new MediaRecorder(dest.stream, { mimeType, audioBitsPerSecond: 48000 })\n const chunks: BlobPart[] = []\n recorder.ondataavailable = (e) => { if (e.data.size > 0) chunks.push(e.data) }\n recorder.onstop = async () => {\n const blob = new Blob(chunks, { type: mimeType })\n const ab = await blob.arrayBuffer()\n const arr = new Uint8Array(ab)\n let binary = ''\n const chunkSize = 8192\n for (let i = 0; i < arr.length; i += chunkSize) {\n binary += String.fromCharCode(...arr.slice(i, Math.min(i + chunkSize, arr.length)))\n }\n resolve({ ok: true, base64: btoa(binary), size: ab.byteLength, mimeType })\n }\n recorder.start(1000)\n video.playbackRate = 2.0\n video.play().catch(() => undefined)\n setTimeout(() => { recorder.stop(); ctx.close() }, recordSecs * 1000)\n } catch (e) {\n resolve({ ok: false, error: String(e) })\n }\n })\n }, WHISPER_RECORD_SECONDS)\n\n if (!result.ok || !result.base64 || !result.mimeType) return null\n return { bytes: Buffer.from(result.base64, 'base64'), mimeType: result.mimeType }\n}\n\nasync function attemptKernelWhisper(\n videoId: string,\n kernelApiKey: string,\n falKey: string,\n start: number,\n): Promise<CaptionResult | null> {\n const driver = new BrowserDriver()\n try {\n await driver.launch({ kernelApiKey, headless: true, viewport: { width: 1280, height: 800 }, locale: 'en-US' })\n const page = driver.getPage()\n\n await driver.navigateTo(`https://www.youtube.com/watch?v=${videoId}`)\n await page.waitForFunction(\n () => !!(window as unknown as { ytcfg?: unknown }).ytcfg,\n { timeout: 20_000 },\n )\n\n const playStatus = await page.evaluate(() => {\n const w = window as unknown as { ytInitialPlayerResponse?: { playabilityStatus?: { status: string } } }\n return w.ytInitialPlayerResponse?.playabilityStatus?.status\n })\n\n if (playStatus !== 'OK') {\n await driver.close()\n return null\n }\n\n const audioInfo = await captureAudioViaMediaRecorder(page)\n await driver.close()\n\n if (!audioInfo || audioInfo.bytes.length < 10000) return null\n\n fal.config({ credentials: falKey })\n const ext = audioInfo.mimeType.includes('ogg') ? 'ogg' : 'webm'\n const audioFile = new File([new Uint8Array(audioInfo.bytes)], `audio.${ext}`, { type: audioInfo.mimeType })\n const uploadedUrl = await fal.storage.upload(audioFile)\n\n const result = await fal.subscribe('fal-ai/wizper', {\n input: { audio_url: uploadedUrl, task: 'transcribe', language: 'en' },\n logs: false,\n pollInterval: 3000,\n })\n\n const data = result.data as unknown as {\n text?: string\n chunks?: Array<{ timestamp: [number, number]; text: string }>\n }\n const chunks: CaptionChunk[] = (data.chunks ?? []).map((c) => ({\n timestamp: c.timestamp,\n text: c.text.trim(),\n }))\n const text = data.text ?? chunks.map((c) => c.text).join(' ')\n if (!text) return null\n\n return { videoId, text, chunks, durationMs: Date.now() - start, method: 'browser-whisper' }\n } catch {\n await driver.close()\n return null\n }\n}\n\nasync function fetchViaKernelWhisper(videoId: string): Promise<CaptionResult | null> {\n const kernelApiKey = process.env.KERNEL_API_KEY\n const falKey = process.env.FAL_KEY\n if (!kernelApiKey || !falKey) return null\n\n const start = Date.now()\n for (let attempt = 0; attempt < 4; attempt++) {\n if (attempt > 0) await new Promise((r) => setTimeout(r, 2000 * attempt))\n const result = await attemptKernelWhisper(videoId, kernelApiKey, falKey, start)\n if (result) return result\n }\n return null\n}\n\nexport function finalizeCaptions(result: CaptionResult): CaptionResult {\n const chunks = result.chunks.map((c) => {\n const startOk = Number.isFinite(c.timestamp[0])\n const endOk = Number.isFinite(c.timestamp[1])\n return {\n ...c,\n timestamp: [startOk ? c.timestamp[0] : 0, endOk ? c.timestamp[1] : (startOk ? c.timestamp[0] : 0)] as [number, number],\n }\n })\n const lastEnd = chunks.length ? chunks[chunks.length - 1].timestamp[1] : 0\n const words = result.text.split(/\\s+/).filter(Boolean).length\n const wordEstSec = words > 0 ? Math.ceil(words / 2.5) : 0\n const videoDurationMs = lastEnd > 1 ? Math.round(lastEnd * 1000) : wordEstSec * 1000\n return { ...result, chunks, durationMs: videoDurationMs }\n}\n\nexport async function fetchCaptions(videoId: string): Promise<CaptionResult> {\n const primary = await fetchViaYoutubeTranscript(videoId)\n if (primary) return finalizeCaptions(primary)\n\n const kernel = await fetchViaKernelInnertube(videoId)\n if (kernel) return finalizeCaptions(kernel)\n\n const whisper = await fetchViaKernelWhisper(videoId)\n if (whisper) return finalizeCaptions(whisper)\n\n throw new Error(`No captions available for ${videoId} — all three tiers failed`)\n}\n","import { createMiddleware } from 'hono/factory'\nimport { getUserByApiKey } from './db.js'\nimport type { User } from './db.js'\n\nexport type ApiKeyEnv = { Variables: { user: User } }\n\nexport function createApiKeyAuth<T extends ApiKeyEnv = ApiKeyEnv>() {\n return createMiddleware<T>(async (c, next) => {\n const key = c.req.header('x-api-key')\n if (!key) return c.json({ error: 'Missing API key' }, 401)\n const user = await getUserByApiKey(key)\n if (!user) return c.json({ error: 'Invalid or inactive API key' }, 401)\n c.set('user', user as T['Variables']['user'])\n return next()\n })\n}\n","import { z } from 'zod'\n\nexport const HarvestBodySchema = z.object({\n query: z.string().min(1, 'query is required'),\n location: z.string().optional(),\n depth: z.number().int().min(1).max(4).optional(),\n maxQuestions: z.number().int().min(1).optional(),\n callback_url: z.string().url().optional(),\n gl: z.string().optional(),\n hl: z.string().optional(),\n device: z.enum(['desktop', 'mobile']).optional(),\n proxyMode: z.enum(['location', 'configured', 'none']).optional(),\n proxyZip: z.string().regex(/^\\d{5}$/).optional(),\n debug: z.boolean().optional(),\n serpOnly: z.boolean().optional(),\n pages: z.number().int().min(1).max(2).optional(),\n})\n\nexport const ExtractUrlBodySchema = z.object({\n url: z.string().min(1, 'url is required'),\n screenshot: z.boolean().optional(),\n screenshotDevice: z.enum(['desktop', 'mobile']).optional(),\n extractBranding: z.boolean().optional(),\n downloadMedia: z.boolean().optional(),\n mediaTypes: z.array(z.enum(['image', 'video', 'audio'])).optional(),\n allowLocal: z.boolean().optional(),\n})\n\nexport const MapUrlsBodySchema = z.object({\n url: z.string().min(1, 'url is required'),\n maxUrls: z.number().int().min(1).max(2000).optional(),\n concurrency: z.number().int().min(1).max(20).optional(),\n browserFallback: z.boolean().optional(),\n kernelFallback: z.boolean().optional(),\n})\n\nexport const ExtractSiteBodySchema = z.object({\n url: z.string().min(1, 'url is required'),\n maxPages: z.number().int().min(1).max(200).optional(),\n browserFallback: z.boolean().optional(),\n kernelFallback: z.boolean().optional(),\n})\n\nexport const YoutubeHarvestBodySchema = z.object({\n mode: z.enum(['search', 'channel']),\n query: z.string().optional(),\n channelHandle: z.string().optional(),\n maxVideos: z.number().int().min(1).max(500).optional(),\n})\n\nexport const YoutubeTranscribeBodySchema = z.object({\n videoId: z.string().min(1, 'videoId is required'),\n})\n\nexport type HarvestBody = z.infer<typeof HarvestBodySchema>\nexport type ExtractUrlBody = z.infer<typeof ExtractUrlBodySchema>\nexport type MapUrlsBody = z.infer<typeof MapUrlsBodySchema>\nexport type ExtractSiteBody = z.infer<typeof ExtractSiteBodySchema>\nexport type YoutubeHarvestBody = z.infer<typeof YoutubeHarvestBodySchema>\nexport type YoutubeTranscribeBody = z.infer<typeof YoutubeTranscribeBodySchema>\n","import { Hono } from 'hono'\nimport { z } from 'zod'\nimport { captureScreenshot, type ScreenshotDevice } from '../lib/screenshot.js'\nimport { createApiKeyAuth, type ApiKeyEnv } from './api-auth.js'\nimport { validatePublicHttpUrl } from './url-utils.js'\n\ntype Env = ApiKeyEnv\n\nexport const ScreenshotBodySchema = z.object({\n url: z.string().trim().min(1, 'url is required'),\n device: z.string().trim().optional(),\n allowLocal: z.boolean().optional(),\n})\n\nexport type ScreenshotBody = z.infer<typeof ScreenshotBodySchema>\n\nexport const screenshotApp = new Hono<Env>()\n\nscreenshotApp.use('*', createApiKeyAuth<Env>())\n\nscreenshotApp.post('/', async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = ScreenshotBodySchema.safeParse(raw)\n if (!parsed.success) {\n return c.json({ error_code: 'invalid_request' as const, message: parsed.error.issues[0]?.message ?? 'Invalid request' }, 400)\n }\n const body: ScreenshotBody = parsed.data\n\n const urlCheck = await validatePublicHttpUrl(body.url, { field: 'url', requireHttps: false })\n if (urlCheck.error) {\n if (!body.allowLocal) {\n return c.json({ error_code: 'invalid_request' as const, message: urlCheck.error }, 400)\n }\n let parsedFallback: URL\n try {\n parsedFallback = new URL(body.url.trim())\n } catch {\n return c.json({ error: 'Invalid url' }, 400)\n }\n if (parsedFallback.protocol !== 'http:' && parsedFallback.protocol !== 'https:') {\n return c.json({ error: 'url must use http or https' }, 400)\n }\n const device: ScreenshotDevice = body.device === 'mobile' ? 'mobile' : 'desktop'\n try {\n const buf = await captureScreenshot(parsedFallback.href, process.env.KERNEL_API_KEY?.trim(), device)\n return new Response(new Uint8Array(buf), {\n status: 200,\n headers: {\n 'Content-Type': 'image/png',\n 'Content-Length': String(buf.length),\n 'Cache-Control': 'no-store',\n },\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n return c.json({ error: msg }, 502)\n }\n }\n\n const device: ScreenshotDevice = body.device === 'mobile' ? 'mobile' : 'desktop'\n\n try {\n const buf = await captureScreenshot(urlCheck.parsed!.href, process.env.KERNEL_API_KEY?.trim(), device)\n return new Response(new Uint8Array(buf), {\n status: 200,\n headers: {\n 'Content-Type': 'image/png',\n 'Content-Length': String(buf.length),\n 'Cache-Control': 'no-store',\n },\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n return c.json({ error: msg }, 502)\n }\n})\n","import { Hono } from 'hono'\nimport { z } from 'zod'\nimport { debitMc, creditMc, logRequestEvent } from './db.js'\nimport { MC_COSTS, insufficientBalanceResponse, LedgerOperation } from './rates.js'\nimport { BrowserDriver } from '../driver/BrowserDriver.js'\nimport { FacebookAdExtractor } from '../extractor/FacebookAdExtractor.js'\nimport { fal } from '@fal-ai/client'\nimport { createApiKeyAuth, type ApiKeyEnv } from './api-auth.js'\nimport { validatePublicHttpUrl } from './url-utils.js'\n\ntype FbEnv = ApiKeyEnv\n\nexport const FacebookAdBodySchema = z.object({\n url: z.string().trim().optional(),\n libraryId: z.string().trim().optional(),\n openModal: z.boolean().optional(),\n}).refine(d => !!d.url || !!d.libraryId, { message: 'url or libraryId is required' })\n\nexport type FacebookAdBody = z.infer<typeof FacebookAdBodySchema>\n\nexport const FacebookPageIntelBodySchema = z.object({\n pageId: z.string().trim().optional(),\n query: z.string().trim().optional(),\n libraryId: z.string().trim().optional(),\n maxAds: z.number().int().min(1).max(200).optional(),\n country: z.string().trim().toUpperCase().optional(),\n}).refine(d => !!d.pageId || !!d.query || !!d.libraryId, {\n message: 'pageId, libraryId, or query is required',\n})\n\nexport type FacebookPageIntelBody = z.infer<typeof FacebookPageIntelBodySchema>\n\nexport const FacebookTranscribeBodySchema = z.object({\n videoUrl: z.string().trim().min(1, 'videoUrl is required'),\n})\n\nexport type FacebookTranscribeBody = z.infer<typeof FacebookTranscribeBodySchema>\n\nexport const FacebookSearchBodySchema = z.object({\n query: z.string().trim().min(1, 'query is required'),\n country: z.string().trim().toUpperCase().optional(),\n maxResults: z.number().int().min(1).max(20).optional(),\n})\n\nexport type FacebookSearchBody = z.infer<typeof FacebookSearchBodySchema>\n\nexport const FacebookMediaBodySchema = z.object({\n url: z.string().trim().min(1, 'url is required'),\n filename: z.string().trim().optional(),\n})\n\nexport type FacebookMediaBody = z.infer<typeof FacebookMediaBodySchema>\n\nfunction invalidRequest(message: string) {\n return { error_code: 'invalid_request' as const, message }\n}\n\nasync function detectSoftBlock(driver: BrowserDriver): Promise<boolean> {\n const page = driver.getPage() as import('playwright').Page\n const bodyText: string = await page.evaluate(() => document.body?.innerText ?? '').catch(() => '')\n return bodyText.length < 200 || /Log in|log in|Create new account|You must log in/.test(bodyText)\n}\n\nfunction buildPageIntelUrl(body: FacebookPageIntelBody, country: string): string {\n if (body.libraryId?.trim()) return `https://www.facebook.com/ads/library/?id=${FacebookAdExtractor.resolveLibraryId(body.libraryId.trim()) ?? body.libraryId.trim()}`\n if (body.pageId?.trim()) return `https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=${country}&is_targeted_country=false&media_type=all&search_type=page&view_all_page_id=${body.pageId.trim()}`\n return `https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=${country}&q=${encodeURIComponent(body.query!.trim())}&search_type=keyword_unordered`\n}\n\nfunction kernelLaunchOpts() {\n return { headless: true as const, kernelApiKey: process.env.KERNEL_API_KEY?.trim(), kernelProxyId: process.env.KERNEL_PROXY_ID?.trim(), viewport: { width: 1280, height: 900 }, locale: 'en-US' }\n}\n\nexport const facebookAdApp = new Hono<FbEnv>()\n\nfacebookAdApp.post('/ad', createApiKeyAuth<FbEnv>(), async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = FacebookAdBodySchema.safeParse(raw)\n if (!parsed.success) {\n return c.json(invalidRequest(parsed.error.issues[0]?.message ?? 'Invalid request'), 400)\n }\n const body: FacebookAdBody = parsed.data\n\n const raw2 = body.url?.trim() ?? body.libraryId?.trim() ?? ''\n const libraryId = FacebookAdExtractor.resolveLibraryId(raw2)\n if (!libraryId) return c.json({ error: 'Could not resolve a valid Facebook Ad Library ID from the provided input' }, 400)\n\n const fbUser = c.get('user')\n const { ok: adOk, balance_mc: adBal } = await debitMc(fbUser.id, MC_COSTS.fb_ad, LedgerOperation.FB_AD, raw2)\n if (!adOk) return c.json(insufficientBalanceResponse(adBal, MC_COSTS.fb_ad), 402)\n\n const driver = new BrowserDriver()\n try {\n await driver.launch(kernelLaunchOpts())\n const extractor = new FacebookAdExtractor(driver)\n const result = await extractor.extract(libraryId, { openModal: body.openModal !== false })\n await logRequestEvent({\n userId: fbUser.id,\n source: 'facebook_ad',\n status: 'done',\n query: raw2,\n resultCount: Array.isArray((result as any).variants) ? (result as any).variants.length : null,\n result,\n })\n return c.json(result)\n } catch (err) {\n await creditMc(fbUser.id, MC_COSTS.fb_ad, LedgerOperation.FB_AD_REFUND, 'failed call')\n const msg = err instanceof Error ? err.message : String(err)\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_ad', status: 'failed', query: raw2, error: msg })\n if (msg.toLowerCase().includes('blocked') || msg.toLowerCase().includes('captcha')) {\n return c.json({ error: msg }, 503)\n }\n return c.json({ error: msg }, 500)\n } finally {\n await driver.close()\n }\n})\n\nfacebookAdApp.post('/page-intel', createApiKeyAuth<FbEnv>(), async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = FacebookPageIntelBodySchema.safeParse(raw)\n if (!parsed.success) {\n return c.json(invalidRequest(parsed.error.issues[0]?.message ?? 'Invalid request'), 400)\n }\n const body: FacebookPageIntelBody = parsed.data\n\n const maxAds = Math.min(200, Math.max(1, body.maxAds ?? 50))\n const country = body.country?.trim().toUpperCase() ?? 'US'\n const listingUrl = buildPageIntelUrl(body, country)\n const fbUser = c.get('user')\n const { ok: fbOk, balance_mc: fbBal } = await debitMc(fbUser.id, MC_COSTS.fb_ad, LedgerOperation.FB_AD, body.pageId ?? body.query ?? body.libraryId ?? '')\n if (!fbOk) return c.json(insufficientBalanceResponse(fbBal, MC_COSTS.fb_ad), 402)\n\n const driver = new BrowserDriver()\n let refunded = false\n try {\n await driver.launch(kernelLaunchOpts())\n await driver.navigateTo(listingUrl)\n const extractor = new FacebookAdExtractor(driver)\n const result = await extractor.extractPageIntel(listingUrl, maxAds)\n if (result.ads.length === 0 && await detectSoftBlock(driver)) {\n await creditMc(fbUser.id, MC_COSTS.fb_ad, LedgerOperation.FB_AD_REFUND, 'soft-block empty result')\n refunded = true\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_page_intel', status: 'failed', query: body.pageId ?? body.query ?? body.libraryId ?? '', error: 'soft-block: empty result refunded' })\n return c.json({ error: 'soft-block: no ads returned (refunded)' }, 503)\n }\n\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_page_intel', status: 'done', query: body.pageId ?? body.query ?? body.libraryId ?? '', resultCount: result.ads.length, result })\n return c.json(result)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (!refunded) await creditMc(fbUser.id, MC_COSTS.fb_ad, LedgerOperation.FB_AD_REFUND, 'failed call')\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_page_intel', status: 'failed', query: body.pageId ?? body.query ?? body.libraryId ?? '', error: msg })\n if (msg.toLowerCase().includes('blocked') || msg.toLowerCase().includes('captcha')) {\n return c.json({ error: msg }, 503)\n }\n return c.json({ error: msg }, 500)\n } finally {\n await driver.close()\n }\n})\n\nfacebookAdApp.post('/transcribe', createApiKeyAuth<FbEnv>(), async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = FacebookTranscribeBodySchema.safeParse(raw)\n if (!parsed.success) {\n return c.json(invalidRequest(parsed.error.issues[0]?.message ?? 'Invalid request'), 400)\n }\n const body: FacebookTranscribeBody = parsed.data\n\n const urlCheck = await validatePublicHttpUrl(body.videoUrl, { field: 'videoUrl', requireHttps: false })\n if (urlCheck.error) {\n return c.json(invalidRequest(urlCheck.error), 400)\n }\n const videoUrl = urlCheck.parsed!.href\n\n const fbUser = c.get('user')\n const { ok, balance_mc } = await debitMc(fbUser.id, MC_COSTS.fb_transcribe, LedgerOperation.FB_TRANSCRIBE, videoUrl)\n if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.fb_transcribe), 402)\n\n fal.config({ credentials: process.env.FAL_KEY })\n\n try {\n const startMs = Date.now()\n const result = await fal.subscribe('fal-ai/wizper', {\n input: { audio_url: videoUrl, task: 'transcribe', language: 'en' },\n logs: false,\n pollInterval: 3000,\n })\n\n const data = result.data as unknown as {\n text?: string\n chunks?: Array<{ timestamp: [number, number]; text: string }>\n }\n const text = data.text ?? ''\n const chunks = data.chunks ?? []\n const durationMs = Date.now() - startMs\n\n const fmtTs = (s: number) => `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}`\n const lines = ['# Facebook Ad Transcript', '', `*Transcribed in ${(durationMs / 1000).toFixed(1)}s*`, '', '## Full Text', '', text, '']\n if (chunks.length) {\n lines.push('## Timestamped Segments', '')\n for (const ch of chunks) {\n lines.push(`**[${fmtTs(ch.timestamp[0])} → ${fmtTs(ch.timestamp[1])}]** ${ch.text.trim()}`, '')\n }\n }\n\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_transcribe', status: 'done', query: videoUrl, resultCount: chunks.length, result: { text, chunks, durationMs } })\n return c.json({ text, chunks, durationMs, markdown: lines.join('\\n') })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n await creditMc(fbUser.id, MC_COSTS.fb_transcribe, LedgerOperation.FB_TRANSCRIBE_REFUND, 'failed call')\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_transcribe', status: 'failed', query: videoUrl, error: msg })\n return c.json({ error: msg }, 500)\n }\n})\n\nfacebookAdApp.post('/search', createApiKeyAuth<FbEnv>(), async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = FacebookSearchBodySchema.safeParse(raw)\n if (!parsed.success) {\n return c.json(invalidRequest(parsed.error.issues[0]?.message ?? 'Invalid request'), 400)\n }\n const body: FacebookSearchBody = parsed.data\n\n const country = body.country?.trim().toUpperCase() ?? 'US'\n const maxResults = Math.min(20, Math.max(1, body.maxResults ?? 10))\n const searchUrl = `https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=${country}&q=${encodeURIComponent(body.query.trim())}&search_type=keyword_unordered`\n\n const fbUser = c.get('user')\n const { ok, balance_mc } = await debitMc(fbUser.id, MC_COSTS.fb_search, LedgerOperation.FB_SEARCH, body.query.trim())\n if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.fb_search), 402)\n\n const driver = new BrowserDriver()\n let searchRefunded = false\n try {\n await driver.launch(kernelLaunchOpts())\n const page = driver.getPage() as import('playwright').Page\n await driver.navigateTo(searchUrl)\n\n try {\n await page.waitForFunction(\n () => {\n const bt = document.body ? (document.body.innerText ?? '') : ''\n return bt.includes('Library ID') || bt.includes('No results')\n },\n { timeout: 20_000, polling: 500 },\n )\n } catch {}\n await page.waitForTimeout(1500)\n\n for (let scroll = 0; scroll < 3; scroll++) {\n await page.evaluate(() => { if (document.body) window.scrollTo(0, document.body.scrollHeight) })\n await page.waitForTimeout(1000)\n }\n\n const rawBodyText: string = await page.evaluate(() => document.body?.innerText ?? '')\n\n if (rawBodyText.length < 200 || /Log in|You must log in|Create new account/.test(rawBodyText)) {\n await creditMc(fbUser.id, MC_COSTS.fb_search, LedgerOperation.FB_SEARCH_REFUND, 'soft-block empty result')\n searchRefunded = true\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_search', status: 'failed', query: body.query.trim(), error: 'soft-block: empty result refunded' })\n return c.json({ error: 'soft-block: no results returned (refunded)' }, 503)\n }\n\n const bodyText = rawBodyText.replace(//g, ' ').replace(/\\s+/g, ' ')\n\n const adChunks: string[] = []\n const splitRe = /(?=(?:Active|Inactive)\\s+Library ID[:\\s]+\\d{10,20})/g\n let last = 0\n let m: RegExpExecArray | null\n while ((m = splitRe.exec(bodyText)) !== null) {\n if (m.index > last) adChunks.push(bodyText.slice(last, m.index))\n last = m.index\n if (splitRe.lastIndex === m.index) splitRe.lastIndex++\n }\n if (last < bodyText.length) adChunks.push(bodyText.slice(last))\n\n const advertiserMap = new Map<string, { pageName: string; sampleLibraryId: string; adCount: number }>()\n\n for (const chunk of adChunks) {\n const lidM = chunk.match(/Library ID[:\\s]+([0-9]{10,20})/i)\n if (!lidM) continue\n const libraryId = lidM[1]\n\n const detailsParts = chunk.split('See ad details ')\n if (detailsParts.length < 2) continue\n const afterDetails = detailsParts[1]\n const sponsoredIdx = afterDetails.indexOf(' Sponsored')\n if (sponsoredIdx < 0) continue\n const pageName = afterDetails.slice(0, sponsoredIdx).trim()\n if (!pageName) continue\n\n const existing = advertiserMap.get(pageName)\n if (existing) {\n existing.adCount++\n } else {\n advertiserMap.set(pageName, { pageName, sampleLibraryId: libraryId, adCount: 1 })\n }\n }\n\n const results = [...advertiserMap.values()]\n .sort((a, b) => b.adCount - a.adCount)\n .slice(0, maxResults)\n\n const searchResult = { query: body.query.trim(), searchUrl, results }\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_search', status: 'done', query: body.query.trim(), resultCount: results.length, result: searchResult })\n return c.json(searchResult)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (!searchRefunded) await creditMc(fbUser.id, MC_COSTS.fb_search, LedgerOperation.FB_SEARCH_REFUND, 'failed call')\n await logRequestEvent({ userId: fbUser.id, source: 'facebook_search', status: 'failed', query: body.query.trim(), error: msg })\n if (msg.toLowerCase().includes('blocked') || msg.toLowerCase().includes('captcha')) {\n return c.json({ error: msg }, 503)\n }\n return c.json({ error: msg }, 500)\n } finally {\n await driver.close()\n }\n})\n\nconst ALLOWED_MEDIA_HOSTS = ['fbcdn.net', 'cdninstagram.com', 'scontent.facebook.com', 'scontent.cdninstagram.com']\n\nfacebookAdApp.post('/media', createApiKeyAuth<FbEnv>(), async (c) => {\n const raw = await c.req.json().catch(() => ({}))\n const parsed = FacebookMediaBodySchema.safeParse(raw)\n if (!parsed.success) {\n return c.json(invalidRequest(parsed.error.issues[0]?.message ?? 'Invalid request'), 400)\n }\n const body: FacebookMediaBody = parsed.data\n\n let parsedUrl: URL\n try {\n parsedUrl = new URL(body.url.trim())\n } catch {\n return c.json({ error: 'Invalid URL' }, 400)\n }\n\n const hostname = parsedUrl.hostname.toLowerCase()\n if (!ALLOWED_MEDIA_HOSTS.some(h => hostname === h || hostname.endsWith('.' + h))) {\n return c.json({ error: 'URL must be a Facebook or Instagram CDN media URL (fbcdn.net or cdninstagram.com)' }, 400)\n }\n\n let res: Response\n try {\n res = await fetch(parsedUrl.href, {\n headers: {\n 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',\n 'Referer': 'https://www.facebook.com/',\n },\n signal: AbortSignal.timeout(60_000),\n })\n } catch (err) {\n return c.json({ error: `Fetch failed: ${err instanceof Error ? err.message : String(err)}` }, 502)\n }\n\n if (!res.ok) {\n return c.json({ error: `CDN returned ${res.status}` }, 502)\n }\n\n const contentType = res.headers.get('content-type') ?? 'application/octet-stream'\n const ext = contentType.includes('video') ? 'mp4'\n : contentType.includes('jpeg') ? 'jpg'\n : contentType.includes('png') ? 'png'\n : contentType.includes('webp') ? 'webp'\n : 'bin'\n\n const filename = body.filename?.trim() ?? `fb-media.${ext}`\n\n return new Response(res.body, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Content-Disposition': `attachment; filename=\"${filename}\"`,\n 'Cache-Control': 'no-store',\n },\n })\n})\n","import type { Page } from 'playwright'\nimport type { IBrowserDriver } from '../driver/IBrowserDriver.js'\nimport type { FacebookAdResult, FacebookAdVariation } from '../types.js'\n\nexport interface PageIntelResult {\n listingUrl: string\n scannedAt: string\n advertiserPageId: string | null\n advertiserName: string | null\n summary: {\n totalAds: number\n activeCount: number\n inactiveCount: number\n videoCount: number\n imageCount: number\n unknownCreativeCount: number\n }\n ads: Array<{\n libraryId: string | null\n status: string | null\n started: string | null\n creativeType: 'video' | 'image' | 'unknown'\n headline: string | null\n primaryText: string | null\n cta: string | null\n clusterCount: number | null\n imageSrc: string | null\n videoSrc: string | null\n videoPoster: string | null\n }>\n}\n\nconst SPRITE_Y_TO_PLATFORM: Record<number, string> = {\n [-766]: 'Facebook',\n [-805]: 'Instagram',\n [-818]: 'Messenger',\n [-831]: 'Threads',\n [-779]: 'WhatsApp',\n [-792]: 'Audience Network',\n}\n\nfunction gcd(a: number, b: number): number {\n return b === 0 ? a : gcd(b, a % b)\n}\n\nfunction aspectRatioStr(w: number, h: number): string {\n const d = gcd(Math.abs(w), Math.abs(h))\n return `${w / d}:${h / d}`\n}\n\nfunction parseLibraryId(input: string): string | null {\n const m = input.match(/[?&]id=(\\d+)/) ?? input.match(/^(\\d{10,20})$/)\n return m ? m[1] : null\n}\n\nexport class FacebookAdExtractor {\n constructor(private readonly driver: IBrowserDriver) {}\n\n static resolveLibraryId(input: string): string | null {\n return parseLibraryId(input.trim())\n }\n\n async extract(libraryId: string, opts: { openModal?: boolean } = {}): Promise<FacebookAdResult> {\n const adLibraryUrl = `https://www.facebook.com/ads/library/?id=${libraryId}`\n const page = this.driver.getPage() as Page\n\n await this.driver.navigateTo(adLibraryUrl)\n await this.waitForHydration(page, libraryId)\n\n const cardData = await page.evaluate(\n ({ spriteMap, targetId }) => {\n const KNOWN_PLATFORMS = ['Facebook', 'Instagram', 'Messenger', 'Threads', 'WhatsApp', 'Audience Network']\n const CTA_LABELS = [\n 'Learn more', 'Shop now', 'Sign up', 'Subscribe', 'Get offer', 'Book now',\n 'Contact us', 'Apply now', 'Get quote', 'Download', 'Order now', 'Watch more',\n 'See menu', 'See offers', 'Listen now', 'Get directions', 'Send message', 'Call now',\n ]\n const SKIP_PREFIXES = [\n 'active', 'inactive', 'library id', 'started running', 'ran from', 'platforms',\n 'learn more', 'shop now', 'sign up', 'subscribe', 'get offer', 'book now',\n 'contact us', 'apply now', 'get quote', 'download', 'order now', 'watch more',\n ]\n if (!document.body) return null\n const root = findAdCard(targetId) ?? document.body\n return extractCard(root, spriteMap, KNOWN_PLATFORMS, CTA_LABELS, SKIP_PREFIXES)\n\n function findAdCard(id: string): Element | null {\n const allLeaves = Array.from(document.querySelectorAll('span,div')).filter(el => el.children.length === 0)\n const lidEl = allLeaves.find(el => (el.textContent ?? '').includes(id))\n if (!lidEl) return null\n let el: Element | null = lidEl.parentElement\n let best: Element | null = null\n while (el && el !== document.body) {\n const txt = (el as HTMLElement).innerText ?? ''\n const hasMedia = el.querySelector('video, img[src*=\"fbcdn.net\"]')\n const hasCTA = el.querySelector('a[role=\"button\"],button,[role=\"button\"]')\n const hasDomain = /\\b[A-Z0-9]{3,}\\.[A-Z]{2,}\\b/.test(txt)\n if (hasMedia && hasCTA && hasDomain) return el\n if ((hasMedia || hasCTA) && txt.length > 200) best = el\n el = el.parentElement\n }\n return best\n }\n\n function t(el: Element | null): string {\n return el ? ((el as HTMLElement).innerText ?? el.textContent ?? '').replace(/\\s+/g, ' ').trim() : ''\n }\n\n function extractCard(\n root: Element,\n sm: Record<number, string>,\n knownPlatforms: string[],\n ctaLabels: string[],\n skipPrefixes: string[],\n ) {\n const bodyText = (root as HTMLElement).innerText ?? root.textContent ?? ''\n\n let libraryId: string | null = null\n const lidM = bodyText.match(/Library ID[:\\s]+([0-9]{10,20})/)\n if (lidM) libraryId = lidM[1]\n\n let status: string | null = null\n const allEls = Array.from(root.querySelectorAll('span,div'))\n const activeEl = allEls.find(el => t(el) === 'Active' && el.children.length === 0)\n if (activeEl) status = 'Active'\n const inactiveEl = allEls.find(el => t(el) === 'Inactive' && el.children.length === 0)\n if (inactiveEl) status = 'Inactive'\n\n let started: string | null = null, ranFromDate: string | null = null, ranToDate: string | null = null\n const startM = bodyText.match(/Started running on\\s+([A-Za-z]+\\.?\\s+\\d{1,2},?\\s*\\d{4})/i)\n if (startM) started = startM[1].replace(/\\s+/g, ' ').trim()\n const ranM = bodyText.match(/Ran from\\s+([A-Za-z]+\\.?\\s+\\d{1,2},?\\s*\\d{4})\\s+to\\s+([A-Za-z]+\\.?\\s+\\d{1,2},?\\s*\\d{4})/i)\n if (ranM) { ranFromDate = ranM[1].trim(); ranToDate = ranM[2].trim() }\n\n let pageName: string | null = null, pageId: string | null = null, pageUrl: string | null = null\n const pageLinks = Array.from(root.querySelectorAll('a[href*=\"facebook.com\"]')).filter(a => {\n const href = (a as HTMLAnchorElement).href ?? ''\n return !href.includes('/ads/library') && !href.includes('l.facebook.com/l.php') &&\n !href.includes('facebook.com/help') && !href.includes('facebook.com/legal') &&\n !href.includes('facebook.com/policies')\n })\n if (pageLinks.length > 0) {\n const lnk = pageLinks[0] as HTMLAnchorElement\n const candidate = t(lnk)\n if (candidate && candidate.length < 80) pageName = candidate\n try {\n const u = new URL(lnk.href)\n const idParam = u.searchParams.get('id')\n if (idParam) { pageId = idParam; pageUrl = lnk.href }\n else {\n const seg = u.pathname.replace(/^\\//, '').split('/')[0]\n if (seg && seg !== 'ads') {\n pageUrl = 'https://www.facebook.com/' + seg\n if (/^\\d+$/.test(seg)) pageId = seg\n }\n if (!pageId) pageId = lnk.getAttribute('data-store-id') ?? lnk.getAttribute('data-profileid') ?? null\n }\n } catch {}\n }\n\n const platforms: string[] = []\n for (const icon of Array.from(root.querySelectorAll('[style*=\"mask\"],[style*=\"-webkit-mask\"],[style*=\"background-position\"]'))) {\n const st = window.getComputedStyle(icon)\n const pos = st.getPropertyValue('mask-position') || st.getPropertyValue('-webkit-mask-position') || st.getPropertyValue('background-position') || ''\n const m = pos.match(/(-?\\d+)px\\s+(-?\\d+)px/)\n if (!m) continue\n const platform = sm[parseInt(m[2])]\n if (platform && !platforms.includes(platform)) platforms.push(platform)\n }\n for (const el of Array.from(root.querySelectorAll('span,div'))) {\n const txt = t(el)\n if (el.children.length === 0 && knownPlatforms.includes(txt) && !platforms.includes(txt)) platforms.push(txt)\n }\n\n let multipleVersions = false, clusterCount: number | null = null\n const multiM = bodyText.match(/(\\d+)\\s+ad[s]?\\s+use[s]?\\s+this/i)\n if (multiM) { multipleVersions = true; clusterCount = parseInt(multiM[1]) }\n if (!multipleVersions && /multiple versions/i.test(bodyText)) multipleVersions = true\n\n let primaryText: string | null = null\n const textCandidates = Array.from(root.querySelectorAll('div,p,span'))\n .filter(el => {\n const txt = t(el)\n return txt.length > 30 && el.children.length === 0 &&\n !skipPrefixes.some(s => txt.toLowerCase().startsWith(s))\n })\n .sort((a, b) => t(b).length - t(a).length)\n if (textCandidates.length > 0) primaryText = t(textCandidates[0])\n\n let domain: string | null = null, headline: string | null = null\n let description: string | null = null, cta: string | null = null, landingUrl: string | null = null\n\n const domainEl = Array.from(root.querySelectorAll('span,div')).find(el => {\n const txt = t(el)\n return el.children.length === 0 && txt.length >= 4 && txt.length <= 60 &&\n txt === txt.toUpperCase() && /^[A-Z0-9.\\-]+$/.test(txt)\n })\n if (domainEl) {\n domain = t(domainEl)\n const container = domainEl.closest('div[role=\"link\"],a,[role=\"link\"]') ?? domainEl.parentElement\n if (container) {\n const bolds = container.querySelectorAll('strong,b')\n if (bolds.length > 0) {\n headline = t(bolds[0] as Element)\n } else {\n const candidates = Array.from(container.querySelectorAll('span,div')).filter(el => {\n const txt = t(el)\n return el.children.length === 0 && txt.length > 4 && txt.length < 120 && txt !== domain\n })\n if (candidates.length > 0) headline = t(candidates[0])\n }\n const descEls = Array.from(container.querySelectorAll('span,div')).filter(el => {\n const txt = t(el)\n return el.children.length === 0 && txt.length > 10 && txt !== domain && txt !== headline\n })\n if (descEls.length > 0) description = t(descEls[0])\n const lphpLinks = Array.from(container.querySelectorAll('a[href*=\"l.facebook.com/l.php\"],a[href*=\"l.instagram.com/l.php\"]'))\n if (lphpLinks.length > 0) {\n try { landingUrl = decodeURIComponent(new URL((lphpLinks[0] as HTMLAnchorElement).href).searchParams.get('u') ?? '') }\n catch { landingUrl = (lphpLinks[0] as HTMLAnchorElement).href }\n }\n }\n }\n if (!landingUrl) {\n const anyLphp = root.querySelector('a[href*=\"l.facebook.com/l.php\"],a[href*=\"l.instagram.com/l.php\"]')\n if (anyLphp) {\n try { landingUrl = decodeURIComponent(new URL((anyLphp as HTMLAnchorElement).href).searchParams.get('u') ?? '') }\n catch { landingUrl = (anyLphp as HTMLAnchorElement).href }\n }\n }\n\n const ctaEl = Array.from(root.querySelectorAll('a[role=\"button\"],button,[role=\"button\"],a')).find(el => {\n const txt = t(el)\n return ctaLabels.some(c => c.toLowerCase() === txt.toLowerCase())\n })\n if (ctaEl) cta = t(ctaEl)\n\n let videoSrc: string | null = null, videoPoster: string | null = null\n let videoDurationSec: number | null = null, videoWidth: number | null = null, videoHeight: number | null = null\n const videoEl = root.querySelector('video') as HTMLVideoElement | null\n if (videoEl) {\n videoSrc = videoEl.currentSrc || videoEl.src || (videoEl.querySelector('source') as HTMLSourceElement | null)?.src || null\n videoPoster = videoEl.poster || null\n if (isFinite(videoEl.duration) && videoEl.duration > 0) videoDurationSec = Math.round(videoEl.duration * 100) / 100\n videoWidth = videoEl.videoWidth > 0 ? videoEl.videoWidth : null\n videoHeight = videoEl.videoHeight > 0 ? videoEl.videoHeight : null\n }\n\n let imageSrc: string | null = null\n if (!videoSrc) {\n const imgs = Array.from(root.querySelectorAll('img')).filter(img => {\n const src = (img as HTMLImageElement).src ?? ''\n return (src.includes('fbcdn.net') || src.includes('cdninstagram.com')) && (img as HTMLImageElement).width > 100\n })\n if (imgs.length > 0) imageSrc = (imgs[0] as HTMLImageElement).src\n }\n\n return {\n libraryId, status, started, ranFromDate, ranToDate,\n platforms, pageName, pageId, pageUrl,\n multipleVersions, clusterCount,\n primaryText, domain, headline, description, cta, landingUrl,\n videoSrc, videoPoster, videoDurationSec, videoWidth, videoHeight, imageSrc,\n }\n }\n },\n { spriteMap: SPRITE_Y_TO_PLATFORM, targetId: libraryId },\n )\n\n if (!cardData) throw new Error('Page body was null after hydration — possible redirect or consent wall')\n\n let platforms = cardData.platforms\n let variations: FacebookAdVariation[] = []\n let modalOverrides: Partial<typeof cardData> = {}\n\n if (opts.openModal !== false) {\n const modal = await this.openModal(page)\n if (modal) {\n if (modal.platforms.length > platforms.length) platforms = modal.platforms\n if (modal.variations.length > 0) variations = modal.variations\n modalOverrides = modal.overrides\n }\n }\n\n const merged = { ...cardData, ...Object.fromEntries(Object.entries(modalOverrides).filter(([, v]) => v != null)) }\n\n const videoAspectRatio = (merged.videoWidth && merged.videoHeight)\n ? aspectRatioStr(merged.videoWidth, merged.videoHeight)\n : null\n\n return {\n libraryId,\n adLibraryUrl,\n status: merged.status ?? null,\n started: merged.started ?? null,\n ranFromDate: merged.ranFromDate ?? null,\n ranToDate: merged.ranToDate ?? null,\n platforms,\n pageName: merged.pageName ?? null,\n pageId: merged.pageId ?? null,\n pageUrl: merged.pageUrl ?? null,\n multipleVersions: merged.multipleVersions ?? false,\n clusterCount: merged.clusterCount ?? null,\n primaryText: merged.primaryText ?? null,\n domain: merged.domain ?? null,\n headline: merged.headline ?? null,\n description: merged.description ?? null,\n cta: merged.cta ?? null,\n landingUrl: merged.landingUrl ?? null,\n videoSrc: merged.videoSrc ?? null,\n videoPoster: merged.videoPoster ?? null,\n videoDurationSec: merged.videoDurationSec ?? null,\n videoWidth: merged.videoWidth ?? null,\n videoHeight: merged.videoHeight ?? null,\n videoAspectRatio,\n imageSrc: merged.imageSrc ?? null,\n variations,\n extractedAt: new Date().toISOString(),\n }\n }\n\n async extractPageIntel(listingUrl: string, maxAds: number): Promise<PageIntelResult> {\n const page = this.driver.getPage() as Page\n\n try {\n await page.waitForFunction(\n () => {\n const bt = document.body ? (document.body.innerText ?? '') : ''\n return bt.includes('Library ID') || bt.includes('No ads')\n },\n { timeout: 25_000, polling: 600 },\n )\n } catch {}\n await page.waitForTimeout(1500)\n\n let prevCount = 0\n for (let scroll = 0; scroll < 20; scroll++) {\n const count = await page.evaluate(() => {\n const bt = document.body ? (document.body.innerText ?? '') : ''\n return [...bt.matchAll(/Library ID/g)].length\n })\n if (count >= maxAds) break\n if (count === prevCount && scroll > 0) break\n prevCount = count\n await page.evaluate(() => { if (document.body) window.scrollTo(0, document.body.scrollHeight) })\n await page.waitForTimeout(1200)\n }\n\n type AdVisual = { imageSrc: string | null; videoSrc: string | null; videoPoster: string | null }\n const adVisuals: Record<string, AdVisual> = await page.evaluate(() => {\n const out: { [lid: string]: { imageSrc: string | null; videoSrc: string | null; videoPoster: string | null } } = {}\n const walker = document.createTreeWalker(document.body ?? document.documentElement, NodeFilter.SHOW_TEXT)\n let node: Node | null\n let processed = 0\n while ((node = walker.nextNode()) && processed < 12000) {\n processed++\n const t = node.textContent ?? ''\n if (!t.includes('Library ID')) continue\n const m = t.match(/Library ID[:\\s]+(\\d{10,20})/)\n if (!m) continue\n const lid = m[1]\n if (out[lid]) continue\n let el: Element | null = node.parentElement\n let found = false\n for (let i = 0; i < 22 && el && !found; i++) {\n const vid = el.querySelector('video') as HTMLVideoElement | null\n if (vid) {\n out[lid] = { imageSrc: null, videoSrc: vid.currentSrc || vid.src || null, videoPoster: vid.poster || null }\n found = true\n } else {\n const img = el.querySelector('img[src*=\"fbcdn.net\"],img[src*=\"cdninstagram\"]') as HTMLImageElement | null\n if (img && img.width > 60) {\n out[lid] = { imageSrc: img.src, videoSrc: null, videoPoster: null }\n found = true\n }\n }\n el = el.parentElement\n }\n if (!found) out[lid] = { imageSrc: null, videoSrc: null, videoPoster: null }\n }\n return out\n }).catch(() => ({} as Record<string, AdVisual>))\n\n const rawBodyText: string = await page.evaluate(() =>\n document.body ? (document.body.innerText ?? '') : ''\n )\n\n const bodyText = rawBodyText.replace(//g, ' ').replace(/\\s+/g, ' ')\n\n const CTA_LABELS = [\n 'Learn more', 'Shop now', 'Sign up', 'Subscribe', 'Get offer', 'Book now',\n 'Contact us', 'Apply now', 'Get quote', 'Download', 'Order now', 'Watch more',\n 'See menu', 'See offers', 'Listen now', 'Get directions', 'Send message', 'Call now',\n ]\n const adChunks: string[] = []\n const splitRe = /(?=(?:Active|Inactive)\\s+Library ID[:\\s]+\\d{10,20})/g\n let last = 0\n let m: RegExpExecArray | null\n while ((m = splitRe.exec(bodyText)) !== null) {\n if (m.index > last) adChunks.push(bodyText.slice(last, m.index))\n last = m.index\n if (splitRe.lastIndex === m.index) splitRe.lastIndex++\n }\n if (last < bodyText.length) adChunks.push(bodyText.slice(last))\n\n const ads = adChunks\n .filter(c => /Library ID[:\\s]+\\d{10,20}/i.test(c))\n .slice(0, maxAds)\n .map(chunk => {\n const lidM = chunk.match(/Library ID[:\\s]+([0-9]{10,20})/i)\n const libraryId = lidM ? lidM[1] : null\n\n const statusM = chunk.match(/^(Active|Inactive)/i)\n const status = statusM ? statusM[1] : null\n\n const startM = chunk.match(/Started running on\\s+([A-Za-z]+\\.?\\s+\\d{1,2},?\\s*\\d{4})/i)\n const started = startM ? startM[1].replace(/\\s+/g, ' ').trim() : null\n\n const visual = adVisuals[libraryId ?? ''] ?? { imageSrc: null, videoSrc: null, videoPoster: null }\n const hasVideoText = /0:00\\s*\\/\\s*0:00|\\d+:\\d+\\s*\\/\\s*\\d+:\\d+/.test(chunk)\n const creativeType: 'video' | 'image' | 'unknown' = (visual.videoSrc || hasVideoText) ? 'video' : (visual.imageSrc ? 'image' : 'unknown')\n\n let domain: string | null = null\n const domainRe = /\\b([A-Z0-9]{2,}(?:\\.[A-Z]{2,})+)\\b/g\n let dm: RegExpExecArray | null\n while ((dm = domainRe.exec(chunk)) !== null) {\n const c = dm[1]\n if (c.length >= 6 && c !== 'LIBRARY') { domain = c; break }\n }\n\n let primaryText: string | null = null\n let headline: string | null = null\n const sponsoredIdx = chunk.search(/\\bSponsored\\b/i)\n if (sponsoredIdx >= 0) {\n let afterSponsored = chunk.slice(sponsoredIdx + 'Sponsored'.length).trim()\n afterSponsored = afterSponsored.replace(/\\s*\\d*:?\\d+\\s*\\/\\s*\\d*:?\\d+.*$/, '')\n if (domain) {\n const di = afterSponsored.indexOf(domain)\n if (di >= 0) afterSponsored = afterSponsored.slice(0, di)\n }\n const pt = afterSponsored.trim()\n if (pt.length >= 30) primaryText = pt\n }\n const ctaRe2 = new RegExp('\\\\b(' + CTA_LABELS.map(l => l.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')).join('|') + ')\\\\b', 'i')\n if (domain) {\n const di = chunk.indexOf(domain)\n if (di >= 0) {\n let afterDomain = chunk.slice(di + domain.length).trim()\n const ctaMatch = afterDomain.search(ctaRe2)\n if (ctaMatch >= 0) afterDomain = afterDomain.slice(0, ctaMatch).trim()\n if (afterDomain.length > 3 && afterDomain.length < 120) headline = afterDomain\n }\n }\n if (!headline) {\n const ctaIdx = chunk.search(ctaRe2)\n if (ctaIdx > 0) {\n const beforeCta = chunk.slice(0, ctaIdx).trimEnd()\n const sentenceEnd = Math.max(beforeCta.lastIndexOf('. '), beforeCta.lastIndexOf('! '), beforeCta.lastIndexOf('? '))\n if (sentenceEnd >= 0) {\n const candidate = beforeCta.slice(sentenceEnd + 2).trim()\n if (candidate.length > 3 && candidate.length < 120) headline = candidate\n } else if (beforeCta.length > 3 && beforeCta.length < 120) {\n headline = beforeCta.trim()\n }\n }\n }\n\n let cta: string | null = null\n for (const lbl of CTA_LABELS) {\n if (chunk.toLowerCase().includes(lbl.toLowerCase())) { cta = lbl; break }\n }\n\n const multiM = chunk.match(/(\\d+)\\s+ad[s]?\\s+use[s]?\\s+this/i)\n const clusterCount = multiM ? parseInt(multiM[1]) : null\n\n return {\n libraryId, status, started, creativeType, headline, primaryText, cta, clusterCount,\n imageSrc: visual.imageSrc, videoSrc: visual.videoSrc, videoPoster: visual.videoPoster,\n }\n })\n .filter((ad, idx, arr) => ad.libraryId && arr.findIndex(a => a.libraryId === ad.libraryId) === idx)\n\n const totalAds = ads.length\n const activeCount = ads.filter(a => a.status === 'Active').length\n const videoCount = ads.filter(a => a.creativeType === 'video').length\n const imageCount = ads.filter(a => a.creativeType === 'image').length\n const unknownCreativeCount = ads.filter(a => a.creativeType === 'unknown').length\n\n const advertiserPageId = new URL(page.url()).searchParams.get('view_all_page_id') ?? null\n\n const nameFreq = new Map<string, number>()\n for (const chunk of adChunks) {\n const detailsParts = chunk.split('See ad details ')\n if (detailsParts.length < 2) continue\n const sponsoredIdx = detailsParts[1].indexOf(' Sponsored')\n if (sponsoredIdx < 0) continue\n const n = detailsParts[1].slice(0, sponsoredIdx).trim()\n if (n) nameFreq.set(n, (nameFreq.get(n) ?? 0) + 1)\n }\n const advertiserName = nameFreq.size\n ? [...nameFreq.entries()].sort((a, b) => b[1] - a[1])[0][0]\n : null\n\n return {\n listingUrl,\n scannedAt: new Date().toISOString(),\n advertiserPageId,\n advertiserName,\n summary: {\n totalAds,\n activeCount,\n inactiveCount: totalAds - activeCount,\n videoCount,\n imageCount,\n unknownCreativeCount,\n },\n ads,\n }\n }\n\n private async waitForHydration(page: Page, libraryId: string): Promise<void> {\n try {\n await page.waitForFunction(\n (id: string) => {\n const bt = document.body ? (document.body.innerText ?? '') : ''\n return (bt.includes('Library ID: ' + id) || bt.includes('Library ID:' + id)) &&\n (!!document.querySelector('a[href*=\"l.facebook.com/l.php\"]') ||\n !!document.querySelector('video') ||\n !!document.querySelector('img[src*=\"fbcdn.net\"]'))\n },\n libraryId,\n { timeout: 30_000, polling: 500 },\n )\n } catch {}\n await page.waitForTimeout(800)\n }\n\n private async openModal(page: Page): Promise<{\n platforms: string[]\n variations: FacebookAdVariation[]\n overrides: Record<string, unknown>\n } | null> {\n const trigger = page.locator('text=\"See ad details\", text=\"See summary details\"').first()\n if (await trigger.count() === 0) return null\n\n try {\n await trigger.click()\n await page.waitForSelector('[role=\"dialog\"]', { timeout: 8_000 })\n await page.waitForTimeout(800)\n } catch {\n return null\n }\n\n const raw = await page.evaluate(() => {\n const modal = document.querySelector('[role=\"dialog\"]') ?? document.body\n const KNOWN = ['Facebook', 'Instagram', 'Messenger', 'Threads', 'WhatsApp', 'Audience Network']\n const CTA_LABELS = [\n 'Learn more', 'Shop now', 'Sign up', 'Subscribe', 'Get offer', 'Book now',\n 'Contact us', 'Apply now', 'Get quote', 'Download', 'Order now', 'Watch more',\n 'See menu', 'See offers', 'Listen now', 'Get directions', 'Send message', 'Call now',\n ]\n\n function t(el: Element | null): string {\n return el ? ((el as HTMLElement).innerText ?? el.textContent ?? '').replace(/\\s+/g, ' ').trim() : ''\n }\n\n const platforms = Array.from(modal.querySelectorAll('span,div'))\n .filter(el => el.children.length === 0 && KNOWN.includes(t(el)))\n .map(el => t(el))\n .filter((v, i, a) => a.indexOf(v) === i)\n\n const variationCardEls = Array.from(\n modal.querySelectorAll('[data-visualcompletion=\"ad_variation_card\"], .xh8yej3, [data-ad-preview]')\n )\n\n const variations = variationCardEls.map(card => {\n let primaryText: string | null = null\n const textEls = Array.from(card.querySelectorAll('div,p,span'))\n .filter(el => {\n const txt = t(el)\n return txt.length > 60 && el.children.length < 4\n })\n .sort((a, b) => t(b).length - t(a).length)\n if (textEls.length > 0) primaryText = t(textEls[0])\n\n const domainEl = Array.from(card.querySelectorAll('span,div')).find(el => {\n const txt = t(el)\n return el.children.length === 0 && txt.length >= 4 && txt.length <= 60 &&\n txt === txt.toUpperCase() && /^[A-Z0-9.\\-]+$/.test(txt)\n })\n const domain = domainEl ? t(domainEl) : null\n\n let headline: string | null = null\n if (domainEl) {\n const container = domainEl.closest('div[role=\"link\"],a,[role=\"link\"]') ?? domainEl.parentElement\n if (container) {\n const bolds = container.querySelectorAll('strong,b')\n if (bolds.length > 0) headline = t(bolds[0] as Element)\n }\n }\n\n let landingUrl: string | null = null\n const lphp = card.querySelector('a[href*=\"l.facebook.com/l.php\"],a[href*=\"l.instagram.com/l.php\"]')\n if (lphp) {\n try { landingUrl = decodeURIComponent(new URL((lphp as HTMLAnchorElement).href).searchParams.get('u') ?? '') }\n catch { landingUrl = (lphp as HTMLAnchorElement).href }\n }\n\n const ctaEl = Array.from(card.querySelectorAll('a[role=\"button\"],button,[role=\"button\"],a')).find(el => {\n const txt = t(el)\n return CTA_LABELS.some(c => c.toLowerCase() === txt.toLowerCase())\n })\n const cta = ctaEl ? t(ctaEl) : null\n\n const videoEl = card.querySelector('video') as HTMLVideoElement | null\n const videoSrc = videoEl ? (videoEl.currentSrc || videoEl.src || null) : null\n const videoPoster = videoEl?.poster ?? null\n const videoDurationSec = videoEl && isFinite(videoEl.duration) && videoEl.duration > 0\n ? Math.round(videoEl.duration * 100) / 100 : null\n const videoWidth = (videoEl && videoEl.videoWidth > 0) ? videoEl.videoWidth : null\n const videoHeight = (videoEl && videoEl.videoHeight > 0) ? videoEl.videoHeight : null\n\n let imageSrc: string | null = null\n if (!videoSrc) {\n const imgs = Array.from(card.querySelectorAll('img')).filter(img => {\n const src = (img as HTMLImageElement).src ?? ''\n return (src.includes('fbcdn.net') || src.includes('cdninstagram.com')) && (img as HTMLImageElement).width > 100\n })\n if (imgs.length > 0) imageSrc = (imgs[0] as HTMLImageElement).src\n }\n\n return { primaryText, domain, headline, landingUrl, cta, videoSrc, videoPoster,\n videoDurationSec, videoWidth, videoHeight, imageSrc }\n })\n\n const overrides: Record<string, unknown> = {}\n if (platforms.length > 0) overrides['platforms'] = platforms\n\n const avtLink = modal.querySelector('a[href*=\"facebook.com\"]:not([href*=\"/ads/library\"]):not([href*=\"l.php\"])')\n if (avtLink) { const c = t(avtLink); if (c && c.length < 80) overrides['pageName'] = c }\n\n const activeEl = Array.from(modal.querySelectorAll('span,div')).find(el => t(el) === 'Active' && el.children.length === 0)\n if (activeEl) overrides['status'] = 'Active'\n\n const startM = ((modal as HTMLElement).innerText ?? '').match(/Started running on\\s+([A-Za-z]+\\.?\\s+\\d{1,2},?\\s*\\d{4})/i)\n if (startM) overrides['started'] = startM[1].replace(/\\s+/g, ' ').trim()\n\n return { platforms, variations, overrides }\n })\n\n return {\n platforms: raw.platforms,\n overrides: raw.overrides,\n variations: raw.variations.map((v): FacebookAdVariation => ({\n primaryText: v.primaryText ?? null,\n domain: v.domain ?? null,\n headline: v.headline ?? null,\n description: null,\n cta: v.cta ?? null,\n landingUrl: v.landingUrl ?? null,\n videoSrc: v.videoSrc ?? null,\n videoPoster: v.videoPoster ?? null,\n videoDurationSec: v.videoDurationSec ?? null,\n videoWidth: v.videoWidth ?? null,\n videoHeight: v.videoHeight ?? null,\n imageSrc: v.imageSrc ?? null,\n })),\n }\n }\n}\n","import { Hono } from 'hono'\nimport { creditMc, debitMc, logRequestEvent } from './db.js'\nimport { MC_COSTS, insufficientBalanceResponse, LedgerOperation } from './rates.js'\nimport { MapsExtractor } from '../extractor/MapsExtractor.js'\nimport { BrowserDriver } from '../driver/BrowserDriver.js'\nimport { MapsPlaceOptionsSchema } from '../schemas.js'\nimport { createApiKeyAuth, type ApiKeyEnv } from './api-auth.js'\n\ntype MapsEnv = ApiKeyEnv\n\nexport const mapsApp = new Hono<MapsEnv>()\n\nmapsApp.post('/place', createApiKeyAuth<MapsEnv>(), async (c) => {\n const user = c.get('user')\n const body = await c.req.json().catch(() => ({}))\n const parsed = MapsPlaceOptionsSchema.safeParse({\n kernelApiKey: process.env.KERNEL_API_KEY,\n ...body,\n })\n if (!parsed.success) {\n return c.json({ error: parsed.error.issues[0]?.message ?? 'Invalid request' }, 400)\n }\n\n const { ok, balance_mc } = await debitMc(\n user.id,\n MC_COSTS.maps_place,\n LedgerOperation.MAPS_PLACE,\n `${parsed.data.businessName} ${parsed.data.location}`,\n )\n if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.maps_place), 402)\n\n const driver = new BrowserDriver()\n const extractor = new MapsExtractor(driver)\n let reviewDebitMc = 0\n try {\n const result = await extractor.extract(parsed.data)\n const reviewCount = Array.isArray(result.reviews) ? result.reviews.length : 0\n if (reviewCount > 0) {\n const reviewCost = reviewCount * MC_COSTS.maps_review\n const reviewDebit = await debitMc(\n user.id,\n reviewCost,\n LedgerOperation.MAPS_REVIEW,\n `${parsed.data.businessName} ${parsed.data.location}`,\n )\n if (reviewDebit.ok) {\n reviewDebitMc = reviewCost\n } else {\n result.reviews = []\n }\n }\n await logRequestEvent({\n userId: user.id,\n source: 'maps_place',\n status: 'done',\n query: `${parsed.data.businessName} ${parsed.data.location}`,\n location: parsed.data.location,\n resultCount: reviewCount,\n result,\n })\n return c.json(result)\n } catch (err) {\n await creditMc(user.id, MC_COSTS.maps_place, LedgerOperation.REFUND, 'failed maps_place call')\n if (reviewDebitMc > 0) {\n await creditMc(\n user.id,\n reviewDebitMc,\n LedgerOperation.MAPS_REVIEW_REFUND,\n 'failed maps review extraction',\n )\n }\n const msg = err instanceof Error ? err.message : String(err)\n await logRequestEvent({\n userId: user.id,\n source: 'maps_place',\n status: 'failed',\n query: `${parsed.data.businessName} ${parsed.data.location}`,\n location: parsed.data.location,\n error: msg,\n })\n if (msg.includes('CAPTCHA') || msg.includes('blocked')) {\n return c.json({ error: msg }, 503)\n }\n return c.json({ error: msg }, 500)\n } finally {\n await driver.close()\n }\n})\n","import type { Page } from 'playwright'\nimport { MapsSelectors } from '../selectors.js'\n\nexport class MapsNavigator {\n constructor(private readonly page: Page) {}\n\n async navigateToPlacePage(businessName: string, location: string): Promise<string> {\n const query = `${businessName} ${location}`\n const searchUrl = `https://www.google.com/maps/search/${encodeURIComponent(query)}`\n await this.page.goto(searchUrl, { waitUntil: 'domcontentloaded', timeout: 45_000 })\n\n const onPlacePage = await this.page.evaluate(() => /\\/maps\\/place\\//.test(window.location.href))\n if (!onPlacePage) {\n await this.clickFirstResult()\n }\n await this.page.waitForSelector('h1', { timeout: 20_000 })\n return this.page.url()\n }\n\n async navigateToReviews(placeUrl: string): Promise<void> {\n const reviewsUrl = this.buildReviewsUrl(placeUrl)\n if (reviewsUrl) {\n await this.page.goto(reviewsUrl, { waitUntil: 'domcontentloaded', timeout: 45_000 })\n await this.page.waitForSelector('[role=\"tab\"]', { timeout: 20_000 })\n await this.page.waitForTimeout(1500)\n const activeTab = await this.page.$('button[role=\"tab\"][aria-selected=\"true\"]')\n const activeText = activeTab ? await activeTab.evaluate(el => (el as HTMLElement).innerText) : ''\n if (!/review/i.test(activeText)) {\n await this.clickReviewTab()\n }\n } else {\n await this.page.goto(placeUrl, { waitUntil: 'domcontentloaded', timeout: 45_000 }).catch(() => {})\n await this.page.waitForSelector('[role=\"tab\"]', { timeout: 20_000 }).catch(() => {})\n await this.clickReviewTab()\n }\n await this.page.evaluate(() => {\n const scrollables = ([...document.querySelectorAll('div[tabindex=\"-1\"]')] as HTMLElement[])\n .filter(el => el.scrollHeight > el.clientHeight + 50)\n const withCards = scrollables\n .map(el => ({ el, cards: el.querySelectorAll('[data-review-id]').length }))\n .filter(x => x.cards > 0)\n .sort((a, b) => b.cards - a.cards)\n const pane = withCards.length > 0\n ? withCards[0].el\n : scrollables.sort((a, b) => b.scrollHeight - a.scrollHeight)[0]\n if (pane) {\n pane.scrollTop = pane.scrollHeight\n } else {\n window.scrollTo(0, document.body.scrollHeight)\n }\n })\n await this.page.waitForSelector('[data-review-id]', { timeout: 8_000 }).catch(() => {})\n }\n\n async navigateToAboutTab(): Promise<void> {\n const aboutTab = await this.page.$(MapsSelectors.aboutTab)\n if (!aboutTab) return\n const tabText = await aboutTab.evaluate(el => (el as HTMLElement).innerText)\n if (!/about/i.test(tabText)) return\n await aboutTab.click().catch(() => {})\n await this.page.waitForSelector('[role=\"main\"] h2', { timeout: 5_000 }).catch(() => {})\n }\n\n buildReviewsUrl(placeUrl: string): string | null {\n const fid = placeUrl.match(/!1s(0x[0-9a-f]+:0x[0-9a-f]+)/i)\n const pin = placeUrl.match(/!3d(-?\\d+\\.\\d+)!4d(-?\\d+\\.\\d+)/)\n const kg = placeUrl.match(/!16s([^!?&]+)/)\n const at = placeUrl.match(/@(-?\\d+\\.\\d+),(-?\\d+\\.\\d+),(\\d+(?:\\.\\d+)?z)/)\n const name = placeUrl.match(/\\/place\\/([^/@]+)/)\n if (!fid || !pin || !name) return null\n const atLat = at ? at[1] : pin[1]\n const atLng = at ? at[2] : pin[2]\n const atZoom = at ? at[3] : '17z'\n const kgPart = kg ? `!16s${kg[1]}` : ''\n return (\n `https://www.google.com/maps/place/${name[1]}` +\n `/@${atLat},${atLng},${atZoom}` +\n `/data=!4m8!3m7!1s${fid[1]}!8m2!3d${pin[1]}!4d${pin[2]}!9m1!1b1${kgPart}`\n )\n }\n\n private async clickReviewTab(): Promise<void> {\n const tab = await this.page.$(MapsSelectors.reviewTab)\n if (!tab) return\n await tab.click().catch(() => {})\n await this.page.waitForSelector(MapsSelectors.reviewCard, { timeout: 10_000 }).catch(() => {})\n await this.page.waitForTimeout(1000)\n }\n\n private async clickFirstResult(): Promise<void> {\n const firstResult = await this.page.waitForSelector(\n '[role=\"feed\"] a[href*=\"/maps/place/\"]',\n { timeout: 15_000 },\n ).catch(() => null)\n if (firstResult) {\n const href = await firstResult.getAttribute('href')\n if (href) {\n const fullUrl = href.startsWith('http') ? href : `https://www.google.com${href}`\n await this.page.goto(fullUrl, { waitUntil: 'domcontentloaded', timeout: 45_000 })\n return\n }\n await firstResult.click()\n } else {\n const fallback = await this.page.$('[data-result-index=\"0\"]')\n if (fallback) await fallback.click()\n }\n await this.page.waitForTimeout(2500)\n }\n}\n","import type { Page } from 'playwright'\nimport type { MapsReviewCard } from '../types.js'\nimport { MapsSelectors } from '../selectors.js'\n\nexport const REVIEW_SCROLL_BUDGET_MS = 45_000\nexport const REVIEW_SCROLL_STEP_MS = 1_200\n\nexport interface ReviewCollectionResult {\n reviews: MapsReviewCard[]\n reachedTarget: boolean\n scrolledRounds: number\n durationMs: number\n}\n\nexport interface IMapsReviewCollector {\n collect(maxReviews: number): Promise<ReviewCollectionResult>\n}\n\nexport class MapsReviewCollector implements IMapsReviewCollector {\n constructor(private readonly page: Page) {}\n\n async collect(maxReviews: number): Promise<ReviewCollectionResult> {\n const startMs = Date.now()\n const map = new Map<string, MapsReviewCard>()\n let scrolledRounds = 0\n let noGrowthRounds = 0\n\n while (true) {\n if (Date.now() - startMs >= REVIEW_SCROLL_BUDGET_MS) break\n\n await this.page.evaluate(() => {\n const scrollables = ([...document.querySelectorAll('div[tabindex=\"-1\"]')] as HTMLElement[])\n .filter(el => el.scrollHeight > el.clientHeight + 50)\n const withCards = scrollables\n .map(el => ({ el, cards: el.querySelectorAll('[data-review-id]').length }))\n .filter(x => x.cards > 0)\n .sort((a, b) => b.cards - a.cards)\n const pane = withCards.length > 0\n ? withCards[0].el\n : scrollables.sort((a, b) => b.scrollHeight - a.scrollHeight)[0]\n if (pane) {\n pane.scrollTop = pane.scrollHeight\n } else {\n window.scrollTo(0, document.body.scrollHeight)\n }\n })\n\n await this.page.evaluate((sel: string) => {\n document.querySelectorAll<HTMLElement>(sel).forEach(btn => btn.click())\n }, MapsSelectors.expandReview)\n\n await this.page.waitForTimeout(REVIEW_SCROLL_STEP_MS)\n scrolledRounds++\n\n const cards = await this.page.evaluate((): Array<{\n reviewId: string\n author: string | null\n text: string | null\n date: string | null\n stars: string | null\n }> => {\n return Array.from(document.querySelectorAll('[data-review-id]')).map(card => ({\n reviewId: card.getAttribute('data-review-id') ?? '',\n author: card.querySelector('.d4r55')?.textContent?.trim() ?? null,\n text: card.querySelector('.wiI7pd')?.textContent?.trim() ?? null,\n date: card.querySelector('.rsqaWe')?.textContent?.trim() ?? null,\n stars: card.querySelector('[role=\"img\"][aria-label*=\"star\"]')?.getAttribute('aria-label') ?? null,\n }))\n })\n\n const prevSize = map.size\n\n for (const c of cards) {\n if (!c.reviewId) continue\n const existing = map.get(c.reviewId)\n if (!existing || (c.text && c.text.length > 0)) {\n map.set(c.reviewId, {\n reviewId: c.reviewId,\n author: c.author,\n stars: c.stars,\n date: c.date,\n text: c.text,\n ownerResponse: null,\n })\n }\n }\n\n if (map.size >= maxReviews) break\n\n if (map.size === 0) {\n noGrowthRounds = 0\n } else if (map.size === prevSize) {\n noGrowthRounds++\n if (noGrowthRounds >= 4) break\n } else {\n noGrowthRounds = 0\n }\n }\n\n const durationMs = Date.now() - startMs\n const reachedTarget = map.size >= maxReviews\n const reviews = [...map.values()].slice(0, maxReviews)\n\n return { reviews, reachedTarget, scrolledRounds, durationMs }\n }\n}\n","import type { Page } from 'playwright'\nimport type { IBrowserDriver } from '../driver/IBrowserDriver.js'\nimport type {\n MapsPlaceOptions,\n MapsPlaceResult,\n MapsHoursDay,\n MapsReviewCard,\n MapsUrlIdentifiers,\n RawMapsOverview,\n RawMapsReviewStats,\n DriverConfig,\n} from '../types.js'\nimport {\n RawMapsOverviewSchema,\n RawMapsHoursRowSchema,\n RawMapsReviewStatsSchema,\n RawMapsAboutAttributeSchema,\n} from '../schemas.js'\nimport { MapsSelectors } from '../selectors.js'\nimport type { IMapsExtractor } from './IMapsExtractor.js'\nimport { MapsNavigator } from './MapsNavigator.js'\nimport { MapsReviewCollector } from './MapsReviewCollector.js'\nimport { z } from 'zod'\n\nexport class MapsExtractor implements IMapsExtractor {\n constructor(private readonly driver: IBrowserDriver) {}\n\n async extract(options: MapsPlaceOptions): Promise<MapsPlaceResult> {\n const startMs = Date.now()\n const config: DriverConfig = {\n headless: options.headless,\n kernelApiKey: options.kernelApiKey,\n kernelProxyId: options.kernelProxyId,\n viewport: { width: 1280, height: 900 },\n locale: `${options.hl}-${options.gl.toUpperCase()}`,\n }\n\n try {\n await this.driver.launch(config)\n const page = this.driver.getPage() as Page\n const navigator = new MapsNavigator(page)\n\n const placeUrl = await navigator.navigateToPlacePage(options.businessName, options.location)\n const identifiers = this.parseUrlIdentifiers(placeUrl)\n\n const overview = await this.extractOverview(page)\n const hoursTable = await this.extractFullHours(page)\n\n let reviewStats: RawMapsReviewStats = { reviewHistogram: [], reviewTopics: [] }\n let reviews: MapsReviewCard[] = []\n let reviewsStatus: import('../types.js').ReviewsStatus = 'not_requested'\n\n if (!options.includeReviews) {\n try {\n await navigator.navigateToReviews(placeUrl)\n reviewStats = await this.extractReviewStats(page)\n } catch (err) {\n console.warn('[MapsExtractor] review stats extraction failed:', err instanceof Error ? err.message : String(err))\n }\n } else {\n try {\n await navigator.navigateToReviews(placeUrl)\n reviewStats = await this.extractReviewStats(page)\n const collected = await new MapsReviewCollector(page).collect(options.maxReviews)\n reviews = collected.reviews\n const overviewReviewCount = overview.reviewCount\n ? parseInt(String(overview.reviewCount).replace(/[^0-9]/g, ''), 10)\n : NaN\n if (reviews.length > 0) {\n reviewsStatus = 'collected'\n } else if (overviewReviewCount === 0 || (Number.isNaN(overviewReviewCount) && reviewStats.reviewHistogram.length === 0)) {\n reviewsStatus = 'none_exist'\n } else {\n reviewsStatus = 'unavailable'\n }\n } catch (err) {\n console.warn('[MapsExtractor] review extraction failed:', err instanceof Error ? err.message : String(err))\n reviewsStatus = 'unavailable'\n }\n }\n\n await navigator.navigateToAboutTab()\n const aboutAttributes = await this.extractAbout(page)\n\n const reviewCountFinal = overview.reviewCount\n ?? (reviewStats.reviewHistogram.length > 0\n ? reviewStats.reviewHistogram.reduce((s, r) => s + parseInt(r.count, 10), 0).toString()\n : null)\n\n return {\n businessName: options.businessName,\n location: options.location,\n extractedAt: new Date().toISOString(),\n placeUrl,\n ...identifiers,\n ...overview,\n reviewCount: reviewCountFinal,\n hoursTable,\n reviewHistogram: reviewStats.reviewHistogram,\n reviewTopics: reviewStats.reviewTopics,\n aboutAttributes,\n reviews,\n reviewsStatus,\n durationMs: Date.now() - startMs,\n }\n } finally {\n await this.driver.close()\n }\n }\n\n private parseUrlIdentifiers(url: string): MapsUrlIdentifiers {\n const pin = url.match(/!3d(-?\\d+\\.\\d+)!4d(-?\\d+\\.\\d+)/)\n const fid = url.match(/!1s(0x[0-9a-f]+):(0x[0-9a-f]+)/i)\n const kg = decodeURIComponent(url).match(/!16s(\\/g\\/[a-z0-9_]+)/i)\n let cidDecimal: string | null = null\n if (fid) { try { cidDecimal = BigInt(fid[2]).toString() } catch { } }\n return {\n cid: fid ? `${fid[1]}:${fid[2]}` : null,\n cidDecimal,\n cidUrl: cidDecimal ? `https://www.google.com/maps?cid=${cidDecimal}` : null,\n kgmid: kg ? kg[1] : null,\n lat: pin ? parseFloat(pin[1]) : null,\n lng: pin ? parseFloat(pin[2]) : null,\n }\n }\n\n private async extractOverview(page: Page): Promise<RawMapsOverview> {\n const raw = await page.evaluate((ratingAndCountSel: string) => {\n const f7 = document.querySelector(ratingAndCountSel) as HTMLElement | null\n const f7Text = f7?.innerText ?? ''\n const rating = f7Text.match(/^(\\d+\\.\\d+|\\d+)/)?.[1] ?? null\n const countFromF7 = f7Text.match(/\\(([\\d,]+)\\)/)?.[1]?.replace(/,/g, '') ?? null\n const countFromTab = (() => {\n const btn = Array.from(document.querySelectorAll('button[role=\"tab\"]')).find(el =>\n /review/i.test((el as HTMLElement).innerText)\n )\n const m = btn?.getAttribute('aria-label')?.match(/(\\d[\\d,]*)\\s*reviews?/i)\n ?? (btn as HTMLElement | null)?.innerText?.match(/(\\d[\\d,]*)\\s*reviews?/i)\n return m ? m[1].replace(/,/g, '') : null\n })()\n const phoneBtn = document.querySelector('button[data-item-id^=\"phone:tel:\"]')\n const phoneId = phoneBtn?.getAttribute('data-item-id') ?? null\n const phone = phoneId ? phoneId.replace('phone:tel:', '') : null\n const phoneAria = phoneBtn?.getAttribute('aria-label') ?? null\n const phoneDisplay = phoneAria ? phoneAria.replace(/^Phone:\\s*/i, '').trim() : null\n const addressBtn = document.querySelector('button[data-item-id=\"address\"]')\n const addressLabel = addressBtn?.getAttribute('aria-label') ?? null\n const address = addressLabel ? addressLabel.replace(/^Address:\\s*/i, '').trim() : null\n const hoursBtn = document.querySelector('button[data-item-id=\"oh\"]')\n const hoursLabel = hoursBtn?.getAttribute('aria-label') ?? null\n const hoursSummary = hoursLabel ? hoursLabel.replace(/\\s*·\\s*See more hours.*$/i, '').trim() : null\n const websiteEl = document.querySelector('a[data-item-id=\"authority\"]') as HTMLAnchorElement | null\n const plusBtn = document.querySelector('button[data-item-id=\"oloc\"]')\n const plusAria = plusBtn?.getAttribute('aria-label') ?? null\n const plusCode = plusAria ? plusAria.replace(/^Plus code:\\s*/i, '').trim() : null\n const bookingEl = document.querySelector('a[data-item-id^=\"action:\"]') as HTMLAnchorElement | null\n const catBtn = document.querySelector('button[jsaction*=\"category\"]')\n const h1El = document.querySelector('h1')\n const catRaw = (catBtn as HTMLElement | null)?.innerText ?? ''\n const category = catRaw.split('\\n').map((s: string) => s.trim()).find(Boolean) ?? null\n return {\n name: (h1El as HTMLElement | null)?.innerText?.trim() ?? null,\n rating,\n reviewCount: countFromF7 ?? countFromTab,\n category,\n address,\n hoursSummary,\n phone,\n phoneDisplay,\n website: websiteEl?.href ?? null,\n plusCode,\n bookingUrl: bookingEl?.href ?? null,\n }\n }, MapsSelectors.ratingAndCount)\n\n const result = RawMapsOverviewSchema.safeParse(raw)\n if (!result.success) {\n console.warn('[MapsExtractor] overview parse failed', result.error.flatten())\n return { name: null, rating: null, reviewCount: null, category: null, address: null,\n hoursSummary: null, phone: null, phoneDisplay: null, website: null, plusCode: null, bookingUrl: null }\n }\n return result.data\n }\n\n private async extractFullHours(page: Page): Promise<MapsHoursDay[]> {\n const hoursBtn = await page.$('button[data-item-id=\"oh\"]')\n if (hoursBtn) {\n await hoursBtn.click().catch(() => {})\n await page.waitForSelector(\n `${MapsSelectors.hoursTable} tr, ${MapsSelectors.hoursTableAlt} tr`,\n { timeout: 3000 },\n ).catch(() => {})\n }\n\n const raw = await page.evaluate((sel: { hoursTable: string; hoursTableAlt: string }) => {\n const seen = new Set<string>()\n const rows: { day: string; hours: string }[] = []\n document.querySelectorAll(`${sel.hoursTable} tr, ${sel.hoursTableAlt} tr`).forEach(tr => {\n const cells = tr.querySelectorAll('td')\n const day = (cells[0] as HTMLElement | undefined)?.innerText?.replace(/\\n/g, ' ').trim()\n const hrs = (cells[1] as HTMLElement | undefined)?.innerText?.replace(/\\n/g, ' ').trim()\n if (day && hrs && !seen.has(day)) {\n seen.add(day)\n rows.push({ day, hours: hrs })\n }\n })\n return rows\n }, { hoursTable: MapsSelectors.hoursTable, hoursTableAlt: MapsSelectors.hoursTableAlt })\n\n const result = z.array(RawMapsHoursRowSchema).safeParse(raw)\n if (!result.success) {\n console.warn('[MapsExtractor] hours parse failed', result.error.flatten())\n return []\n }\n return result.data\n }\n\n private async extractReviewStats(page: Page): Promise<RawMapsReviewStats> {\n const raw = await page.evaluate((reviewCardSel: string) => {\n const histogram: Array<{ stars: number; count: string }> = []\n document.querySelectorAll('[aria-label*=\"stars,\"], [aria-label*=\"star,\"]').forEach(el => {\n const label = el.getAttribute('aria-label') ?? ''\n const m = label.match(/^(\\d+)\\s+stars?,\\s*([\\d,]+)\\s+reviews?/i)\n if (m) histogram.push({ stars: parseInt(m[1], 10), count: m[2].replace(/,/g, '') })\n })\n const firstReview = document.querySelector(reviewCardSel)\n const topics = Array.from(document.querySelectorAll('button[jsaction]'))\n .filter(btn => {\n if (!firstReview) return false\n return btn.compareDocumentPosition(firstReview) & Node.DOCUMENT_POSITION_FOLLOWING\n })\n .flatMap(btn => {\n const lines = ((btn as HTMLElement).innerText?.trim() ?? '').split(/\\n/)\n if (lines.length === 2 && /^\\d+$/.test(lines[1].trim()) && lines[0].length > 2) {\n return [{ label: lines[0].trim(), count: lines[1].trim() }]\n }\n return []\n })\n return { reviewHistogram: histogram, reviewTopics: topics }\n }, MapsSelectors.reviewCard)\n\n const result = RawMapsReviewStatsSchema.safeParse(raw)\n if (!result.success) {\n console.warn('[MapsExtractor] review stats parse failed', result.error.flatten())\n return { reviewHistogram: [], reviewTopics: [] }\n }\n return result.data\n }\n\n private async extractAbout(page: Page): Promise<Array<{ section: string; attribute: string }>> {\n const raw = await page.evaluate(() => {\n const results: Array<{ section: string; attribute: string }> = []\n const seen = new Set<string>()\n const main = document.querySelector('[role=\"main\"]')\n if (!main) return results\n let currentSection = 'General'\n main.querySelectorAll('h2, li').forEach(el => {\n if (el.tagName === 'H2') {\n currentSection = (el as HTMLElement).innerText?.trim() || currentSection\n } else if (el.tagName === 'LI') {\n const text = (el as HTMLElement).innerText?.replace(/^[\\s\\S]*?\\n/, '').trim()\n const key = `${currentSection}|${text}`\n if (text && !seen.has(key)) { seen.add(key); results.push({ section: currentSection, attribute: text }) }\n }\n })\n main.querySelectorAll('[href*=\"businessownership\"], [data-item-id^=\"place-info-links:\"]').forEach(el => {\n const text = (el as HTMLElement).innerText?.trim()\n const key = `Ownership|${text}`\n if (text && !seen.has(key)) { seen.add(key); results.push({ section: 'Ownership', attribute: text }) }\n })\n return results\n })\n\n const result = z.array(RawMapsAboutAttributeSchema).safeParse(raw)\n if (!result.success) {\n console.warn('[MapsExtractor] about parse failed', result.error.flatten())\n return []\n }\n return result.data\n }\n}\n","import { Hono } from 'hono'\nimport { ZodError } from 'zod'\nimport { capturePageSnapshots } from '../serp-intelligence/page-snapshot-extractor.js'\nimport {\n captureSerpIntelligenceSnapshot,\n SerpIntelligenceCaptureError,\n} from '../serp-intelligence/serp-capture-service.js'\nimport {\n SerpIntelligenceCaptureBodySchema,\n SerpIntelligencePageSnapshotsBodySchema,\n} from '../serp-intelligence/schemas.js'\nimport { createApiKeyAuth, type ApiKeyEnv } from './api-auth.js'\nimport {\n checkRateLimit,\n countActiveJobsForUser,\n creditMc,\n debitMc,\n logRequestEvent,\n} from './db.js'\nimport { MC_COSTS, insufficientBalanceResponse, LedgerOperation } from './rates.js'\n\ntype Env = ApiKeyEnv\n\ntype JsonResponder = {\n json: (body: unknown, status?: number, headers?: Record<string, string>) => Response\n}\n\nconst SERP_INTELLIGENCE_RATE_LIMIT = 60\nconst SERP_INTELLIGENCE_RATE_WINDOW_SECONDS = 60\nconst POST_CAPTURE_ROUTE_LABEL = 'POST /capture'\nconst POST_PAGE_SNAPSHOTS_ROUTE_LABEL = 'POST /page-snapshots'\n\nexport const serpIntelligenceApp = new Hono<Env>()\n\nserpIntelligenceApp.use('*', createApiKeyAuth<Env>())\n\nfunction structuredError(input: {\n error_code: string\n error_type: string\n message: string\n retryable: boolean\n attempts?: unknown[]\n}) {\n return {\n error_code: input.error_code,\n error_type: input.error_type,\n message: input.message,\n retryable: input.retryable,\n attempts: input.attempts ?? [],\n }\n}\n\nfunction formatZodError(error: ZodError) {\n return structuredError({\n error_code: 'invalid_request',\n error_type: 'validation_error',\n message: error.issues[0]?.message ?? 'Invalid request',\n retryable: false,\n })\n}\n\nasync function enforceSerpIntelligenceRateLimit(\n c: JsonResponder,\n userId: number | bigint,\n): Promise<Response | null> {\n const result = await checkRateLimit(\n 'serp_intelligence',\n String(userId),\n SERP_INTELLIGENCE_RATE_LIMIT,\n SERP_INTELLIGENCE_RATE_WINDOW_SECONDS,\n )\n if (result.allowed) return null\n\n return c.json(\n structuredError({\n error_code: 'rate_limited',\n error_type: 'rate_limit',\n message: 'Too many SERP Intelligence requests. Try again shortly.',\n retryable: true,\n }),\n 429,\n { 'Retry-After': String(result.resetSeconds) },\n )\n}\n\nasync function enforceSerpIntelligenceConcurrency(user: Env['Variables']['user']): Promise<Response | null> {\n const active = await countActiveJobsForUser(user.id)\n const limit = 1 + (user.extra_concurrency_slots ?? 0)\n if (active < limit) return null\n\n return new Response(\n JSON.stringify(structuredError({\n error_code: 'concurrency_limit_exceeded',\n error_type: 'concurrency_limit',\n message: `You have ${active} active job${active !== 1 ? 's' : ''}. Your account allows ${limit} concurrent job${limit !== 1 ? 's' : ''}.`,\n retryable: true,\n })),\n {\n status: 429,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': '30',\n },\n },\n )\n}\n\nfunction pageSnapshotTargetsFromBody(body: ReturnType<typeof SerpIntelligencePageSnapshotsBodySchema.parse>) {\n return body.targets?.length\n ? body.targets\n : body.urls.map((url) => ({\n url,\n sourceKind: 'configured_target' as const,\n sourcePosition: null,\n }))\n}\n\nserpIntelligenceApp.post('/capture', async (c) => {\n void POST_CAPTURE_ROUTE_LABEL\n const user = c.get('user')\n const raw = await c.req.json().catch(() => ({}))\n const parsed = SerpIntelligenceCaptureBodySchema.safeParse(raw)\n if (!parsed.success) return c.json(formatZodError(parsed.error), 400)\n\n const limited = await enforceSerpIntelligenceRateLimit(c, user.id)\n if (limited) return limited\n\n const concurrencyLimited = await enforceSerpIntelligenceConcurrency(user)\n if (concurrencyLimited) return concurrencyLimited\n\n const cost = MC_COSTS.serp\n const { ok, balance_mc } = await debitMc(\n user.id,\n cost,\n LedgerOperation.SERP,\n parsed.data.query,\n )\n if (!ok) return c.json(insufficientBalanceResponse(balance_mc, cost), 402)\n\n try {\n const result = await captureSerpIntelligenceSnapshot(parsed.data, {\n kernelApiKey: process.env.KERNEL_API_KEY?.trim(),\n kernelProxyId: process.env.KERNEL_PROXY_ID?.trim(),\n signal: c.req.raw.signal,\n billing: { creditsUsed: cost / 1_000 },\n })\n await logRequestEvent({\n userId: user.id,\n source: 'serp_intelligence_capture',\n status: 'done',\n query: parsed.data.query,\n location: parsed.data.location,\n resultCount: result.harvestResult.organicResults.length,\n result,\n })\n return c.json(result)\n } catch (error) {\n await creditMc(user.id, cost, LedgerOperation.REFUND, 'failed serp intelligence capture')\n const body = error instanceof SerpIntelligenceCaptureError\n ? error.toJSON()\n : structuredError({\n error_code: 'capture_failed',\n error_type: 'capture_error',\n message: error instanceof Error ? error.message : String(error),\n retryable: true,\n })\n await logRequestEvent({\n userId: user.id,\n source: 'serp_intelligence_capture',\n status: 'failed',\n query: parsed.data.query,\n location: parsed.data.location,\n error: 'message' in body && typeof body.message === 'string' ? body.message : 'SERP Intelligence capture failed',\n })\n const status = error instanceof SerpIntelligenceCaptureError ? error.httpStatus : 500\n return c.json(body, status as 500)\n }\n})\n\nserpIntelligenceApp.post('/page-snapshots', async (c) => {\n void POST_PAGE_SNAPSHOTS_ROUTE_LABEL\n const user = c.get('user')\n const raw = await c.req.json().catch(() => ({}))\n const parsed = SerpIntelligencePageSnapshotsBodySchema.safeParse(raw)\n if (!parsed.success) return c.json(formatZodError(parsed.error), 400)\n\n const limited = await enforceSerpIntelligenceRateLimit(c, user.id)\n if (limited) return limited\n\n const concurrencyLimited = await enforceSerpIntelligenceConcurrency(user)\n if (concurrencyLimited) return concurrencyLimited\n\n const targets = pageSnapshotTargetsFromBody(parsed.data)\n const cost = targets.length * MC_COSTS.page_scrape\n const { ok, balance_mc } = await debitMc(\n user.id,\n cost,\n LedgerOperation.EXTRACT_URL,\n `serp intelligence page snapshots: ${targets.length}`,\n )\n if (!ok) return c.json(insufficientBalanceResponse(balance_mc, cost), 402)\n\n try {\n const result = await capturePageSnapshots(targets, {\n kernelApiKey: process.env.KERNEL_API_KEY?.trim(),\n timeoutMs: parsed.data.timeoutMs,\n maxConcurrency: parsed.data.maxConcurrency,\n debug: parsed.data.debug,\n })\n await logRequestEvent({\n userId: user.id,\n source: 'serp_intelligence_page_snapshots',\n status: 'done',\n query: targets[0]?.url ?? 'page snapshots',\n resultCount: result.pageSnapshotArtifacts.length,\n result,\n })\n return c.json(result)\n } catch (error) {\n await creditMc(user.id, cost, LedgerOperation.REFUND, 'failed serp intelligence page snapshots')\n const body = structuredError({\n error_code: 'page_snapshot_failed',\n error_type: 'capture_error',\n message: error instanceof Error ? error.message : String(error),\n retryable: true,\n })\n await logRequestEvent({\n userId: user.id,\n source: 'serp_intelligence_page_snapshots',\n status: 'failed',\n query: targets[0]?.url ?? 'page snapshots',\n error: body.message,\n })\n return c.json(body, 500)\n }\n})\n","import { createHash } from 'node:crypto'\nimport pLimit from 'p-limit'\nimport { extractKpo } from '../api/kpo-extractor.js'\nimport { validatePublicHttpUrl } from '../api/url-utils.js'\nimport type { SerpPageSnapshotCapture } from './schemas.js'\nimport { SerpPageSnapshotSourceKindValues } from './schemas.js'\n\nexport type SerpPageSnapshotSourceKind = typeof SerpPageSnapshotSourceKindValues[number]\n\nexport interface PageSnapshotExtractorTarget {\n url: string\n sourceKind?: SerpPageSnapshotSourceKind\n sourcePosition?: number | null\n}\n\nexport interface PageSnapshotExtractorOptions {\n kernelApiKey?: string\n timeoutMs?: number\n maxConcurrency?: number\n debug?: boolean\n includeContent?: boolean\n maxContentChars?: number\n}\n\nexport interface PageSnapshotDiagnostics {\n requestedAt: string\n completedAt: string\n durationMs: number\n warnings: string[]\n urlValidation: {\n passed: boolean\n error: string | null\n }\n browserFallback: {\n configured: boolean\n used: boolean\n boundedByTimeoutMs: number\n }\n content: {\n htmlIncluded: boolean\n markdownIncluded: boolean\n textIncluded: boolean\n maxContentChars: number\n truncated: boolean\n }\n}\n\nexport interface PageSnapshotContentFields {\n html: string | null\n markdown: string | null\n text: string | null\n}\n\nexport interface PageSnapshotHashes {\n htmlSha256: string | null\n markdownSha256: string | null\n textSha256: string | null\n}\n\nexport interface PageSnapshotMetrics {\n wordCount: number\n internalLinkCount: number\n externalLinkCount: number\n totalLinkCount: number\n}\n\nexport type PageSnapshotCaptureResult = SerpPageSnapshotCapture & {\n schemaTypes: string[]\n wordCount: number\n linkCounts: PageSnapshotMetrics\n hashes: PageSnapshotHashes\n diagnostics: PageSnapshotDiagnostics\n content?: PageSnapshotContentFields\n}\n\nexport interface PageSnapshotsCaptureResult {\n pageSnapshotArtifacts: PageSnapshotCaptureResult[]\n attempts: {\n attemptNumber: number\n outcome: 'page_captured' | 'page_failed'\n startedAt: string\n completedAt: string\n durationMs: number\n problemCode?: string\n message?: string\n }[]\n diagnostics: {\n requestedCount: number\n capturedCount: number\n failedCount: number\n maxConcurrency: number\n timeoutMs: number\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 15_000\nconst DEFAULT_MAX_CONCURRENCY = 2\nconst DEFAULT_MAX_CONTENT_CHARS = 250_000\n\nfunction sha256(value: string | null): string | null {\n if (!value) return null\n return createHash('sha256').update(value).digest('hex')\n}\n\nfunction countWords(markdown: string): number {\n const matches = markdown.trim().match(/\\b[\\p{L}\\p{N}][\\p{L}\\p{N}'-]*\\b/gu)\n return matches?.length ?? 0\n}\n\nfunction stripMarkdown(markdown: string): string {\n return markdown\n .replace(/```[\\s\\S]*?```/g, ' ')\n .replace(/`([^`]*)`/g, '$1')\n .replace(/[#>*_\\-[\\]()!]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction truncateContent(value: string, maxContentChars: number): { value: string; truncated: boolean } {\n if (value.length <= maxContentChars) return { value, truncated: false }\n return { value: value.slice(0, maxContentChars), truncated: true }\n}\n\nfunction getSchemaTypes(schema: unknown[], kpoSchemaTypes: string[]): string[] {\n const fromSchema = schema.flatMap((node) => {\n if (!node || typeof node !== 'object') return []\n const graph = '@graph' in node && Array.isArray(node['@graph' as keyof typeof node])\n ? node['@graph' as keyof typeof node] as unknown[]\n : [node]\n return graph.flatMap((entry) => {\n if (!entry || typeof entry !== 'object' || !('@type' in entry)) return []\n const value = entry['@type' as keyof typeof entry]\n return Array.isArray(value) ? value : [value]\n })\n })\n\n return [...new Set([...kpoSchemaTypes, ...fromSchema].filter((value): value is string => typeof value === 'string' && value.length > 0))]\n}\n\nfunction countLinks(html: string, baseUrl: string): PageSnapshotMetrics {\n let internalLinkCount = 0\n let externalLinkCount = 0\n\n const base = new URL(baseUrl)\n for (const match of html.matchAll(/<a\\s[^>]*href\\s*=\\s*[\"']([^\"']+)[\"'][^>]*>/gi)) {\n try {\n const href = new URL(match[1], base)\n if (href.protocol !== 'http:' && href.protocol !== 'https:') continue\n if (href.hostname.replace(/^www\\./, '') === base.hostname.replace(/^www\\./, '')) {\n internalLinkCount += 1\n } else {\n externalLinkCount += 1\n }\n } catch {\n continue\n }\n }\n\n return {\n wordCount: 0,\n internalLinkCount,\n externalLinkCount,\n totalLinkCount: internalLinkCount + externalLinkCount,\n }\n}\n\nfunction withTimeout<T>(operation: Promise<T>, timeoutMs: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`Page snapshot timed out after ${timeoutMs}ms`)), timeoutMs)\n operation.then(\n (value) => {\n clearTimeout(timer)\n resolve(value)\n },\n (error: unknown) => {\n clearTimeout(timer)\n reject(error)\n },\n )\n })\n}\n\nfunction normalizeTimeoutMs(timeoutMs: number | undefined): number {\n if (!Number.isFinite(timeoutMs)) return DEFAULT_TIMEOUT_MS\n return Math.max(1_000, Math.min(60_000, Math.trunc(timeoutMs ?? DEFAULT_TIMEOUT_MS)))\n}\n\nfunction normalizeMaxConcurrency(maxConcurrency: number | undefined): number {\n if (!Number.isFinite(maxConcurrency)) return DEFAULT_MAX_CONCURRENCY\n return Math.max(1, Math.min(5, Math.trunc(maxConcurrency ?? DEFAULT_MAX_CONCURRENCY)))\n}\n\nfunction createFailureSnapshot(input: {\n requestedUrl: string\n sourceKind: SerpPageSnapshotSourceKind\n sourcePosition: number | null\n startedAt: string\n completedAt: string\n durationMs: number\n timeoutMs: number\n errorCode: string\n errorMessage: string\n validationPassed: boolean\n validationError: string | null\n kernelConfigured: boolean\n}): PageSnapshotCaptureResult {\n const diagnostics: PageSnapshotDiagnostics = {\n requestedAt: input.startedAt,\n completedAt: input.completedAt,\n durationMs: input.durationMs,\n warnings: [],\n urlValidation: {\n passed: input.validationPassed,\n error: input.validationError,\n },\n browserFallback: {\n configured: input.kernelConfigured,\n used: false,\n boundedByTimeoutMs: input.timeoutMs,\n },\n content: {\n htmlIncluded: false,\n markdownIncluded: false,\n textIncluded: false,\n maxContentChars: 0,\n truncated: false,\n },\n }\n\n return {\n url: input.requestedUrl,\n requestedUrl: input.requestedUrl,\n finalUrl: null,\n sourceKind: input.sourceKind,\n sourcePosition: input.sourcePosition,\n status: 'failed',\n fetchedVia: null,\n httpStatus: null,\n contentType: null,\n title: null,\n canonicalUrl: null,\n metaDescription: null,\n headings: [],\n artifact: {\n htmlBlobUrl: null,\n textBlobUrl: null,\n markdownBlobUrl: null,\n screenshotBlobUrl: null,\n contentSha256: null,\n capturedAt: null,\n },\n error: {\n code: input.errorCode,\n message: input.errorMessage,\n },\n schemaTypes: [],\n wordCount: 0,\n linkCounts: {\n wordCount: 0,\n internalLinkCount: 0,\n externalLinkCount: 0,\n totalLinkCount: 0,\n },\n hashes: {\n htmlSha256: null,\n markdownSha256: null,\n textSha256: null,\n },\n diagnostics,\n }\n}\n\nexport async function capturePageSnapshot(\n target: string | PageSnapshotExtractorTarget,\n options: PageSnapshotExtractorOptions = {},\n): Promise<PageSnapshotCaptureResult> {\n const startedAtDate = new Date()\n const startedAt = startedAtDate.toISOString()\n const timeoutMs = normalizeTimeoutMs(options.timeoutMs)\n const maxContentChars = Math.max(1_000, Math.trunc(options.maxContentChars ?? DEFAULT_MAX_CONTENT_CHARS))\n const requestedUrl = typeof target === 'string' ? target : target.url\n const sourceKind = typeof target === 'string' ? 'configured_target' : target.sourceKind ?? 'configured_target'\n const sourcePosition = typeof target === 'string' ? null : target.sourcePosition ?? null\n const kernelApiKey = options.kernelApiKey?.trim()\n const kernelConfigured = Boolean(kernelApiKey)\n\n const checked = await validatePublicHttpUrl(requestedUrl, { field: 'URL' })\n if (checked.error || !checked.parsed) {\n const completedAtDate = new Date()\n return createFailureSnapshot({\n requestedUrl,\n sourceKind,\n sourcePosition,\n startedAt,\n completedAt: completedAtDate.toISOString(),\n durationMs: completedAtDate.getTime() - startedAtDate.getTime(),\n timeoutMs,\n errorCode: 'invalid_url',\n errorMessage: checked.error ?? 'Invalid URL',\n validationPassed: false,\n validationError: checked.error ?? 'Invalid URL',\n kernelConfigured,\n })\n }\n\n try {\n const extraction = await withTimeout(\n extractKpo({ url: checked.parsed.href, kernelApiKey }),\n timeoutMs,\n )\n const completedAtDate = new Date()\n const completedAt = completedAtDate.toISOString()\n const durationMs = completedAtDate.getTime() - startedAtDate.getTime()\n const text = stripMarkdown(extraction.bodyMarkdown)\n const htmlContent = truncateContent(extraction.bodyHtml, maxContentChars)\n const markdownContent = truncateContent(extraction.bodyMarkdown, maxContentChars)\n const textContent = truncateContent(text, maxContentChars)\n const schemaTypes = getSchemaTypes(extraction.schema, extraction.kpo.schemaTypes)\n const metrics = countLinks(extraction.bodyHtml, extraction.url)\n const wordCount = countWords(extraction.bodyMarkdown)\n const contentSha256 = sha256(extraction.bodyMarkdown)\n const canonicalUrl = extraction.kpo.canonicalUrl\n ? new URL(extraction.kpo.canonicalUrl, extraction.url).href\n : null\n const contentIncluded = Boolean(options.includeContent)\n const truncated = htmlContent.truncated || markdownContent.truncated || textContent.truncated\n\n const diagnostics: PageSnapshotDiagnostics = {\n requestedAt: startedAt,\n completedAt,\n durationMs,\n warnings: [\n ...(extraction.fetchedVia === 'headless' ? ['plain fetch failed or was insufficient; headless browser fallback was used'] : []),\n ...(truncated ? ['content fields were truncated to maxContentChars'] : []),\n ],\n urlValidation: {\n passed: true,\n error: null,\n },\n browserFallback: {\n configured: kernelConfigured,\n used: extraction.fetchedVia === 'headless',\n boundedByTimeoutMs: timeoutMs,\n },\n content: {\n htmlIncluded: contentIncluded,\n markdownIncluded: contentIncluded,\n textIncluded: contentIncluded,\n maxContentChars,\n truncated,\n },\n }\n\n return {\n url: checked.parsed.href,\n requestedUrl: checked.parsed.href,\n finalUrl: extraction.url,\n sourceKind,\n sourcePosition,\n status: 'captured',\n fetchedVia: extraction.fetchedVia,\n httpStatus: null,\n contentType: 'text/html',\n title: extraction.title,\n canonicalUrl,\n metaDescription: extraction.meta.description ?? extraction.meta['og:description'] ?? extraction.meta['twitter:description'] ?? null,\n headings: extraction.headings,\n artifact: {\n htmlBlobUrl: null,\n textBlobUrl: null,\n markdownBlobUrl: null,\n screenshotBlobUrl: null,\n contentSha256,\n capturedAt: completedAt,\n },\n error: null,\n schemaTypes,\n wordCount,\n linkCounts: {\n ...metrics,\n wordCount,\n },\n hashes: {\n htmlSha256: sha256(extraction.bodyHtml),\n markdownSha256: contentSha256,\n textSha256: sha256(text),\n },\n diagnostics,\n ...(contentIncluded\n ? {\n content: {\n html: htmlContent.value,\n markdown: markdownContent.value,\n text: textContent.value,\n },\n }\n : {}),\n }\n } catch (error) {\n const completedAtDate = new Date()\n const message = error instanceof Error ? error.message : String(error)\n const isTimeout = message.toLowerCase().includes('timed out')\n\n return createFailureSnapshot({\n requestedUrl: checked.parsed.href,\n sourceKind,\n sourcePosition,\n startedAt,\n completedAt: completedAtDate.toISOString(),\n durationMs: completedAtDate.getTime() - startedAtDate.getTime(),\n timeoutMs,\n errorCode: isTimeout ? 'timeout' : 'capture_failed',\n errorMessage: message,\n validationPassed: true,\n validationError: null,\n kernelConfigured,\n })\n }\n}\n\nexport async function capturePageSnapshots(\n targets: Array<string | PageSnapshotExtractorTarget>,\n options: PageSnapshotExtractorOptions = {},\n): Promise<PageSnapshotsCaptureResult> {\n const timeoutMs = normalizeTimeoutMs(options.timeoutMs)\n const maxConcurrency = normalizeMaxConcurrency(options.maxConcurrency)\n const limit = pLimit(maxConcurrency)\n\n const pageSnapshotArtifacts = await Promise.all(\n targets.map((target) => limit(() => capturePageSnapshot(target, { ...options, timeoutMs }))),\n )\n\n const attempts = pageSnapshotArtifacts.map((artifact, index) => ({\n attemptNumber: index + 1,\n outcome: artifact.status === 'captured' ? 'page_captured' as const : 'page_failed' as const,\n startedAt: artifact.diagnostics.requestedAt,\n completedAt: artifact.diagnostics.completedAt,\n durationMs: artifact.diagnostics.durationMs,\n ...(artifact.error ? { problemCode: artifact.error.code, message: artifact.error.message } : {}),\n }))\n\n const capturedCount = pageSnapshotArtifacts.filter((artifact) => artifact.status === 'captured').length\n\n return {\n pageSnapshotArtifacts,\n attempts,\n diagnostics: {\n requestedCount: targets.length,\n capturedCount,\n failedCount: targets.length - capturedCount,\n maxConcurrency,\n timeoutMs,\n },\n }\n}\n","import { z } from 'zod'\n\nexport const SerpIntelligenceDeviceValues = ['desktop', 'mobile'] as const\nexport const SerpIntelligenceProxyModeValues = ['location', 'configured', 'none'] as const\nexport const SerpIntelligenceAttemptOutcomeValues = [\n 'paa_found',\n 'paa_partial',\n 'no_paa',\n 'serp_only',\n 'captcha',\n 'request_aborted',\n 'timeout',\n 'location_mismatch',\n 'mcp_unavailable',\n 'error',\n] as const\nexport const SerpIntelligenceLocalizationStatusValues = [\n 'not_requested',\n 'matched',\n 'mismatch',\n 'unknown',\n] as const\nexport const SerpPageSnapshotSourceKindValues = [\n 'organic',\n 'ai_citation',\n 'local_pack_website',\n 'configured_target',\n 'site_subject',\n] as const\nexport const SerpPageFetchStatusValues = ['captured', 'skipped', 'failed'] as const\nexport const SerpPageFetchedViaValues = ['fetch', 'headless', 'browser', 'mcp'] as const\n\nconst HostnameSuffixPattern = /(^|\\.)localhost$/i\nconst Ipv4Pattern = /^\\d{1,3}(?:\\.\\d{1,3}){3}$/\n\nfunction isPrivateIpv4(hostname: string): boolean {\n if (!Ipv4Pattern.test(hostname)) return false\n\n const parts = hostname.split('.').map((part) => Number(part))\n if (parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return true\n\n const [first, second] = parts\n if (first === 10 || first === 127 || first === 0) return true\n if (first === 169 && second === 254) return true\n if (first === 172 && second >= 16 && second <= 31) return true\n if (first === 192 && second === 168) return true\n if (first === 100 && second >= 64 && second <= 127) return true\n return false\n}\n\nfunction isPrivateIpv6(hostname: string): boolean {\n const normalized = hostname.replace(/^\\[/, '').replace(/\\]$/, '').toLowerCase()\n return normalized === '::1'\n || normalized === '0:0:0:0:0:0:0:1'\n || normalized.startsWith('fc')\n || normalized.startsWith('fd')\n || normalized.startsWith('fe80:')\n}\n\nfunction isPublicHttpUrl(value: string): boolean {\n try {\n const parsed = new URL(value)\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return false\n\n const hostname = parsed.hostname.toLowerCase()\n if (!hostname || HostnameSuffixPattern.test(hostname)) return false\n if (isPrivateIpv4(hostname) || isPrivateIpv6(hostname)) return false\n\n return true\n } catch {\n return false\n }\n}\n\nexport const SerpIntelligencePublicHttpUrlSchema = z.string()\n .url()\n .refine(isPublicHttpUrl, 'url must be a public HTTP or HTTPS URL')\n\nexport const SerpIntelligenceCaptureBodySchema = z.object({\n query: z.string().trim().min(1, 'query is required'),\n location: z.string().trim().min(1).optional(),\n gl: z.string().trim().length(2).default('us'),\n hl: z.string().trim().length(2).default('en'),\n device: z.enum(SerpIntelligenceDeviceValues).default('desktop'),\n proxyMode: z.enum(SerpIntelligenceProxyModeValues).default('location'),\n proxyZip: z.string().regex(/^\\d{5}$/).optional(),\n pages: z.number().int().min(1).max(2).default(1),\n debug: z.boolean().default(false),\n includePageSnapshots: z.boolean().default(false),\n pageSnapshotLimit: z.number().int().min(0).max(10).default(0),\n}).strict()\n\nexport const SerpIntelligencePageSnapshotRequestSchema = z.object({\n url: SerpIntelligencePublicHttpUrlSchema,\n sourceKind: z.enum(SerpPageSnapshotSourceKindValues).default('configured_target'),\n sourcePosition: z.number().int().min(1).optional(),\n}).strict()\n\nexport const SerpIntelligencePageSnapshotsBodySchema = z.object({\n urls: z.array(SerpIntelligencePublicHttpUrlSchema).min(1).max(25),\n targets: z.array(SerpIntelligencePageSnapshotRequestSchema).min(1).max(25).optional(),\n maxConcurrency: z.number().int().min(1).max(5).default(2),\n timeoutMs: z.number().int().min(1_000).max(60_000).default(15_000),\n debug: z.boolean().default(false),\n}).strict()\n\nexport const SerpIntelligenceAICitationSchema = z.object({\n text: z.string(),\n href: z.string(),\n}).strict()\n\nexport const SerpIntelligenceOrganicResultSchema = z.object({\n position: z.number().int().min(1),\n title: z.string(),\n url: z.string(),\n domain: z.string(),\n cite: z.string().nullable(),\n snippet: z.string().nullable(),\n isRedditStyle: z.boolean(),\n inlineRating: z.object({\n value: z.string(),\n count: z.string(),\n }).strict().nullable(),\n}).strict()\n\nexport const SerpIntelligenceLocationEvidenceSchema = z.object({\n status: z.enum(SerpIntelligenceLocalizationStatusValues),\n expected: z.object({\n city: z.string(),\n regionCode: z.string().nullable(),\n canonicalLocation: z.string(),\n }).strict().nullable(),\n candidates: z.array(z.object({\n city: z.string(),\n regionCode: z.string(),\n count: z.number().int().min(0),\n examples: z.array(z.string()),\n }).strict()),\n}).strict()\n\nexport const SerpIntelligenceHarvestResultSchema = z.object({\n seed: z.string(),\n location: z.string().nullable(),\n extractedAt: z.string(),\n totalQuestions: z.number().int().min(0),\n surface: z.enum(['web', 'aim', 'unknown']),\n aiOverview: z.object({\n detected: z.boolean(),\n text: z.string().nullable(),\n citations: z.array(SerpIntelligenceAICitationSchema),\n expanded: z.boolean().optional(),\n fullyExpanded: z.boolean().optional(),\n sections: z.array(z.string()).optional(),\n }).strict(),\n aiMode: z.object({\n detected: z.boolean(),\n text: z.string().nullable(),\n citations: z.array(SerpIntelligenceAICitationSchema),\n }).strict(),\n tree: z.array(z.unknown()),\n flat: z.array(z.unknown()),\n videos: z.array(z.unknown()),\n forums: z.array(z.unknown()),\n organicResults: z.array(SerpIntelligenceOrganicResultSchema),\n localPack: z.array(z.unknown()),\n entityIds: z.object({\n entities: z.array(z.object({\n name: z.string(),\n kgId: z.string().nullable(),\n cid: z.string().nullable(),\n gcid: z.string().nullable(),\n }).strict()),\n kgIds: z.array(z.string()),\n cids: z.array(z.string()),\n gcids: z.array(z.string()),\n }).strict(),\n stats: z.object({\n seed: z.string(),\n totalQuestions: z.number().int().min(0),\n maxDepthReached: z.number().int().min(0),\n durationMs: z.number().min(0),\n errorCount: z.number().int().min(0),\n }).strict(),\n diagnostics: z.object({\n completionStatus: z.enum(['paa_found', 'no_paa', 'serp_only']),\n problem: z.null(),\n warnings: z.array(z.unknown()).optional(),\n debug: z.object({\n locationEvidence: SerpIntelligenceLocationEvidenceSchema.optional(),\n }).passthrough().optional(),\n }).passthrough(),\n whatPeopleSaying: z.array(z.unknown()),\n}).strict()\n\nexport const SerpIntelligenceCaptureAttemptSchema = z.object({\n attemptNumber: z.number().int().min(1),\n outcome: z.enum(SerpIntelligenceAttemptOutcomeValues),\n startedAt: z.string().optional(),\n completedAt: z.string().optional(),\n durationMs: z.number().min(0).optional(),\n problemCode: z.string().optional(),\n message: z.string().optional(),\n kernelSessionId: z.string().nullable().optional(),\n cleanupSucceeded: z.boolean().nullable().optional(),\n}).strict()\n\nexport const SerpPageSnapshotCaptureSchema = z.object({\n url: SerpIntelligencePublicHttpUrlSchema,\n requestedUrl: SerpIntelligencePublicHttpUrlSchema,\n finalUrl: SerpIntelligencePublicHttpUrlSchema.nullable(),\n sourceKind: z.enum(SerpPageSnapshotSourceKindValues),\n sourcePosition: z.number().int().min(1).nullable(),\n status: z.enum(SerpPageFetchStatusValues),\n fetchedVia: z.enum(SerpPageFetchedViaValues).nullable(),\n httpStatus: z.number().int().min(100).max(599).nullable(),\n contentType: z.string().nullable(),\n title: z.string().nullable(),\n canonicalUrl: SerpIntelligencePublicHttpUrlSchema.nullable(),\n metaDescription: z.string().nullable(),\n headings: z.array(z.object({\n level: z.number().int().min(1).max(6),\n text: z.string(),\n }).strict()).default([]),\n artifact: z.object({\n htmlBlobUrl: z.string().url().nullable(),\n textBlobUrl: z.string().url().nullable(),\n markdownBlobUrl: z.string().url().nullable(),\n screenshotBlobUrl: z.string().url().nullable(),\n contentSha256: z.string().nullable(),\n capturedAt: z.string().nullable(),\n }).strict(),\n error: z.object({\n code: z.string(),\n message: z.string(),\n }).strict().nullable(),\n}).strict()\n\nexport const SerpIntelligenceCaptureResponseSchema = z.object({\n harvestResult: SerpIntelligenceHarvestResultSchema,\n attempts: z.array(SerpIntelligenceCaptureAttemptSchema),\n locationEvidence: SerpIntelligenceLocationEvidenceSchema.nullable(),\n pageSnapshotArtifacts: z.array(SerpPageSnapshotCaptureSchema),\n billing: z.object({\n creditsUsed: z.number().min(0).optional(),\n requestId: z.string().optional(),\n jobId: z.string().optional(),\n }).strict().optional(),\n}).strict()\n\nexport const SerpIntelligencePageSnapshotsResponseSchema = z.object({\n pageSnapshotArtifacts: z.array(SerpPageSnapshotCaptureSchema),\n attempts: z.array(SerpIntelligenceCaptureAttemptSchema).default([]),\n}).strict()\n\nexport type SerpIntelligenceCaptureBody = z.infer<typeof SerpIntelligenceCaptureBodySchema>\nexport type SerpIntelligencePageSnapshotsBody = z.infer<typeof SerpIntelligencePageSnapshotsBodySchema>\nexport type SerpIntelligenceCaptureResponse = z.infer<typeof SerpIntelligenceCaptureResponseSchema>\nexport type SerpPageSnapshotCapture = z.infer<typeof SerpPageSnapshotCaptureSchema>\n","import { harvest } from '../harvest.js'\nimport type { HarvestAttemptLogEvent } from '../harvest.js'\nimport { classifyHarvestProblem, type HarvestProblem } from '../api/harvest-problems.js'\nimport type { HarvestResult, LocalPackBusiness, OrganicResult } from '../types.js'\nimport {\n capturePageSnapshots,\n type PageSnapshotCaptureResult,\n type PageSnapshotExtractorTarget,\n} from './page-snapshot-extractor.js'\nimport {\n SerpIntelligenceCaptureBodySchema,\n SerpIntelligenceCaptureResponseSchema,\n type SerpIntelligenceCaptureBody,\n type SerpIntelligenceCaptureResponse,\n type SerpPageSnapshotCapture,\n} from './schemas.js'\n\ntype SerpIntelligenceCaptureAttempt = SerpIntelligenceCaptureResponse['attempts'][number]\n\nexport interface SerpIntelligenceCaptureRuntimeOptions {\n harvest?: typeof harvest\n capturePageSnapshots?: typeof capturePageSnapshots\n kernelApiKey?: string\n kernelProxyId?: string\n headless?: boolean\n outputDir?: string\n pageSnapshotTimeoutMs?: number\n pageSnapshotMaxConcurrency?: number\n includePageSnapshotContent?: boolean\n maxPageSnapshotContentChars?: number\n signal?: AbortSignal\n billing?: SerpIntelligenceCaptureResponse['billing']\n}\n\nexport interface SerpIntelligenceCaptureProblem extends HarvestProblem {\n attempts: SerpIntelligenceCaptureAttempt[]\n}\n\nexport class SerpIntelligenceCaptureError extends Error {\n readonly name = 'SerpIntelligenceCaptureError'\n readonly error_code: HarvestProblem['error_code']\n readonly error_type: string\n readonly retryable: boolean\n readonly httpStatus: number\n readonly terminalStatus: HarvestProblem['terminalStatus']\n readonly attempts: SerpIntelligenceCaptureAttempt[]\n\n constructor(problem: HarvestProblem, attempts: SerpIntelligenceCaptureAttempt[]) {\n super(problem.message)\n this.error_code = problem.error_code\n this.error_type = problem.error_type\n this.retryable = problem.retryable\n this.httpStatus = problem.httpStatus\n this.terminalStatus = problem.terminalStatus\n this.attempts = attempts\n }\n\n toJSON(): SerpIntelligenceCaptureProblem {\n return {\n error_code: this.error_code,\n error_type: this.error_type,\n message: this.message,\n retryable: this.retryable,\n httpStatus: this.httpStatus,\n terminalStatus: this.terminalStatus,\n attempts: this.attempts,\n }\n }\n}\n\nfunction cleanupSucceeded(event: Extract<HarvestAttemptLogEvent, { type: 'finished' }>): boolean | null {\n if (event.cleanup.kernelDeleteSucceeded != null) return event.cleanup.kernelDeleteSucceeded\n return event.cleanup.browserCloseSucceeded\n}\n\nfunction normalizeAttemptEvent(event: Extract<HarvestAttemptLogEvent, { type: 'finished' }>): SerpIntelligenceCaptureAttempt {\n return {\n attemptNumber: event.attemptNumber,\n outcome: event.outcome,\n completedAt: event.completedAt,\n durationMs: event.durationMs,\n ...(event.error ? { problemCode: event.outcome, message: event.error } : {}),\n kernelSessionId: event.kernelSessionId,\n cleanupSucceeded: cleanupSucceeded(event),\n }\n}\n\nfunction stripPageSnapshotArtifact(artifact: PageSnapshotCaptureResult): SerpPageSnapshotCapture {\n return {\n url: artifact.url,\n requestedUrl: artifact.requestedUrl,\n finalUrl: artifact.finalUrl,\n sourceKind: artifact.sourceKind,\n sourcePosition: artifact.sourcePosition,\n status: artifact.status,\n fetchedVia: artifact.fetchedVia,\n httpStatus: artifact.httpStatus,\n contentType: artifact.contentType,\n title: artifact.title,\n canonicalUrl: artifact.canonicalUrl,\n metaDescription: artifact.metaDescription,\n headings: artifact.headings,\n artifact: artifact.artifact,\n error: artifact.error,\n }\n}\n\nfunction normalizePageSnapshotLimit(input: SerpIntelligenceCaptureBody): number {\n if (!input.includePageSnapshots) return 0\n return Math.max(0, Math.min(10, input.pageSnapshotLimit))\n}\n\nfunction addUniqueTarget(\n targets: PageSnapshotExtractorTarget[],\n seenUrls: Set<string>,\n target: PageSnapshotExtractorTarget,\n): void {\n const url = target.url.trim()\n if (!url || seenUrls.has(url)) return\n seenUrls.add(url)\n targets.push({ ...target, url })\n}\n\nfunction collectPageSnapshotTargets(harvestResult: HarvestResult, limit: number): PageSnapshotExtractorTarget[] {\n if (limit <= 0) return []\n\n const targets: PageSnapshotExtractorTarget[] = []\n const seenUrls = new Set<string>()\n\n for (const result of harvestResult.organicResults as OrganicResult[]) {\n if (targets.length >= limit) return targets\n addUniqueTarget(targets, seenUrls, {\n url: result.url,\n sourceKind: 'organic',\n sourcePosition: result.position,\n })\n }\n\n for (const citation of harvestResult.aiOverview.citations) {\n if (targets.length >= limit) return targets\n addUniqueTarget(targets, seenUrls, {\n url: citation.href,\n sourceKind: 'ai_citation',\n sourcePosition: targets.length + 1,\n })\n }\n\n for (const business of harvestResult.localPack as LocalPackBusiness[]) {\n if (targets.length >= limit) return targets\n if (!business.websiteUrl) continue\n addUniqueTarget(targets, seenUrls, {\n url: business.websiteUrl,\n sourceKind: 'local_pack_website',\n sourcePosition: business.position,\n })\n }\n\n return targets\n}\n\nexport function normalizeHarvestResultForSerpIntelligence(input: {\n harvestResult: HarvestResult\n attempts?: SerpIntelligenceCaptureAttempt[]\n pageSnapshotArtifacts?: SerpPageSnapshotCapture[]\n billing?: SerpIntelligenceCaptureResponse['billing']\n}): SerpIntelligenceCaptureResponse {\n return SerpIntelligenceCaptureResponseSchema.parse({\n harvestResult: input.harvestResult,\n attempts: input.attempts ?? [],\n locationEvidence: input.harvestResult.diagnostics.debug?.locationEvidence ?? null,\n pageSnapshotArtifacts: input.pageSnapshotArtifacts ?? [],\n ...(input.billing ? { billing: input.billing } : {}),\n })\n}\n\nexport async function captureSerpIntelligenceSnapshot(\n rawInput: unknown,\n runtimeOptions: SerpIntelligenceCaptureRuntimeOptions = {},\n): Promise<SerpIntelligenceCaptureResponse> {\n const parsedInput = SerpIntelligenceCaptureBodySchema.parse(rawInput)\n const rawObject = rawInput && typeof rawInput === 'object' ? rawInput as { debug?: unknown } : {}\n const debug = typeof rawObject.debug === 'boolean' ? rawObject.debug : true\n const attempts: SerpIntelligenceCaptureAttempt[] = []\n const harvestFn = runtimeOptions.harvest ?? harvest\n const capturePageSnapshotsFn = runtimeOptions.capturePageSnapshots ?? capturePageSnapshots\n\n try {\n const harvestResult = await harvestFn({\n query: parsedInput.query,\n location: parsedInput.location,\n gl: parsedInput.gl,\n hl: parsedInput.hl,\n device: parsedInput.device,\n proxyMode: parsedInput.proxyMode,\n proxyZip: parsedInput.proxyZip,\n pages: parsedInput.pages,\n debug,\n serpOnly: true,\n headless: runtimeOptions.headless ?? true,\n kernelApiKey: runtimeOptions.kernelApiKey ?? process.env.KERNEL_API_KEY?.trim(),\n kernelProxyId: runtimeOptions.kernelProxyId ?? process.env.KERNEL_PROXY_ID?.trim(),\n format: 'json',\n outputDir: runtimeOptions.outputDir ?? '/tmp/serp-intelligence-output',\n signal: runtimeOptions.signal,\n onAttemptEvent: (event: HarvestAttemptLogEvent) => {\n if (event.type === 'finished') attempts.push(normalizeAttemptEvent(event))\n },\n })\n\n const pageSnapshotLimit = normalizePageSnapshotLimit(parsedInput)\n const pageSnapshotTargets = collectPageSnapshotTargets(harvestResult, pageSnapshotLimit)\n const pageSnapshotArtifacts = pageSnapshotTargets.length > 0\n ? (await capturePageSnapshotsFn(pageSnapshotTargets, {\n kernelApiKey: runtimeOptions.kernelApiKey ?? process.env.KERNEL_API_KEY?.trim(),\n timeoutMs: runtimeOptions.pageSnapshotTimeoutMs,\n maxConcurrency: runtimeOptions.pageSnapshotMaxConcurrency,\n debug,\n includeContent: runtimeOptions.includePageSnapshotContent,\n maxContentChars: runtimeOptions.maxPageSnapshotContentChars,\n })).pageSnapshotArtifacts.map(stripPageSnapshotArtifact)\n : []\n\n return normalizeHarvestResultForSerpIntelligence({\n harvestResult,\n attempts,\n pageSnapshotArtifacts,\n billing: runtimeOptions.billing,\n })\n } catch (error) {\n throw new SerpIntelligenceCaptureError(classifyHarvestProblem(error), attempts)\n }\n}\n","import { Hono } from 'hono'\nimport type { Context } from 'hono'\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'\nimport { buildPaaExtractorMcpServer } from './paa-mcp-server.js'\nimport { HttpMcpToolExecutor } from './http-mcp-tool-executor.js'\nimport { getUserByApiKey } from '../api/db.js'\nimport {\n CaptureSerpSnapshotInputSchema,\n CaptureSerpPageSnapshotsInputSchema,\n type CaptureSerpSnapshotInput,\n type CaptureSerpPageSnapshotsInput,\n} from './mcp-tool-schemas.js'\n\ntype SerpIntelligenceMcpToolExecutor = {\n captureSerpSnapshot(input: CaptureSerpSnapshotInput): Promise<CallToolResult>\n captureSerpPageSnapshots(input: CaptureSerpPageSnapshotsInput): Promise<CallToolResult>\n}\n\nfunction mcpAuthError(): Response {\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: {\n code: -32001,\n message: 'authentication required',\n },\n })\n return new Response(body, {\n status: 401,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\nasync function requireMcpCallerKey(c: Context): Promise<string | Response> {\n const xApiKey = c.req.header('x-api-key')?.trim()\n const authHeader = c.req.header('Authorization')?.trim()\n const bearerKey = authHeader?.startsWith('Bearer ') ? authHeader.slice(7).trim() : undefined\n const callerKey = xApiKey ?? bearerKey\n if (!callerKey) return mcpAuthError()\n const user = await getUserByApiKey(callerKey)\n if (!user) return mcpAuthError()\n return callerKey\n}\n\nexport const mcpApp = new Hono()\n\nfunction registerSerpIntelligenceCaptureTools(\n server: ReturnType<typeof buildPaaExtractorMcpServer>,\n executor: HttpMcpToolExecutor,\n) {\n const serpExecutor = executor as Partial<SerpIntelligenceMcpToolExecutor>\n\n server.registerTool('capture_serp_snapshot', {\n description: 'Capture a structured SERP Intelligence Google snapshot through POST /serp-intelligence/capture, the same product capture path used by Phoenix. Split query from location, infer gl/hl, use proxyMode location for localized residential proxy evidence, configured for the static residential proxy, and none only for direct-network debugging. Set debug true when investigating location evidence, proxy behavior, CAPTCHA, or capture reliability.',\n inputSchema: CaptureSerpSnapshotInputSchema,\n }, async (input) => serpExecutor.captureSerpSnapshot\n ? serpExecutor.captureSerpSnapshot(input)\n : Promise.resolve({ content: [{ type: 'text' as const, text: '{}' }], isError: true }))\n\n server.registerTool('capture_serp_page_snapshots', {\n description: 'Capture public ranking-page evidence through POST /serp-intelligence/page-snapshots, the same product page snapshot path used by Phoenix. Provide urls for simple captures or targets when preserving organic, AI citation, local-pack, configured target, or site-subject source metadata. Private IPs, localhost, file URLs, and internal URLs are rejected by the service. Use timeoutMs for slow pages and debug true for sanitized proxy/browser diagnostics.',\n inputSchema: CaptureSerpPageSnapshotsInputSchema,\n }, async (input) => serpExecutor.captureSerpPageSnapshots\n ? serpExecutor.captureSerpPageSnapshots(input)\n : Promise.resolve({ content: [{ type: 'text' as const, text: '{}' }], isError: true }))\n}\n\nmcpApp.all('/', async (c) => {\n try {\n const keyOrError = await requireMcpCallerKey(c)\n if (keyOrError instanceof Response) return keyOrError\n\n const callerKey = keyOrError\n const baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() ?? process.env.MCP_BASE_URL?.trim() ?? 'https://mcpscraper.dev'\n const executor = new HttpMcpToolExecutor(baseUrl, callerKey)\n\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n })\n const server = buildPaaExtractorMcpServer(executor)\n registerSerpIntelligenceCaptureTools(server, executor)\n await server.connect(transport)\n return transport.handleRequest(c.req.raw)\n } catch {\n return c.json({ error: 'Internal Server Error' }, 500)\n }\n})\n","import Stripe from 'stripe'\nimport { Hono } from 'hono'\nimport { getUserByStripeCustomerId, getUserByEmail, setStripeCustomerId, creditMc, ledgerExistsForStripePI, setExtraConcurrencySlots, setConcurrencySubId, recordStripeEvent } from './db.js'\nimport { BALANCE_PRICE_IDS, BALANCE_PACK_LABELS, CONCURRENCY_PRICE_ID, LedgerOperation } from './rates.js'\n\nconst stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2026-02-25.clover' as any })\nexport const stripeApp = new Hono()\n\nstripeApp.post('/webhooks', async (c) => {\n const sig = c.req.header('stripe-signature')\n const body = await c.req.text()\n let event: Stripe.Event\n try {\n event = stripe.webhooks.constructEvent(body, sig!, process.env.STRIPE_WEBHOOK_SECRET!)\n } catch {\n return c.json({ error: 'Invalid signature' }, 400)\n }\n\n const isNew = await recordStripeEvent(event.id)\n if (!isNew) return c.json({ received: true })\n\n if (event.type === 'checkout.session.completed') {\n const session = event.data.object as Stripe.Checkout.Session\n if (session.payment_status !== 'paid') return c.json({ received: true })\n\n const customerId = session.customer as string\n const lineItems = await stripe.checkout.sessions.listLineItems(session.id)\n const priceId = lineItems.data[0]?.price?.id\n if (!priceId) return c.json({ received: true })\n\n if (priceId in BALANCE_PRICE_IDS) {\n const mc = BALANCE_PRICE_IDS[priceId]\n const label = BALANCE_PACK_LABELS[priceId] ?? 'unknown'\n\n let user = await getUserByStripeCustomerId(customerId)\n if (!user) {\n const email = session.customer_details?.email\n if (!email) return c.json({ received: true })\n user = await getUserByEmail(email)\n if (!user) return c.json({ received: true })\n await setStripeCustomerId(user.id, customerId)\n }\n\n const pi = session.payment_intent as string\n if (await ledgerExistsForStripePI(pi)) return c.json({ received: true })\n await creditMc(user.id, mc, LedgerOperation.TOPUP, `${label} balance top-up`, pi)\n }\n }\n\n if (event.type === 'customer.subscription.created') {\n const sub = event.data.object as Stripe.Subscription\n const priceId = sub.items.data[0]?.price?.id\n if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true })\n const user = await getUserByStripeCustomerId(sub.customer as string)\n if (!user) return c.json({ received: true })\n await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1)\n await setConcurrencySubId(user.id, sub.id)\n }\n\n if (event.type === 'customer.subscription.deleted') {\n const sub = event.data.object as Stripe.Subscription\n const priceId = sub.items.data[0]?.price?.id\n if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true })\n const user = await getUserByStripeCustomerId(sub.customer as string)\n if (!user) return c.json({ received: true })\n await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1))\n await setConcurrencySubId(user.id, null)\n }\n\n return c.json({ received: true })\n})\n","import { makeSiteAuditService } from '../services/site-architecture-auditor/factory.js'\nimport { dispatchSiteAuditPhase } from '../services/site-architecture-auditor/phases.js'\nimport { SiteAuditRepository } from '../services/site-architecture-auditor/site-audit-repository.js'\nimport type { SiteAuditPhase } from '../services/site-architecture-auditor/schemas.js'\n\nexport const MAX_CONCURRENT_SITE_AUDIT = 1\n\nexport async function drainSiteAuditQueue(budget: { maxJobs: number; deadlineMs: number }): Promise<string[]> {\n const processedIds: string[] = []\n\n while (true) {\n if (processedIds.length >= budget.maxJobs) break\n if (Date.now() >= budget.deadlineMs) break\n\n const repo = new SiteAuditRepository()\n const job = await repo.claimPendingSiteAuditJob()\n if (!job) break\n\n const service = makeSiteAuditService()\n\n try {\n const parsed = JSON.parse(job.request) as { phase: SiteAuditPhase; payload: unknown }\n await dispatchSiteAuditPhase(service, parsed.phase, parsed.payload)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n await repo.failSiteAuditJob(job.id, msg)\n }\n\n processedIds.push(job.id)\n }\n\n return processedIds\n}\n\nexport function startSiteAuditWorker(): NodeJS.Timeout | undefined {\n if (process.env.INNGEST_EVENT_KEY) {\n console.log('[site-audit-worker] Inngest active — polling worker disabled')\n return\n }\n console.log(`[site-audit-worker] started — polling every 5s, max ${MAX_CONCURRENT_SITE_AUDIT} concurrent`)\n return setInterval(() => {\n void drainSiteAuditQueue({ maxJobs: 1, deadlineMs: Date.now() + 4000 })\n }, 5000)\n}\n","import { z } from 'zod'\n\nexport const BillingCheckoutBodySchema = z.object({\n priceId: z.string().min(1)\n})\nexport type BillingCheckoutBody = z.infer<typeof BillingCheckoutBodySchema>\n\nexport const FreeCreditBreakdownSchema = z.object({\n signup_grant_mc: z.number().int().nonnegative(),\n monthly_refresh_mc: z.number().int().nonnegative(),\n total_free_mc: z.number().int().nonnegative(),\n signup_grant_credits: z.number().nonnegative(),\n monthly_refresh_credits: z.number().nonnegative(),\n total_free_credits: z.number().nonnegative(),\n})\nexport type FreeCreditBreakdown = z.infer<typeof FreeCreditBreakdownSchema>\n\nexport const BillingBalanceResponseSchema = z.object({\n balance_mc: z.number().int().nonnegative(),\n balance_credits: z.number().nonnegative(),\n free_credits: FreeCreditBreakdownSchema,\n ledger: z.array(z.any()),\n})\nexport type BillingBalanceResponse = z.infer<typeof BillingBalanceResponseSchema>\n\nexport const MonthlyRefreshSweepResultSchema = z.object({\n usersRefreshed: z.number().int().nonnegative(),\n totalMcGranted: z.number().int().nonnegative(),\n})\nexport type MonthlyRefreshSweepResult = z.infer<typeof MonthlyRefreshSweepResultSchema>\n","import { getDb } from './db.js'\nimport { FREE_SIGNUP_MC, FREE_MONTHLY_REFRESH_MC, LedgerOperation, MC_PER_CREDIT } from './rates.js'\nimport { creditMc, claimMonthlyFreeRefresh, ledgerExistsForOperation } from './db.js'\nimport type { User } from './db.js'\nimport type { FreeCreditBreakdown, MonthlyRefreshSweepResult } from './billing-schemas.js'\n\nfunction monthKey(value = new Date()): string {\n return value.toISOString().slice(0, 7)\n}\n\nfunction userCreatedBeforeThisMonth(user: User): boolean {\n return user.created_at.slice(0, 7) < monthKey()\n}\n\nexport async function grantSignupCredit(userId: number): Promise<void> {\n if (await ledgerExistsForOperation(userId, LedgerOperation.SIGNUP_GRANT)) return\n await creditMc(userId, FREE_SIGNUP_MC, LedgerOperation.SIGNUP_GRANT, 'Welcome to MCP Scraper')\n}\n\nexport async function applyMonthlyFreeRefresh(user: User): Promise<User> {\n if (!userCreatedBeforeThisMonth(user)) return user\n const claimed = await claimMonthlyFreeRefresh(user.id, monthKey())\n if (!claimed) return user\n const balance = await creditMc(\n user.id,\n FREE_MONTHLY_REFRESH_MC,\n LedgerOperation.MONTHLY_REFRESH,\n 'Monthly free credits',\n )\n return { ...user, balance_mc: balance }\n}\n\nexport async function runMonthlyRefreshSweep(): Promise<MonthlyRefreshSweepResult> {\n const db = getDb()\n const monthStart = new Date()\n monthStart.setUTCDate(1)\n monthStart.setUTCHours(0, 0, 0, 0)\n const monthStartIso = monthStart.toISOString()\n const currentMonth = monthKey()\n\n const res = await db.execute({\n sql: `\n SELECT * FROM users\n WHERE created_at < ?\n AND active = 1\n AND id NOT IN (\n SELECT user_id FROM free_credit_refreshes WHERE month = ?\n )\n `,\n args: [monthStartIso, currentMonth],\n })\n\n const users = res.rows.map(r => ({\n id: Number(r.id),\n email: String(r.email),\n name: r.name != null ? String(r.name) : null,\n api_key: String(r.api_key ?? ''),\n key_active: Number(r.key_active ?? 1),\n password_hash: r.password_hash != null ? String(r.password_hash) : null,\n created_at: String(r.created_at),\n active: Number(r.active),\n stripe_customer_id: r.stripe_customer_id != null ? String(r.stripe_customer_id) : null,\n balance_mc: Number(r.balance_mc ?? 0),\n extra_concurrency_slots: Number(r.extra_concurrency_slots ?? 0),\n concurrency_stripe_sub_id: r.concurrency_stripe_sub_id != null ? String(r.concurrency_stripe_sub_id) : null,\n })) as User[]\n\n let usersRefreshed = 0\n let totalMcGranted = 0\n\n for (const user of users) {\n const before = user.balance_mc\n const after = await applyMonthlyFreeRefresh(user)\n if (after.balance_mc !== before) {\n usersRefreshed++\n totalMcGranted += FREE_MONTHLY_REFRESH_MC\n }\n }\n\n return { usersRefreshed, totalMcGranted }\n}\n\nexport async function getFreeCreditBreakdown(userId: number): Promise<FreeCreditBreakdown> {\n const res = await getDb().execute({\n sql: `\n SELECT operation, SUM(amount_mc) as total\n FROM ledger\n WHERE user_id = ?\n AND operation IN ('signup_grant', 'monthly_free_refresh')\n GROUP BY operation\n `,\n args: [userId],\n })\n\n let signup_grant_mc = 0\n let monthly_refresh_mc = 0\n\n for (const row of res.rows) {\n const op = String(row.operation)\n const total = Number(row.total ?? 0)\n if (op === LedgerOperation.SIGNUP_GRANT) signup_grant_mc = total\n else if (op === LedgerOperation.MONTHLY_REFRESH) monthly_refresh_mc = total\n }\n\n const total_free_mc = signup_grant_mc + monthly_refresh_mc\n\n return {\n signup_grant_mc,\n monthly_refresh_mc,\n total_free_mc,\n signup_grant_credits: signup_grant_mc / MC_PER_CREDIT,\n monthly_refresh_credits: monthly_refresh_mc / MC_PER_CREDIT,\n total_free_credits: total_free_mc / MC_PER_CREDIT,\n }\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto'\n\nconst isProduction = () => process.env.NODE_ENV === 'production' || process.env.VERCEL === '1'\n\nexport function getSessionSecret(): string {\n const configured = process.env.SESSION_SECRET?.trim()\n if (configured) return configured\n if (isProduction()) throw new Error('SESSION_SECRET is not set — add it to your Vercel env vars (see src/api/env.ts)')\n return 'dev-secret-change-me'\n}\n\nconst secret = () => getSessionSecret()\n\nfunction safeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n try {\n return timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'))\n } catch {\n return false\n }\n}\n\nexport function signSession(userId: number | bigint): string {\n const payload = String(userId)\n const sig = createHmac('sha256', secret()).update(payload).digest('hex')\n return `${payload}.${sig}`\n}\n\nexport function verifySession(token: string): number | null {\n const dot = token.lastIndexOf('.')\n if (dot === -1) return null\n const payload = token.slice(0, dot)\n const sig = token.slice(dot + 1)\n const expected = createHmac('sha256', secret()).update(payload).digest('hex')\n if (!safeEqualHex(sig, expected)) return null\n const id = parseInt(payload)\n return isNaN(id) ? null : id\n}\n\nexport function sessionCookie(token: string): string {\n const secure = isProduction() ? '; Secure' : ''\n return `session=${token}; HttpOnly${secure}; Path=/; SameSite=Strict; Max-Age=${60 * 60 * 24 * 30}`\n}\n\nexport function clearCookie(): string {\n return 'session=; HttpOnly; Path=/; Max-Age=0'\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["mkdirSync","homedir","join","clean","chromium","Kernel","strips","totalH","chromium","Kernel","join","path","UA","Hono","z","z","z","z","z","z","H1","H2","z","z","z","z","z","execFile","pLimit","pLimit","path","path","Hono","path","z","execFile","promisify","path","path","path","createMiddleware","createMiddleware","z","Hono","Hono","z","z","Hono","device","Hono","z","el","root","libraryId","platforms","m","fal","z","Hono","fal","fmtTs","Hono","el","el","z","el","Hono","Hono","pLimit","pLimit","z","Hono","Hono","Hono","Hono","Hono","createMiddleware","Stripe","z","isProduction","createMiddleware","Hono","secret","Stripe","join","homedir","mkdirSync"]}
|