mcp-scraper 0.95.2 → 0.96.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/CHANGELOG.md +67 -1
- package/README.md +21 -3
- package/dist/actions-R4EJDLRO.js +1 -0
- package/dist/artifacts-4ZDNP2JW.js +1 -0
- package/dist/artifacts-N62OPIT5.js +1 -0
- package/dist/artifacts-PSADD5OC.js +1 -0
- package/dist/artifacts-RUR3OHAK.js +1 -0
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.js +22 -22
- package/dist/bin/mcp-scraper-core.js +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.js +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/blob-store-GIH26EDO.js +1 -0
- package/dist/bundle-FCXZMIRB.js +1 -0
- package/dist/chunk-24YDQ3EO.js +5 -0
- package/dist/chunk-3F7AFM53.js +1 -0
- package/dist/chunk-4HZXATHB.js +27 -0
- package/dist/chunk-55ZANTHL.js +3 -0
- package/dist/chunk-5BKSKLHX.js +1 -0
- package/dist/chunk-5LYINW7Z.js +8 -0
- package/dist/chunk-63VWBMYR.js +3 -0
- package/dist/chunk-6BD75VKS.js +218 -0
- package/dist/chunk-6N5EC7ZO.js +1 -0
- package/dist/chunk-7POHNTVW.js +1 -0
- package/dist/chunk-7SR7MEU5.js +62 -0
- package/dist/chunk-7WPCGRD7.js +182 -0
- package/dist/chunk-B3JRL45T.js +1 -0
- package/dist/chunk-B4ZKPLSP.js +1 -0
- package/dist/chunk-BPKTUCV7.js +12 -0
- package/dist/chunk-C7MGJUTU.js +1 -0
- package/dist/{chunk-W2BVJ7S2.js → chunk-CNSVZTTT.js} +4 -4
- package/dist/chunk-CORWWDHL.js +1 -0
- package/dist/chunk-CVQUHVHX.js +1 -0
- package/dist/chunk-FB4KBB5W.js +16 -0
- package/dist/{chunk-Y3Q4FIKK.js → chunk-H6MODA7W.js} +199 -84
- package/dist/chunk-HCK6PLM3.js +3 -0
- package/dist/chunk-HNNYXMSI.js +1 -0
- package/dist/chunk-HUNSEGUN.js +57 -0
- package/dist/{chunk-MC4W6TZT.js → chunk-HVQZRQRJ.js} +41 -8
- package/dist/chunk-IFPION5D.js +121 -0
- package/dist/chunk-IY6ZUUY7.js +173 -0
- package/dist/chunk-JHT6A4U6.js +11 -0
- package/dist/chunk-JMZIDHYQ.js +2 -0
- package/dist/chunk-KXJCGV3H.js +1 -0
- package/dist/chunk-MVMWPKRC.js +2 -0
- package/dist/chunk-NALXKTUU.js +1 -0
- package/dist/chunk-NNOX3FYG.js +198 -0
- package/dist/chunk-OILOHQNL.js +2 -0
- package/dist/chunk-OLYPLX5C.js +1 -0
- package/dist/chunk-QER2ELZP.js +1 -0
- package/dist/chunk-RDHGQ2WZ.js +1 -0
- package/dist/chunk-RIHI3KH7.js +955 -0
- package/dist/chunk-RN4VG3VH.js +4 -0
- package/dist/chunk-RYX4FJVH.js +1 -0
- package/dist/chunk-SDQW7IJN.js +1 -0
- package/dist/{chunk-ENZUJEIM.js → chunk-SE4VKLEC.js} +1 -1
- package/dist/chunk-SEZETSB3.js +1 -0
- package/dist/chunk-SFUOSJ3A.js +159 -0
- package/dist/chunk-UIOSADPB.js +1718 -0
- package/dist/{chunk-ZH75KWBI.js → chunk-UNEJYZ4T.js} +1 -1
- package/dist/chunk-UXNR3K6I.js +1 -0
- package/dist/{chunk-VREXSMC6.js → chunk-VVSHZV5S.js} +5 -5
- package/dist/chunk-WNUXMPVX.js +1 -0
- package/dist/chunk-WP3QS4CJ.js +1 -0
- package/dist/chunk-ZTMQEDIB.js +41 -0
- package/dist/contracts-GUXGFCVV.js +1 -0
- package/dist/core-schema-423BSTE6.js +936 -0
- package/dist/dispatch-P7IY75LH.js +1 -0
- package/dist/index.cjs +103 -213
- package/dist/index.d.cts +184 -169
- package/dist/index.d.ts +184 -169
- package/dist/index.js +1 -1
- package/dist/media-artifacts-67CJWI7O.js +1 -0
- package/dist/metering-ZH2P62IC.js +1 -0
- package/dist/migrations-3AKEC5QE.js +1 -0
- package/dist/queue-AC4XKD6Z.js +1 -0
- package/dist/reconciliation-UZUZ6B74.js +1 -0
- package/dist/report-artifact-offload-6OOY2PTM.js +1 -0
- package/dist/repository-4EC5EXVN.js +1 -0
- package/dist/{location-data-repository-A2QZVP7O.js → repository-CYXVMYAI.js} +1 -1
- package/dist/server-DXZ27KQO.js +5825 -0
- package/dist/settlement-6CS4JZTY.js +1 -0
- package/dist/worker-ATRD6VKM.js +1 -0
- package/package.json +2 -2
- package/dist/analytics-repository-DZZKDZL6.js +0 -1
- package/dist/chunk-6DRKDSXY.js +0 -182
- package/dist/chunk-6ND5AFV5.js +0 -27
- package/dist/chunk-7XDBGQDY.js +0 -6
- package/dist/chunk-7YOXSMHG.js +0 -1565
- package/dist/chunk-BLVBUMH5.js +0 -21
- package/dist/chunk-EDQ57S4P.js +0 -1
- package/dist/chunk-GLTECY3U.js +0 -1282
- package/dist/chunk-GPSPSJBJ.js +0 -1
- package/dist/chunk-HE45FFBU.js +0 -1
- package/dist/chunk-HUV2WTRW.js +0 -1
- package/dist/chunk-IXJQHXDC.js +0 -2595
- package/dist/chunk-K3D2S36Q.js +0 -16
- package/dist/chunk-KFOFU3LA.js +0 -1
- package/dist/chunk-LMWU7VPZ.js +0 -63
- package/dist/chunk-O4LUUIYG.js +0 -4
- package/dist/chunk-OLSW4AN4.js +0 -19
- package/dist/chunk-RLFCJGHA.js +0 -100
- package/dist/chunk-T4VTJNDZ.js +0 -84
- package/dist/chunk-TMB56NCA.js +0 -1
- package/dist/chunk-VXOXB3IB.js +0 -1
- package/dist/chunk-WO3N5FH2.js +0 -5
- package/dist/chunk-WO46LTBW.js +0 -1
- package/dist/chunk-XPEJB4BZ.js +0 -1
- package/dist/db-CDEZHBRN.js +0 -1
- package/dist/extract-bundle-SXC6EWJR.js +0 -26
- package/dist/gmail-service-EWR76XE7.js +0 -1
- package/dist/lead-list-enrichment-repository-SIWGXWQV.js +0 -1
- package/dist/operation-metering-UJ53YX7S.js +0 -1
- package/dist/server-QVEKDIDQ.js +0 -5014
- package/dist/site-extract-repository-23ND4N77.js +0 -1
- package/dist/stripe-event-worker-LPXTITVC.js +0 -1
- package/dist/worker-EDN2TJH3.js +0 -1
- /package/dist/{chunk-YGBTTW5D.js → chunk-2IATUJO6.js} +0 -0
- /package/dist/{chunk-6DTXIZY2.js → chunk-4ZVO6GX4.js} +0 -0
- /package/dist/{chunk-XG6GCEUE.js → chunk-U4WIJZCX.js} +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import{i as j,m as T}from"./chunk-KXJCGV3H.js";import{m as f,q as b}from"./chunk-HUNSEGUN.js";import{d as m,e as C,i as p}from"./chunk-C7MGJUTU.js";import{a as o}from"./chunk-WNUXMPVX.js";import{createHash as k}from"crypto";import{gunzipSync as M,gzipSync as $}from"zlib";var H=10,E=20*1024*1024;function _(t){return k("sha256").update(t).digest("hex")}function W(t){return!t.acquiredHtml||t.extractionStatus==="failed"?null:{pageId:t.pageId??_(t.archivedUrl??t.url),url:t.archivedUrl??t.url,html:t.acquiredHtml,mainHtml:t.mainHtml??"",markdown:t.bodyMarkdown,htmlSha256:t.htmlSha256??_(t.acquiredHtml),markdownSha256:t.markdownSha256??_(t.bodyMarkdown)}}function A(t){return Buffer.from(JSON.stringify({version:"site-extract-content.v1",pages:t}))}async function y(t){let e=t.pages.flatMap(i=>{let c=W(i);return c?[c]:[]}),n=new Map,r=[],a=0,s=async()=>{if(!r.length)return;let i=A(r);if(i.length>E)throw new Error(`site export content chunk exceeds ${E} bytes`);let c=_(r.map(u=>u.pageId).join("\0")).slice(0,16),l=await j({ownerId:t.ownerId,jobId:t.jobId,createdAt:t.createdAt,chunkKey:`content-${String(a).padStart(4,"0")}-${c}`,content:$(i,{level:6})});for(let u of r)n.set(u.url,{artifactId:l.artifactId,artifactSha256:l.sha256,pageId:u.pageId,uncompressedBytes:i.length});a++,r=[]};for(let i of e){let c=[...r,i];if(r.length>0&&(c.length>H||A(c).length>E)&&await s(),r.push(i),A(r).length>E)throw new Error(`page ${i.pageId} exceeds the durable content chunk limit`)}return await s(),n}function Y(t){let e=new Map;return async n=>{let r=e.get(n.artifactId);if(r&&r.digest!==n.artifactSha256)throw new Error("site export content chunk checksum mismatch");let a=r?.chunk;if(!a){let i=await T({artifactId:n.artifactId,ownerId:t});if(!i)throw new Error("site export content chunk is missing or unauthorized");if(_(i)!==n.artifactSha256)throw new Error("site export content chunk checksum mismatch");let c=M(i);if(c.length>E)throw new Error("site export content chunk exceeds its read limit");if(a=JSON.parse(c.toString("utf8")),a.version!=="site-extract-content.v1"||!Array.isArray(a.pages))throw new Error("site export content chunk has an unsupported contract");for(e.set(n.artifactId,{digest:n.artifactSha256,chunk:a});e.size>2;)e.delete(e.keys().next().value)}let s=a.pages.find(i=>i.pageId===n.pageId);if(!s)throw new Error("site export page is missing from its content chunk");if(_(s.html)!==s.htmlSha256||_(s.markdown)!==s.markdownSha256)throw new Error("site export page checksum mismatch");return s}}import{randomUUID as J}from"crypto";function x(t,e,n=!1){if(!e)return{sql:`NOT EXISTS (SELECT 1 FROM operation_executions x JOIN operation_runs r ON r.id=x.run_id
|
|
2
|
+
WHERE r.id='extract-site:' || site_extract_jobs.id AND r.tool='extract_site')`,args:[]};if(e.workflow.runId!==`extract-site:${t}`||n&&(!e.phase||e.recovery))return{sql:"0",args:[]};let r=f(e.workflow,{tool:"extract_site",jobId:t,recovery:e.recovery}),a="site_extract_jobs.user_id = ?";if(!e.phase)return{sql:`(${r.sql} AND ${a})`,args:[...r.args,e.workflow.userId]};if(e.phase.userId!==e.workflow.userId)return{sql:"0",args:[]};let s=f(e.phase,{tool:"extract_site_phase",jobId:t});return{sql:`(${r.sql} AND ${a} AND ${s.sql})`,args:[...r.args,e.workflow.userId,...s.args]}}function h(t="site_extract_jobs."){return`(${b(`${t}id`,`${t}user_id`,"extract_site")}
|
|
3
|
+
AND ${b(`${t}id`,`${t}user_id`,"extract_site_phase")})`}function I(){return b("site_extract_jobs.id","site_extract_jobs.user_id","extract_site_phase")}function D(t="site_extract_jobs."){return`(${t}status='running' OR (${t}status='pending' AND ${t}execution_generation=0 AND (
|
|
4
|
+
EXISTS (SELECT 1 FROM operation_runs r WHERE r.id='extract-site:' || ${t}id AND r.user_id=${t}user_id AND r.tool='extract_site')
|
|
5
|
+
OR COALESCE(json_extract(${t}options,'$.__websiteExecutionProtocol'),0) <> 1)))`}var q="xray_entitlement";function st(t){let e=Math.max(1,Number(t.options.effectiveMaxPages??t.options.maxPages??1)),n=Math.max(e,Number(t.options.requestedMaxPages??e)),r=n>e;return{requestedMaxPages:n,effectiveMaxPages:e,creditLimited:r,creditTruncated:r&&t.totalUrls>=e}}function it(t,e=!1){return t.successfulUrls===0?"failed":t.failedUrls>0||t.remainingUrls>0||e?"partial":"complete"}function g(t){let e=Number(t.total_urls??0),n=t.attempted_urls==null,r=Number(n?t.done_urls??0:t.attempted_urls),a=Number(n?t.done_urls??0:t.successful_urls??0),s=Number(t.failed_urls??Math.max(0,r-a)),i=t.options?JSON.parse(String(t.options)):{},c=i.__websiteExecutionProtocol;delete i.__websiteExecutionProtocol;let l={id:String(t.id),userId:t.user_id!=null?Number(t.user_id):null,idempotencyKey:t.idempotency_key!=null?String(t.idempotency_key):null,requestFingerprint:t.request_fingerprint!=null?String(t.request_fingerprint):null,status:t.status!=null?String(t.status):"pending",executionGeneration:Number(t.execution_generation??0),refinalizeUntil:t.refinalize_until==null?null:String(t.refinalize_until),settlementPlan:t.settlement_plan_json?JSON.parse(String(t.settlement_plan_json)):null,settlementReceipt:t.settlement_receipt_json?JSON.parse(String(t.settlement_receipt_json)):null,dispatchClaimToken:t.dispatch_claim_token==null?null:String(t.dispatch_claim_token),dispatchClaimUntil:t.dispatch_claim_until==null?null:String(t.dispatch_claim_until),dispatchedAt:t.dispatched_at==null?null:String(t.dispatched_at),dispatchAttempts:Number(t.dispatch_attempts??0),dispatchError:t.dispatch_error==null?null:String(t.dispatch_error),startUrl:String(t.start_url??""),options:i,totalUrls:e,doneUrls:r,attemptedUrls:r,successfulUrls:a,failedUrls:s,remainingUrls:Math.max(0,e-r),artifacts:t.artifacts?JSON.parse(String(t.artifacts)):null,error:t.error!=null?String(t.error):null,publicError:C(t.public_error_json),billedMc:t.billed_mc!=null?Number(t.billed_mc):null,createdAt:String(t.created_at??""),updatedAt:String(t.updated_at??"")};return c===1&&Object.defineProperty(l,"executionProtocol",{value:1,enumerable:!1}),l}async function v(t,e){let r=await o().execute({sql:"SELECT * FROM site_extract_jobs WHERE user_id = ? AND idempotency_key = ? LIMIT 1",args:[t,e]});return r.rows[0]?g(r.rows[0]):null}async function ot(t){let n=await o().execute({sql:`INSERT OR IGNORE INTO site_extract_jobs
|
|
6
|
+
(id, user_id, status, start_url, options, idempotency_key, request_fingerprint, created_at, updated_at)
|
|
7
|
+
VALUES (?, ?, 'pending', ?, ?, ?, ?, datetime('now'), datetime('now'))`,args:[t.jobId,t.userId,t.startUrl,JSON.stringify({...t.options,__websiteExecutionProtocol:1}),t.idempotencyKey,t.requestFingerprint]}),r=await v(t.userId,t.idempotencyKey);if(!r)throw new Error("idempotent extract job was not persisted");return{job:r,created:n.rowsAffected===1,conflict:r.requestFingerprint!==t.requestFingerprint}}async function P(t){let n=await o().execute({sql:"SELECT * FROM site_extract_jobs WHERE id = ?",args:[t]});return n.rows[0]?g(n.rows[0]):null}async function ct(t=25){return(await o().execute({sql:`SELECT * FROM site_extract_jobs
|
|
8
|
+
WHERE billed_mc IS NULL AND status IN ('complete', 'partial', 'failed')
|
|
9
|
+
AND (next_settlement_at IS NULL OR next_settlement_at <= datetime('now'))
|
|
10
|
+
ORDER BY updated_at ASC LIMIT ?`,args:[Math.max(1,Math.min(100,Math.round(t)))]})).rows.map(r=>g(r))}var w=`(
|
|
11
|
+
EXISTS (SELECT 1 FROM billing_debits d
|
|
12
|
+
WHERE d.idempotency_key = json_extract(site_extract_jobs.options, '$.debitKey')
|
|
13
|
+
AND d.user_id = site_extract_jobs.user_id AND d.status = 'applied'
|
|
14
|
+
AND d.operation = 'extract_site_hold'
|
|
15
|
+
AND json_type(options, '$.heldMc') = 'integer'
|
|
16
|
+
AND json_extract(options, '$.heldMc') > 0
|
|
17
|
+
AND json_extract(options, '$.heldMc') <= 9007199254740991
|
|
18
|
+
AND d.amount_mc = json_extract(options, '$.heldMc')
|
|
19
|
+
AND COALESCE(json_extract(options, '$.billingClass'), 'credits') = 'credits')
|
|
20
|
+
OR (json_extract(options, '$.billingClass') = 'xray_entitlement'
|
|
21
|
+
AND json_type(options, '$.heldMc') IN ('integer', 'real')
|
|
22
|
+
AND json_extract(options, '$.heldMc') = 0 AND json_extract(options, '$.debitKey') IS NULL
|
|
23
|
+
AND json_extract(options, '$.xraySetupReceipt.billingClass') = 'xray_entitlement'
|
|
24
|
+
AND json_type(options, '$.xraySetupReceipt.consumesMcpScraperCredits') = 'false'
|
|
25
|
+
AND json_type(options, '$.xraySetupReceipt.heldMc') IN ('integer', 'real')
|
|
26
|
+
AND json_extract(options, '$.xraySetupReceipt.heldMc') = 0
|
|
27
|
+
AND json_type(options, '$.xraySetupReceipt.billedMc') IN ('integer', 'real')
|
|
28
|
+
AND json_extract(options, '$.xraySetupReceipt.billedMc') = 0))`;async function lt(t){let e=await o().execute({sql:`SELECT * FROM site_extract_jobs WHERE id = ? AND user_id > 0
|
|
29
|
+
AND status IN ('pending', 'running') AND billed_mc IS NULL
|
|
30
|
+
AND execution_generation = 0 AND ${w}`,args:[t]});return e.rows[0]?g(e.rows[0]):null}async function F(t=25,e=2){let n=Math.max(1,Math.min(60,Math.round(e)));return(await o().execute({sql:`SELECT * FROM site_extract_jobs WHERE status = 'pending' AND billed_mc IS NULL
|
|
31
|
+
AND execution_generation = 0 AND dispatched_at IS NULL AND ${w}
|
|
32
|
+
AND (dispatch_claim_token IS NULL OR dispatch_claim_until <= datetime('now'))
|
|
33
|
+
AND (next_dispatch_at IS NULL OR next_dispatch_at <= datetime('now'))
|
|
34
|
+
AND updated_at <= datetime('now', ?) ORDER BY updated_at, id LIMIT ?`,args:[`-${n} minutes`,Math.max(1,Math.min(100,Math.round(t)))]})).rows.map(a=>g(a))}async function ut(t={}){let e=t.jobId?[t.jobId]:(await F(t.limit)).map(r=>r.id),n=[];for(let r of e){let a=J(),i=(await o().execute({sql:`UPDATE site_extract_jobs SET dispatch_claim_token = ?, dispatch_claim_until = datetime('now', ?),
|
|
35
|
+
dispatch_attempts = dispatch_attempts + 1, dispatch_error = NULL, next_dispatch_at = NULL,
|
|
36
|
+
updated_at = datetime('now')
|
|
37
|
+
WHERE id = ? AND status = 'pending' AND billed_mc IS NULL AND execution_generation = 0
|
|
38
|
+
AND dispatched_at IS NULL AND ${w}
|
|
39
|
+
AND (dispatch_claim_token IS NULL OR dispatch_claim_until <= datetime('now'))
|
|
40
|
+
AND (next_dispatch_at IS NULL OR next_dispatch_at <= datetime('now'))
|
|
41
|
+
RETURNING id, dispatch_claim_token, dispatch_attempts`,args:[a,`+${Math.max(15,Math.min(3600,Math.round(t.leaseSeconds??300)))} seconds`,r]})).rows[0];i&&n.push({id:String(i.id),claimToken:String(i.dispatch_claim_token),attempts:Number(i.dispatch_attempts)})}return n}async function dt(t,e){return(await o().execute({sql:`SELECT id FROM site_extract_jobs WHERE id = ? AND status = 'pending' AND billed_mc IS NULL
|
|
42
|
+
AND execution_generation = 0 AND dispatched_at IS NULL AND dispatch_claim_token = ?
|
|
43
|
+
AND dispatch_claim_until > datetime('now') AND ${w}`,args:[t,e]})).rows.length===1}async function _t(t,e){return(await o().execute({sql:`UPDATE site_extract_jobs SET dispatched_at = datetime('now'), dispatch_claim_token = NULL,
|
|
44
|
+
dispatch_claim_until = NULL, next_dispatch_at = NULL, dispatch_error = NULL, updated_at = datetime('now')
|
|
45
|
+
WHERE id = ? AND execution_generation = 0 AND dispatched_at IS NULL
|
|
46
|
+
AND dispatch_claim_token = ? AND dispatch_claim_until > datetime('now')`,args:[t,e]})).rowsAffected===1}async function gt(t,e,n,r){return(await o().execute({sql:`UPDATE site_extract_jobs SET dispatch_error = ?, next_dispatch_at = datetime('now', ?),
|
|
47
|
+
dispatch_claim_token = NULL, dispatch_claim_until = NULL, updated_at = datetime('now')
|
|
48
|
+
WHERE id = ? AND status = 'pending' AND billed_mc IS NULL AND execution_generation = 0
|
|
49
|
+
AND dispatched_at IS NULL AND dispatch_claim_token = ? AND dispatch_claim_until > datetime('now')`,args:[n.slice(0,2e3),`+${Math.max(15,Math.min(3600,Math.round(r)))} seconds`,t,e]})).rowsAffected===1}async function pt(t,e,n=0){await o().execute({sql:`UPDATE site_extract_jobs SET
|
|
50
|
+
settlement_attempts = settlement_attempts + 1,
|
|
51
|
+
settlement_error = ?,
|
|
52
|
+
next_settlement_at = datetime('now', '+15 minutes'),
|
|
53
|
+
updated_at = datetime('now')
|
|
54
|
+
WHERE id = ? AND billed_mc IS NULL AND execution_generation = ? AND status IN ('complete', 'partial', 'failed')`,args:[p(e).slice(0,1e3),t,n]})}function U(t=""){return`((${t}execution_generation = 0 AND ${t}updated_at <= datetime('now', ?))
|
|
55
|
+
OR (${t}execution_generation > 0 AND ${t}refinalize_until <= datetime('now')))`}async function mt(t=25,e=60){let n=o(),r=Math.max(15,Math.min(1440,Math.round(e)));return(await n.execute({sql:`SELECT j.* FROM site_extract_jobs j
|
|
56
|
+
LEFT JOIN billing_debits d
|
|
57
|
+
ON d.idempotency_key = json_extract(j.options, '$.debitKey')
|
|
58
|
+
AND d.user_id = j.user_id
|
|
59
|
+
AND d.status = 'applied'
|
|
60
|
+
WHERE ${D("j.")}
|
|
61
|
+
AND j.billed_mc IS NULL
|
|
62
|
+
AND (
|
|
63
|
+
json_extract(j.options, '$.debitKey') IS NULL
|
|
64
|
+
OR d.idempotency_key IS NOT NULL
|
|
65
|
+
)
|
|
66
|
+
AND ${U("j.")} AND ${h("j.")}
|
|
67
|
+
ORDER BY j.updated_at ASC LIMIT ?`,args:[`-${r} minutes`,Math.max(1,Math.min(100,Math.round(t)))]})).rows.map(s=>g(s))}async function Et(t,e=60){let n=o(),r=Math.max(15,Math.min(1440,Math.round(e)));return(await n.execute({sql:`UPDATE site_extract_jobs
|
|
68
|
+
SET status = 'failed',
|
|
69
|
+
error = 'Background extraction attempt expired; completed pages were preserved and settlement is pending.',
|
|
70
|
+
public_error_json = ?,
|
|
71
|
+
updated_at = datetime('now')
|
|
72
|
+
WHERE id = ?
|
|
73
|
+
AND ${D()}
|
|
74
|
+
AND billed_mc IS NULL
|
|
75
|
+
AND ${U()} AND ${h()}`,args:[m({error_code:"extraction_failed",error_type:"extraction",message:"The background extraction attempt expired. Completed pages were preserved; retry after settlement completes.",retryable:!0,charge_status:"refund_pending"}),t,`-${r} minutes`]})).rowsAffected===1}async function xt(t){let e=await o().execute({sql:`UPDATE site_extract_jobs
|
|
76
|
+
SET status = 'running', execution_generation = execution_generation + 1,
|
|
77
|
+
refinalize_until = datetime('now', '+60 minutes'), updated_at = datetime('now')
|
|
78
|
+
WHERE id = ? AND (settlement_plan_json IS NULL OR billed_mc IS NOT NULL) AND (status = 'failed' OR (
|
|
79
|
+
status = 'running' AND execution_generation > 0 AND refinalize_until <= datetime('now')
|
|
80
|
+
)) RETURNING *`,args:[t]});return e.rows[0]?g(e.rows[0]):null}async function z(t,e=0){return(await o().execute({sql:`SELECT 1 FROM site_extract_jobs WHERE id = ? AND status IN ('pending', 'running')
|
|
81
|
+
AND execution_generation = ? AND (execution_generation = 0 OR refinalize_until > datetime('now'))`,args:[t,e]})).rows.length===1}async function Nt(t,e,n=0){await o().execute({sql:`UPDATE site_extract_jobs SET billed_mc = 0, settlement_error = ?, updated_at = datetime('now')
|
|
82
|
+
WHERE id = ? AND billed_mc IS NULL AND execution_generation = ? AND status IN ('complete', 'partial', 'failed')`,args:[p(e).slice(0,1e3),t,n]})}async function bt(t,e,n){let r=o(),a=x(t,n);await r.execute({sql:`UPDATE site_extract_jobs
|
|
83
|
+
SET status = 'running', total_urls = MAX(total_urls, ?), updated_at = datetime('now')
|
|
84
|
+
WHERE id = ? AND status IN ('pending', 'running') AND execution_generation = 0 AND ${a.sql}`,args:[e,t,...a.args]})}async function wt(t,e,n){if(e.length===0)return;let r=o(),a=await P(t);if(!a||!await z(t)){if(n)throw Error("site_crawl_page_write_not_current");return}let s=x(t,n,!0);if(!(await r.execute({sql:`SELECT 1 FROM site_extract_jobs WHERE id=? AND ${s.sql}`,args:[t,...s.args]})).rows.length){if(n)throw Error("site_crawl_page_write_not_current");return}let c=a?.userId!=null?await y({ownerId:String(a.userId),jobId:t,createdAt:a.createdAt,pages:e}):new Map,l=e.map(d=>{let{discoveryLinks:O,acquiredHtml:N,mainHtml:R,...L}=d;return L.contentRef=c.get(d.archivedUrl??d.url)??d.contentRef,{sql:`INSERT INTO site_extract_pages (job_id, url, page)
|
|
85
|
+
SELECT ?, ?, ?
|
|
86
|
+
WHERE EXISTS (
|
|
87
|
+
SELECT 1 FROM site_extract_jobs
|
|
88
|
+
WHERE id = ? AND status IN ('pending', 'running') AND execution_generation = 0 AND ${s.sql}
|
|
89
|
+
)
|
|
90
|
+
ON CONFLICT(job_id, url) DO UPDATE SET page = excluded.page
|
|
91
|
+
WHERE NOT CASE
|
|
92
|
+
WHEN json_valid(site_extract_pages.page) = 0 THEN 0
|
|
93
|
+
WHEN json_extract(site_extract_pages.page, '$.extractionStatus') IS NOT NULL
|
|
94
|
+
THEN json_extract(site_extract_pages.page, '$.extractionStatus') = 'successful'
|
|
95
|
+
ELSE COALESCE(
|
|
96
|
+
json_extract(site_extract_pages.page, '$.status') >= 200
|
|
97
|
+
AND json_extract(site_extract_pages.page, '$.status') < 300
|
|
98
|
+
AND json_extract(site_extract_pages.page, '$.wordCount') > 0,
|
|
99
|
+
0
|
|
100
|
+
)
|
|
101
|
+
END
|
|
102
|
+
OR CASE
|
|
103
|
+
WHEN json_valid(excluded.page) = 0 THEN 0
|
|
104
|
+
WHEN json_extract(excluded.page, '$.extractionStatus') IS NOT NULL
|
|
105
|
+
THEN json_extract(excluded.page, '$.extractionStatus') = 'successful'
|
|
106
|
+
ELSE COALESCE(
|
|
107
|
+
json_extract(excluded.page, '$.status') >= 200
|
|
108
|
+
AND json_extract(excluded.page, '$.status') < 300
|
|
109
|
+
AND json_extract(excluded.page, '$.wordCount') > 0,
|
|
110
|
+
0
|
|
111
|
+
)
|
|
112
|
+
END`,args:[t,d.archivedUrl??d.url,JSON.stringify(L),t,...s.args]}}),u,S;try{S=await r.batch([...l,{sql:`UPDATE site_extract_jobs SET
|
|
113
|
+
done_urls = (SELECT COUNT(*) FROM site_extract_pages WHERE job_id = ?),
|
|
114
|
+
attempted_urls = (SELECT COUNT(*) FROM site_extract_pages WHERE job_id = ?),
|
|
115
|
+
successful_urls = (
|
|
116
|
+
SELECT COUNT(*) FROM site_extract_pages
|
|
117
|
+
WHERE job_id = ? AND CASE
|
|
118
|
+
WHEN json_extract(page, '$.extractionStatus') IS NOT NULL
|
|
119
|
+
THEN json_extract(page, '$.extractionStatus') = 'successful'
|
|
120
|
+
ELSE json_extract(page, '$.status') >= 200
|
|
121
|
+
AND json_extract(page, '$.status') < 300
|
|
122
|
+
AND json_extract(page, '$.wordCount') > 0
|
|
123
|
+
END
|
|
124
|
+
),
|
|
125
|
+
failed_urls = (
|
|
126
|
+
SELECT COUNT(*) FROM site_extract_pages
|
|
127
|
+
WHERE job_id = ? AND NOT CASE
|
|
128
|
+
WHEN json_extract(page, '$.extractionStatus') IS NOT NULL
|
|
129
|
+
THEN json_extract(page, '$.extractionStatus') = 'successful'
|
|
130
|
+
ELSE COALESCE(
|
|
131
|
+
json_extract(page, '$.status') >= 200
|
|
132
|
+
AND json_extract(page, '$.status') < 300
|
|
133
|
+
AND json_extract(page, '$.wordCount') > 0,
|
|
134
|
+
0
|
|
135
|
+
)
|
|
136
|
+
END
|
|
137
|
+
),
|
|
138
|
+
updated_at = datetime('now')
|
|
139
|
+
WHERE id = ? AND status IN ('pending', 'running') AND execution_generation = 0 AND ${s.sql}`,args:[t,t,t,t,t,...s.args]}],"write")}catch(d){u=d}if(u){let d=await Promise.all(l.map(N=>r.execute({sql:"SELECT page FROM site_extract_pages WHERE job_id=? AND url=?",args:N.args.slice(0,2)})));if(!(await r.execute({sql:`SELECT 1 FROM site_extract_jobs WHERE id=? AND ${s.sql}
|
|
140
|
+
AND attempted_urls=(SELECT COUNT(*) FROM site_extract_pages WHERE job_id=?)
|
|
141
|
+
AND successful_urls=(SELECT COUNT(*) FROM site_extract_pages WHERE job_id=site_extract_jobs.id AND CASE
|
|
142
|
+
WHEN json_extract(page,'$.extractionStatus') IS NOT NULL THEN json_extract(page,'$.extractionStatus')='successful'
|
|
143
|
+
ELSE COALESCE(json_extract(page,'$.status')>=200 AND json_extract(page,'$.status')<300 AND json_extract(page,'$.wordCount')>0,0) END)
|
|
144
|
+
AND failed_urls=attempted_urls-successful_urls`,args:[t,...s.args,t]})).rows.length||d.some((N,R)=>N.rows[0]?.page!==l[R].args[2]))throw u}else if(n&&S?.at(-1)?.rowsAffected!==1)throw Error("site_crawl_page_write_not_current")}async function St(t){return(await o().execute({sql:"SELECT page FROM site_extract_pages WHERE job_id = ? ORDER BY rowid",args:[t]})).rows.map(r=>JSON.parse(String(r.page)))}async function ft(t,e=181){let n=o(),r=Math.max(1,Math.min(5001,Math.round(e)));return(await n.execute({sql:`SELECT DISTINCT CAST(images.value AS TEXT) AS url
|
|
145
|
+
FROM site_extract_pages p,
|
|
146
|
+
json_each(CASE WHEN json_valid(p.page) THEN p.page ELSE '{}' END, '$.imageLinks') images
|
|
147
|
+
WHERE p.job_id = ?
|
|
148
|
+
AND images.type = 'text'
|
|
149
|
+
AND length(CAST(images.value AS TEXT)) BETWEEN 1 AND 4096
|
|
150
|
+
LIMIT ?`,args:[t,r]})).rows.map(s=>String(s.url))}async function At(t){let n=await o().execute({sql:`SELECT COUNT(*) AS n FROM site_extract_pages
|
|
151
|
+
WHERE job_id = ? AND CASE
|
|
152
|
+
WHEN json_extract(page, '$.extractionStatus') IS NOT NULL
|
|
153
|
+
THEN json_extract(page, '$.extractionStatus') = 'successful'
|
|
154
|
+
ELSE COALESCE(
|
|
155
|
+
json_extract(page, '$.status') >= 200
|
|
156
|
+
AND json_extract(page, '$.status') < 300
|
|
157
|
+
AND json_extract(page, '$.wordCount') > 0,
|
|
158
|
+
0
|
|
159
|
+
)
|
|
160
|
+
END`,args:[t]});return Number(n.rows[0]?.n??0)}async function ht(t,e,n,r=null,a=!1,s=null,i=0,c){let l=o(),u=x(t,c,!0);return(await l.execute({sql:`UPDATE site_extract_jobs
|
|
161
|
+
SET status = ?, artifacts = ?, error = ?, public_error_json = ?, updated_at = datetime('now')
|
|
162
|
+
WHERE id = ? AND execution_generation = ? AND (settlement_plan_json IS NULL OR billed_mc IS NOT NULL)
|
|
163
|
+
AND (
|
|
164
|
+
(execution_generation = 0 AND (status IN ('pending', 'running') OR (? = 1 AND status = 'failed')))
|
|
165
|
+
OR (execution_generation > 0 AND status = 'running' AND refinalize_until > datetime('now'))
|
|
166
|
+
) AND (execution_generation > 0 OR ${u.sql})`,args:[n,JSON.stringify(e??[]),r?p(r).slice(0,2e3):null,m(s),t,i,a?1:0,...u.args]})).rowsAffected===1}async function Dt(t,e,n,r=0,a){let s=x(t,a);return(await o().execute({sql:`UPDATE site_extract_jobs
|
|
167
|
+
SET status = 'failed', error = ?, public_error_json = ?, updated_at = datetime('now')
|
|
168
|
+
WHERE id = ? AND status IN ('pending', 'running') AND execution_generation = ?
|
|
169
|
+
AND (execution_generation = 0 OR refinalize_until > datetime('now'))
|
|
170
|
+
AND (execution_generation > 0 OR (${s.sql} AND ${I()}))`,args:[p(e).slice(0,2e3),m(n),t,r,...s.args]})).rowsAffected===1}async function Rt(t,e,n){await o().execute({sql:`UPDATE site_extract_jobs
|
|
171
|
+
SET status = 'failed', billed_mc = 0, error = ?, public_error_json = ?, updated_at = datetime('now')
|
|
172
|
+
WHERE id = ? AND status = 'pending' AND billed_mc IS NULL`,args:[p(e).slice(0,2e3),m(n),t]})}async function Lt(t){return await o().execute({sql:`UPDATE site_extract_jobs SET settlement_plan_json = ?, updated_at = datetime('now')
|
|
173
|
+
WHERE id = ? AND user_id = ? AND execution_generation = ? AND status = ?
|
|
174
|
+
AND billed_mc IS NULL AND settlement_plan_json IS NULL`,args:[JSON.stringify(t),t.jobId,t.userId,t.generation,t.status]}),P(t.jobId)}async function Ct(t,e){let n=t.billingClass===q?"not_charged":e.netChargeMc>0?"charged":e.refundMc>0?"refunded":"not_charged";await o().execute({sql:`UPDATE site_extract_jobs SET billed_mc = ?, settlement_receipt_json = ?,
|
|
175
|
+
public_error_json = CASE WHEN json_valid(public_error_json) THEN json_set(public_error_json, '$.charge_status', ?) ELSE public_error_json END,
|
|
176
|
+
updated_at = datetime('now')
|
|
177
|
+
WHERE id = ? AND user_id = ? AND execution_generation = ? AND status = ?
|
|
178
|
+
AND billed_mc IS NULL AND settlement_plan_json = ?`,args:[e.netChargeMc,JSON.stringify(e),n,t.jobId,t.userId,t.generation,t.status,JSON.stringify(t)]})}async function jt(t,e,n,r,a,s=0){let{settleExtractJobCharge:i}=await import("./settlement-6CS4JZTY.js");return i(t,e,n,r,a,s)}var B=25;async function*Tt(t){let e=o(),n=0;for(;;){let r=await e.execute({sql:`SELECT rowid AS cursor_id, page
|
|
179
|
+
FROM site_extract_pages
|
|
180
|
+
WHERE job_id = ? AND rowid > ?
|
|
181
|
+
ORDER BY rowid
|
|
182
|
+
LIMIT ${B}`,args:[t,n]});if(!r.rows.length)return;if(n=Number(r.rows.at(-1)?.cursor_id??n),!Number.isSafeInteger(n)||n<=0)throw new Error("site extract page cursor is invalid");yield r.rows.map(a=>JSON.parse(String(a.page)))}}export{Y as a,q as b,st as c,it as d,v as e,ot as f,P as g,ct as h,lt as i,ut as j,dt as k,_t as l,gt as m,pt as n,mt as o,Et as p,xt as q,z as r,Nt as s,bt as t,wt as u,St as v,ft as w,At as x,ht as y,Dt as z,Rt as A,Lt as B,Ct as C,jt as D,B as E,Tt as F};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isIP as c}from"net";import{lookup as d}from"dns/promises";import o from"ipaddr.js";function a(e){return e.startsWith("[")&&e.endsWith("]")?e.slice(1,-1):e}function i(e){let r=a(e);if(!c(r))return!1;try{let t=o.parse(r),s=n=>[["192.0.0.0",24],["192.0.2.0",24],["198.18.0.0",15],["198.51.100.0",24],["203.0.113.0",24]].some(([l,u])=>n.match(o.parse(l),u));if(t instanceof o.IPv6&&t.isIPv4MappedAddress()){let n=t.toIPv4Address();return n.range()!=="unicast"||s(n)}return t instanceof o.IPv4&&s(t)?!0:t.range()!=="unicast"}catch{return!0}}async function p(e){let r=a(e.toLowerCase());if(r==="localhost"||r.endsWith(".localhost")||r.endsWith(".local")||i(r))return!0;try{let t=await d(r,{all:!0,verbatim:!0});return t.length===0||t.some(s=>i(s.address))}catch{return!0}}async function I(e,r){let t;try{t=new URL(e.trim())}catch{return{error:`Invalid ${r.field}`}}return(r.requireHttps?["https:"]:["http:","https:"]).includes(t.protocol)?await p(t.hostname)?{error:`${r.field} must resolve to a public internet host`}:{parsed:t}:{error:r.requireHttps?`${r.field} must use https`:`${r.field} must use http or https`}}export{i as a,I as b};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function S(){return(process.env.BROWSER_SERVICE_API_KEY??process.env.\u004b\u0045\u0052\u004e\u0045\u004c_API_KEY)?.trim()||void 0}function i(){return(process.env.BROWSER_AGENT_PROFILE_NAME??process.env.BROWSER_SERVICE_PROFILE_NAME??process.env.\u004b\u0045\u0052\u004e\u0045\u004c_BROWSER_PROFILE_NAME??process.env.\u004b\u0045\u0052\u004e\u0045\u004c_PROFILE_NAME)?.trim()||void 0}function u(){let e=(process.env.BROWSER_AGENT_PROFILE_SAVE_CHANGES??process.env.BROWSER_SERVICE_PROFILE_SAVE_CHANGES??process.env.\u004b\u0045\u0052\u004e\u0045\u004c_BROWSER_PROFILE_SAVE_CHANGES??process.env.\u004b\u0045\u0052\u004e\u0045\u004c_PROFILE_SAVE_CHANGES)?.trim().toLowerCase();if(e){if(["1","true","yes","on"].includes(e))return!0;if(["0","false","no","off"].includes(e))return!1}}var A="none";function a(e){switch(e){case"day":return"qdr:d";case"week":return"qdr:w";case"month":return"qdr:m";case"year":return"qdr:y";default:return}}function M(e,r=!1,n="light",t=1,_=!1,E=!1){let o=r&&(_||E)?24e4:r&&n==="full"?t>=2?75e3:45e3:r?4e4:4e5,R=Math.min(o+5e3,795e3);return{serverMs:o,clientMs:R}}function s(e){return e.reason??new DOMException("The operation was aborted.","AbortError")}async function f(e,r){if(!r)return e;if(r.aborted)throw s(r);let n,t=new Promise((_,E)=>{n=()=>E(s(r)),r.addEventListener("abort",n,{once:!0})});e.catch(()=>{});try{return await Promise.race([e,t])}finally{n&&r.removeEventListener("abort",n)}}function O(e,r){let n=AbortSignal.timeout(e);return r?AbortSignal.any([r,n]):n}export{A as a,a as b,M as c,S as d,i as e,u as f,s as g,f as h,O as i};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import{a as D,c as A,d as T,e as C,f as w,g as p}from"./chunk-6BD75VKS.js";import{B as k,C as x,D as v,b as E,g,x as I}from"./chunk-7WPCGRD7.js";import{e as P,f as S}from"./chunk-5LYINW7Z.js";import{a as _}from"./chunk-WNUXMPVX.js";import{randomUUID as O}from"crypto";var L=class{constructor(t,r,i){this.userId=t;this.sourceSurface=r;this.fence=i;if(i&&i.userId!==t)throw Error("Billing scope principal differs from execution claim")}userId;sourceSurface;fence;identity=`debit:${O()}`;charges=new Map;pending=new Map;compensating=new Set;closing=!1;async debit(t,r,i,c,a){if(this.closing||this.compensating.has(t))throw Error("Billing scope is compensating; new charges are forbidden");let s=this.charges.get(t);if(s?.outcome==="pending")throw Error("Named debit outcome is pending; reconcile before retrying");if(s?.outcome==="funded"&&s.amountMc!==r)throw Error("An applied debit amount cannot change");let o={key:s?.key??`${this.identity}:${t}`,amountMc:r,operation:i,description:c,refundOperation:a,outcome:"pending"};if(s&&(s.operation!==i||s.description!==c||s.refundOperation!==a))throw Error("Named charge identity cannot change");this.charges.set(t,o);let u=(async()=>{if(this.fence){let f=await A(this.fence,{label:t,amountMc:r,operation:i,description:c,refundOperation:a,sourceSurface:this.sourceSurface});if(o.key=f.key,this.closing||this.compensating.has(t))throw Error("Billing scope began compensating while planning")}return this.fence?w(this.userId,r,i,c,o.key,this.fence):w(this.userId,r,i,c,o.key)})();this.pending.set(t,u);try{let f=await u;return o.outcome=f.ok?"funded":"unfunded",{...f,billingKey:o.key}}finally{this.pending.delete(t)}}async compensate(t){this.compensating.add(t),await this.pending.get(t)?.catch(()=>{});try{this.fence&&await this.loadDurablePlans();let r=this.charges.get(t);if(!r)return"not_charged";let i=await C(this.userId,r.key);return i?i.amountMc!==r.amountMc||i.operation!==r.operation||i.description!==r.description?"refund_pending":i.status==="claimed"?this.fence?.mode==="compensate"||r.outcome==="unfunded"?"not_charged":"refund_pending":i.status!=="applied"?"refund_pending":(this.fence?await p(this.userId,r.key,0,r.refundOperation,`${this.sourceSurface} failed`,this.sourceSurface,this.fence):await p(this.userId,r.key,0,r.refundOperation,`${this.sourceSurface} failed`,this.sourceSurface)).final_amount_mc===0?"refunded":"refund_pending":this.fence?.mode==="compensate"||r.outcome==="unfunded"?"not_charged":"refund_pending"}catch{return"refund_pending"}}async compensateAll(){if(this.closing=!0,await Promise.allSettled([...this.pending.values()]),this.fence)try{await this.loadDurablePlans()}catch{return"refund_pending"}let t=await Promise.all([...this.charges.keys()].map(r=>this.compensate(r)));return t.includes("refund_pending")?"refund_pending":t.includes("refunded")?"refunded":"not_charged"}async loadDurablePlans(){if(this.fence)for(let t of await T(this.fence)){let r=this.charges.get(t.label);if(t.sourceSurface!==this.sourceSurface)throw Error("Persisted billing source differs from scope");this.charges.set(t.label,{key:t.key,amountMc:t.amountMc,operation:t.operation,description:t.description,refundOperation:t.refundOperation,outcome:r?.key===t.key?r.outcome:"pending"})}}};async function H(e){if(!Number.isSafeInteger(e.userId)||e.userId<=0||!Number.isSafeInteger(e.refundMc)||e.refundMc<0)throw Error("invalid_legacy_refund");await _().batch([e.admission,{sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, expires_at)
|
|
2
|
+
SELECT ?, ?, ?, ?, ? WHERE changes() = 1 AND ? > 0 AND datetime(?) > datetime('now')`,args:[e.userId,e.refundMc,e.refundMc,e.operation,e.expiresAt,e.refundMc,e.expiresAt]},{sql:"UPDATE users SET balance_mc = balance_mc + ? WHERE id = ? AND ? > 0 AND changes() = 1",args:[e.refundMc,e.userId,e.refundMc]},{sql:`INSERT INTO ledger (user_id, amount_mc, operation, description)
|
|
3
|
+
SELECT ?, ?, ?, ? WHERE ? > 0 AND changes() = 1`,args:[e.userId,e.refundMc,e.operation,e.description,e.refundMc]},e.acknowledgment],"write")}function J(e){return e.status==="complete"||e.status==="partial"||e.status==="failed"}function y(e,t,r){return e.userId===t&&(e.executionGeneration??0)===r&&J(e)}function W(e){let t=e.options.xraySetupReceipt;return e.options.heldMc===0&&e.options.debitKey==null&&t?.billingClass===E&&t.consumesMcpScraperCredits===!1&&t.heldMc===0&&t.billedMc===0}function b(e,t){if(!e||!y(e,t.userId,t.generation)||JSON.stringify(e.settlementPlan)!==JSON.stringify(t))return null;let r=e.settlementReceipt;return!r||r.jobId!==t.jobId||r.userId!==t.userId||r.generation!==t.generation||e.billedMc!==r.netChargeMc?null:r}async function Q(e,t,r,i,c,a=0){let s=await g(e);if(!s||!y(s,t,a))throw Error("extract_settlement_identity_or_terminal_conflict");if(s.billedMc!=null)return s.settlementReceipt??null;let o=Number(s.options.heldMc??0);if(![o,r,i].every(d=>Number.isSafeInteger(d)&&d>=0)||r+i!==o)throw Error("extract_settlement_amount_conflict");let u=String(s.options.billingClass??"credits");if(u===E&&!W(s))throw Error("xray_extract_zero_charge_finalization_rejected");let f={version:1,jobId:e,userId:t,generation:a,status:s.status,heldMc:o,debitKey:typeof s.options.debitKey=="string"?s.options.debitKey:null,billingClass:u,netChargeMc:i,refundMc:r,reference:c},l;try{l=await k(f)}catch(d){if(l=await g(e).catch(()=>null),!l?.settlementPlan||!y(l,t,a))throw d}if(!l||!y(l,t,a)||!l.settlementPlan)throw Error("extract_settlement_admission_conflict");let n=l.settlementPlan;if(n.jobId!==e||n.userId!==t||n.generation!==a||n.status!==l.status||n.heldMc!==o||n.debitKey!==f.debitKey||n.billingClass!==u)throw Error("extract_settlement_plan_conflict");let N=b(l,n);if(N)return N;let m={version:1,jobId:e,userId:t,generation:a,netChargeMc:n.netChargeMc,refundMc:0,expiredRefundMc:0};try{if(u===E)await x(n,m);else if(n.debitKey){let d=await p(t,n.debitKey,n.netChargeMc,S.EXTRACT_SITE_REFUND,n.reference,"extract_site");m.netChargeMc=d.final_amount_mc,m.refundMc=d.refund_mc,m.expiredRefundMc=d.expired_refund_mc??0,await x(n,m)}else{let h=await _().execute({sql:"SELECT datetime(created_at, ?) AS expires_at FROM site_extract_jobs WHERE id = ?",args:[D,e]}),R=[e,t,a,n.status,JSON.stringify(n)];await H({userId:t,refundMc:n.refundMc,expiresAt:String(h.rows[0]?.expires_at),operation:S.EXTRACT_SITE_REFUND,description:n.reference,admission:{sql:`UPDATE site_extract_jobs SET billed_mc = -1
|
|
4
|
+
WHERE id = ? AND user_id = ? AND execution_generation = ? AND status = ?
|
|
5
|
+
AND settlement_plan_json = ? AND billed_mc IS NULL`,args:R},acknowledgment:{sql:`UPDATE site_extract_jobs SET billed_mc = ?,
|
|
6
|
+
settlement_receipt_json = json_set(?, '$.refundMc', CASE WHEN changes() = 1 THEN ? ELSE 0 END,
|
|
7
|
+
'$.expiredRefundMc', CASE WHEN changes() = 1 THEN 0 ELSE ? END),
|
|
8
|
+
public_error_json = CASE WHEN json_valid(public_error_json) THEN json_set(public_error_json, '$.charge_status',
|
|
9
|
+
CASE WHEN ? > 0 THEN 'charged' WHEN changes() = 1 AND ? > 0 THEN 'refunded' ELSE 'not_charged' END) ELSE public_error_json END,
|
|
10
|
+
updated_at = datetime('now')
|
|
11
|
+
WHERE id = ? AND user_id = ? AND execution_generation = ? AND status = ?
|
|
12
|
+
AND settlement_plan_json = ? AND billed_mc = -1`,args:[n.netChargeMc,JSON.stringify(m),n.refundMc,n.refundMc,n.netChargeMc,n.refundMc,...R]}})}}catch(d){let h=b(await g(e).catch(()=>null),n);if(h)return h;throw d}let M=b(await g(e),n);if(!M)throw Error("extract_settlement_acknowledgment_unknown");return M}async function V(e){if(e.userId==null||e.billedMc!=null)return e.settlementReceipt??null;if(!J(e))throw Error("extract_settlement_requires_terminal_job");let t=Number(e.options.heldMc??0),r=await I(e.id),i=Math.min(r*P.page_scrape,t),c="site";try{c=new URL(e.startUrl).hostname}catch{}return await v(e.id,e.userId,t-i,i,c,e.executionGeneration??0)}export{L as a,Q as b,V as c};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var k="Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.",m="This operation is temporarily unavailable. Please retry later.",b=["request_aborted","captcha_exhausted","captcha_or_blocked","location_mismatch","proxy_tunnel_failed","proxy_unavailable","harvest_timeout","mcp_request_timeout","mcp_http_error","extraction_failed","concurrency_limit_exceeded","unauthorized","forbidden","invalid_request","rate_limited","upstream_rate_limited","insufficient_balance","service_unavailable","response_lost","vendor_unavailable","page_not_found","page_forbidden","page_rate_limited","page_server_error","page_http_error","bot_check_unresolved","page_too_large","page_unreachable","browser_session_interrupted","site_export_not_found","directory_job_not_found","site_export_format_unavailable","site_export_read_failed","site_export_image_not_found","image_url_invalid","image_url_not_https","image_host_unresolvable","image_host_blocked","image_redirect_invalid","image_source_unavailable","image_too_large","image_redirect_limit","image_base64_invalid","image_source_missing","image_source_ambiguous","image_source_type_invalid","image_artifact_not_found","image_page_too_large","page_image_not_found","page_image_candidates_unusable","image_format_unsupported","image_storage_unconfigured","featured_image_unhostable"],h=["request_aborted","captcha","location_mismatch","connection","timeout","extraction","concurrency_limit","unauthorized","forbidden","invalid_request","not_found","rate_limit","billing","service_unavailable"];function S(e){return e?JSON.stringify(e):null}function R(e){if(e==null)return null;try{let r=typeof e=="string"?JSON.parse(e):e;if(!r||typeof r!="object"||Array.isArray(r))return null;let t=r;if(typeof t.error_code!="string"||!p.has(t.error_code))return null;let a=C({errorCode:t.error_code,errorType:t.error_type,retryable:t.retryable,retryAfterSeconds:t.retry_after_seconds,chargeStatus:t.charge_status,details:t.details});if(typeof t.message=="string"){let n=g(t.message).trim().slice(0,1e3);n&&!/https?:\/\//i.test(n)&&(a.message=n)}return a}catch{return null}}var p=new Set(b),f=new Set(h),y=new Set(["active","limit","operation","upgrade_url","topup_url","upgrade_command","slots_per_pack","pack_price_usd","balance_credits","required_credits","retry_guidance","http_status"]),v=12,T=500;function o(e){return typeof e=="string"&&p.has(e)?e:"service_unavailable"}function x(e){if(!e||typeof e!="object"||Array.isArray(e))return;let r={};for(let[t,a]of Object.entries(e)){if(Object.keys(r).length>=v)break;if(y.has(t))if(typeof a=="string"){if((t==="upgrade_url"||t==="topup_url")&&!E(a))continue;let n=g(a).slice(0,T);n&&(r[t]=n)}else(typeof a=="number"&&Number.isFinite(a)||typeof a=="boolean"||a===null)&&(r[t]=a)}return Object.keys(r).length>0?r:void 0}function E(e){if(e.startsWith("/")&&!e.startsWith("//"))return!0;try{let r=new URL(e);return r.protocol==="https:"&&(r.hostname==="mcpscraper.dev"||r.hostname.endsWith(".mcpscraper.dev"))}catch{return!1}}function P(e){let r=o(e);switch(r){case"captcha_exhausted":case"captcha_or_blocked":return"captcha";case"proxy_tunnel_failed":case"proxy_unavailable":case"response_lost":return"connection";case"harvest_timeout":case"mcp_request_timeout":return"timeout";case"concurrency_limit_exceeded":return"concurrency_limit";case"rate_limited":case"upstream_rate_limited":return"rate_limit";case"extraction_failed":case"page_not_found":case"page_forbidden":case"page_rate_limited":case"page_server_error":case"page_http_error":case"bot_check_unresolved":case"page_too_large":case"page_unreachable":case"browser_session_interrupted":case"site_export_not_found":case"site_export_format_unavailable":case"site_export_read_failed":case"site_export_image_not_found":return"extraction";case"image_url_invalid":case"image_url_not_https":case"image_host_unresolvable":case"image_host_blocked":case"image_redirect_invalid":case"image_source_unavailable":case"image_too_large":case"image_redirect_limit":case"image_base64_invalid":case"image_source_missing":case"image_source_ambiguous":case"image_source_type_invalid":case"image_artifact_not_found":case"image_page_too_large":case"page_image_not_found":case"page_image_candidates_unusable":case"image_format_unsupported":case"image_storage_unconfigured":case"featured_image_unhostable":return"invalid_request";case"insufficient_balance":return"billing";case"vendor_unavailable":case"mcp_http_error":return"service_unavailable";case"directory_job_not_found":return"not_found";default:return r}}function w(e){switch(o(e)){case"request_aborted":return"The request was cancelled before it completed.";case"captcha_exhausted":case"captcha_or_blocked":return"The target site returned a verification challenge. Heavy traffic can temporarily increase these challenges; retry in a few minutes.";case"location_mismatch":return"The target site returned results for a different location. Please retry.";case"proxy_tunnel_failed":case"proxy_unavailable":return"The web request could not establish a working connection. Please retry later.";case"harvest_timeout":return"The request timed out before it completed. Please retry with a smaller request.";case"mcp_request_timeout":return"The MCP caller stopped waiting before a final response arrived. Durable work may still be running; continue with the returned Task or job handle instead of restarting it.";case"concurrency_limit_exceeded":return"The service is available, but this account has reached its parallel-operation limit. Wait for an active operation to finish or add capacity.";case"unauthorized":return"Authentication is required to perform this operation.";case"forbidden":return"This account does not have permission to perform this operation.";case"invalid_request":return"The request is invalid. Check the supplied inputs and try again.";case"rate_limited":return"This account is sending requests too quickly. Wait briefly and retry.";case"upstream_rate_limited":return"The connected provider is temporarily rate-limiting requests. Wait briefly and retry.";case"mcp_http_error":return"The API returned an unexpected HTTP response.";case"insufficient_balance":return"This account does not have enough Credits for the operation.";case"extraction_failed":return"The target could not be extracted. Check retryable before attempting it again.";case"directory_job_not_found":return"This directory job was not found for the current account.";case"response_lost":return"The connection was lost before the response arrived. The operation may have already completed and been billed on our side \u2014 check credits_info before retrying blind.";case"vendor_unavailable":return"MCP Scraper's internal browser services require MCP Scraper team attention. Servers or IPs are down until this is fixed \u2014 this is not caused by your request. Please retry in a few minutes.";case"page_not_found":return"The page could not be found. It may have been moved or deleted.";case"page_forbidden":return"The site refused the request.";case"page_rate_limited":return"The site is rate-limiting requests right now. Retrying after a short wait usually works.";case"page_server_error":return"The page's own server returned an error. This is on the target site's side, not something a retry here can fix.";case"page_http_error":return"The page returned an unexpected HTTP response.";case"bot_check_unresolved":return"This site has automated-traffic protection (a bot/CAPTCHA check) that could not be resolved in time. Some sites are simply not extractable this way.";case"page_too_large":return"The page is larger than we can safely process.";case"page_unreachable":return"The page did not respond in time, or the connection could not be completed.";case"browser_session_interrupted":return"The browser session closed before extraction completed. MCP Scraper will retry once with a fresh session; retry the request if this persists.";case"site_export_not_found":return"The site export or requested page was not found, expired, or does not belong to this account.";case"site_export_format_unavailable":return"That representation was not retained for this export page. Read the export manifest and request one of the formats marked available.";case"site_export_read_failed":return"The stored site export could not be read or did not pass its integrity check.";case"site_export_image_not_found":return"The requested site-export image was not found, expired, or does not belong to this account.";case"image_url_invalid":return"The image source is not a valid URL. Use a stable public HTTPS URL or inline image bytes.";case"image_url_not_https":return"Commons image sources must use a public HTTPS URL or inline image bytes.";case"image_host_unresolvable":return"The image host could not be resolved. Use a different stable public URL or inline image bytes.";case"image_host_blocked":return"The image source resolves to a network address Commons cannot fetch safely.";case"image_redirect_invalid":return"The image source redirected to an invalid or unsafe destination.";case"image_source_unavailable":return"The image source could not be fetched. Host a different stable public URL or provide inline image bytes.";case"image_too_large":return"The image exceeds the Commons image size limit.";case"image_redirect_limit":return"The image source exceeded the Commons redirect limit.";case"image_base64_invalid":return"The supplied inline image bytes are invalid.";case"image_source_missing":return"Provide exactly one of a public image or webpage URL, inline image bytes, or an owner-scoped image artifact.";case"image_source_ambiguous":return"Provide only one image source: sourceUrl, imageBase64, or artifactId.";case"image_source_type_invalid":return"Use sourceType only with inline image bytes or an owner-scoped image artifact.";case"image_artifact_not_found":return"The image artifact was not found, expired, or does not belong to this account.";case"image_page_too_large":return"The webpage is too large for bounded Commons image discovery.";case"page_image_not_found":return"The webpage did not expose a usable featured or content image.";case"page_image_candidates_unusable":return"The webpage exposed image candidates, but none returned supported public image bytes.";case"image_format_unsupported":return"Commons accepts JPEG, PNG, GIF, or WebP image bytes.";case"image_storage_unconfigured":return"Commons image storage is temporarily unavailable.";case"featured_image_unhostable":return"The required featured image could not be hosted for Commons publication.";default:return m}}function C(e){let r=o(e.errorCode),t=typeof e.retryAfterSeconds=="number"&&Number.isFinite(e.retryAfterSeconds)?Math.max(1,Math.min(86400,Math.trunc(e.retryAfterSeconds))):void 0,a=e.chargeStatus==="not_charged"||e.chargeStatus==="refund_pending"||e.chargeStatus==="refunded"||e.chargeStatus==="charged"?e.chargeStatus:void 0,n=typeof e.errorType=="string"&&f.has(e.errorType)?e.errorType:void 0,s=x(e.details);return{error_code:r,error_type:n??P(r),message:w(r),retryable:e.retryable===!0,...t?{retry_after_seconds:t}:{},...a?{charge_status:a}:{},...s?{details:s}:{}}}function g(e){return e.replace((new RegExp("https?:\\/\\/[^\\s]*(?:\u0062\u0072\u0069\u0067\u0068\u0074\u0064\u0061\u0074\u0061|on\u006b\u0065\u0072\u006e\u0065\u006c|\u006b\u0065\u0072\u006e\u0065\u006c\\.sh)[^\\s]*", "gi")),"the service").replace(/bright\s*data/gi,"the service").replace((new RegExp("\u0062\u0072\u0069\u0067\u0068\u0074\u0064\u0061\u0074\u0061", "gi")),"the service").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh\\s+sessions?", "gi")),"sessions").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh\\s+session", "gi")),"this session").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh", "gi")),"the service").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\s+sessions?", "gi")),"sessions").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\s+session", "gi")),"this session").replace(/\b(?:sub|cus|sess|proxy)_[A-Za-z0-9_-]+\b/g,"[redacted]").replace((new RegExp("\\b(?:\u004b\u0045\u0052\u004e\u0045\u004c|\u0042\u0052\u0049\u0047\u0048\u0054\u0044\u0041\u0054\u0041|\u0042\u0052\u0049\u0047\u0048\u0054_\u0044\u0041\u0054\u0041)_[A-Z0-9_]*API_KEY\\b", "g")),"BROWSER_SERVICE_API_KEY").replace((new RegExp("\\b\u006b\u0065\u0072\u006e\u0065\u006c\\b", "gi")),"the service").replace(/ +/g," ").trim()}var i=class extends Error{constructor(t){super(`CAPTCHA detected. ${t}`);this.instructions=t}instructions;name="CaptchaError"},c=class extends Error{constructor(t,a){super(t);this.cause=a}cause;name="ExtractionError"},u=class extends Error{constructor(t){super(`Google Maps search did not reach a complete result state (${t})`);this.pageState=t}pageState;name="MapsSearchIncompletePageError"},l=class extends Error{name="RequestAbortedError";constructor(r="Request aborted before harvest completed"){super(r)}},_=class extends Error{name="PaaInteractionInterruptedError"},d=class extends Error{name="LocationMismatchError";constructor(r="Google returned results for a different location than requested"){super(r)}};export{k as a,m as b,b as c,S as d,R as e,o as f,w as g,C as h,g as i,i as j,c as k,u as l,l as m,_ as n,d as o};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import{b as y}from"./chunk-
|
|
2
|
-
`)}function I(s){let r=s.replace(/^www\./,"").toLowerCase(),c=r.split(".");return c.length<=2?r:c.slice(-2).join(".")}function U(s,r,c){let n="";try{n=I(new URL(c).hostname)}catch{n=""}let e=s.filter(l=>l.internal),a=r.length,
|
|
3
|
-
`)}function
|
|
1
|
+
import{b as y}from"./chunk-B3JRL45T.js";var $=100*1024,L=new Set(["webp","avif","svg","svg+xml"]);function x(s){return s==null?null:s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/(1024*1024)).toFixed(2)} MB`}var R=s=>s.replace(/&/g,"&").replace(/�?38;/g,"&").replace(/&/gi,"&").trim(),M=s=>s.replace(/^https?:\/\//i,"//").replace(/\/$/,""),E=(s,r)=>{if(s){let c=s.split(";")[0].trim().toLowerCase();if(c.startsWith("image/"))return c.replace("image/","")}return(r.split("?")[0].match(/\.([a-z0-9]+)$/i)?.[1]||"unknown").toLowerCase()};function T(s){let r=new Map;for(let c of s)for(let n of c.imageLinks||[]){let e=R(n);if(!/^https?:\/\//i.test(e)||/[{}]|%7[bd]/i.test(e))continue;let a=M(e),o=r.get(a);(!o||/^https:/i.test(e)&&/^http:/i.test(o))&&r.set(a,e)}return[...r.values()]}async function D(s,r){let c=new AbortController,n=setTimeout(()=>c.abort(),r),e=o=>({len:o.headers.get("content-length"),cr:o.headers.get("content-range"),ct:o.headers.get("content-type"),status:o.status}),a=async o=>{let g=s;for(let p=0;p<=5;p++){let m=await y(g,{field:"image URL"});if(m.error||!m.parsed)throw new Error(m.error??"Image URL was rejected");let u=await fetch(m.parsed.href,{method:o,...o==="GET"?{headers:{Range:"bytes=0-0"}}:{},redirect:"manual",signal:c.signal});if(u.status>=300&&u.status<400){let t=u.headers.get("location");if(!t)throw new Error(`HTTP ${u.status} redirect did not include Location`);g=new URL(t,m.parsed.href).href;continue}return u}throw new Error("Image request exceeded five redirects")};try{let o=null,g=null,p=null;try{let m=e(await a("HEAD"));p=m.status,g=m.ct,m.len&&(o=Number(m.len))}catch{}if(o==null){let m=await a("GET"),u=e(m);if(p=p??u.status,g=g??u.ct,u.cr&&u.cr.includes("/")){let t=u.cr.split("/")[1];t&&t!=="*"&&(o=Number(t))}else u.len&&(o=Number(u.len));try{await m.body?.cancel()}catch{}}return{url:s,status:p,bytes:Number.isFinite(o)?o:null,contentType:g}}catch(o){return{url:s,status:null,bytes:null,contentType:null,error:String(o.message||o)}}finally{clearTimeout(n)}}async function v(s,r,c){let n=new Array(s.length),e=0;return await Promise.all(Array.from({length:Math.min(r,s.length)},async()=>{for(;e<s.length;){let a=e++;n[a]=await c(s[a])}})),n}async function S(s,r={}){return A(T(s),r)}async function A(s,r={}){let c=r.concurrency??12,n=r.timeoutMs??12e3,e=r.max??5e3,a=T([{imageLinks:s}]).slice(0,e),g=(await v(a,c,t=>D(t,n))).map(t=>{let f=E(t.contentType,t.url);return{...t,size:x(t.bytes),format:f,over100kb:t.bytes!=null&&t.bytes>$,legacyFormat:f!=="unknown"&&!L.has(f)}}),p=g.filter(t=>t.bytes!=null),m=p.reduce((t,f)=>t+f.bytes,0),u={};for(let t of g)u[t.format]=(u[t.format]||0)+1;return{rows:g.sort((t,f)=>(f.bytes??0)-(t.bytes??0)),summary:{unique:g.length,sized:p.length,totalBytes:m,totalSize:x(m)??"0 B",avgSize:x(Math.round(m/(p.length||1)))??"0 B",over100kb:g.filter(t=>t.over100kb).length,legacyFormat:g.filter(t=>t.legacyFormat).length,formatCounts:u,...r.sampleTruncated?{sampleTruncated:!0,sampleCap:e}:{}}}}function _(s){let r=s.summary,c=Object.entries(r.formatCounts).sort((a,o)=>o[1]-a[1]).map(([a,o])=>`${a}:${o}`).join(" "),n=s.rows.filter(a=>a.over100kb).slice(0,15),e=["## Images",`**${r.unique} unique images** \xB7 ${r.totalSize} total \xB7 ${r.avgSize} avg \xB7 ${r.over100kb} over 100 KB \xB7 ${r.legacyFormat} legacy format`,`Formats: ${c}`];if(n.length){e.push("","| Size | Format | URL |","|------|--------|-----|");for(let a of n)e.push(`| ${a.size} | ${a.format} | ${a.url} |`)}return e.join(`
|
|
2
|
+
`)}function I(s){let r=s.replace(/^www\./,"").toLowerCase(),c=r.split(".");return c.length<=2?r:c.slice(-2).join(".")}function U(s,r,c){let n="";try{n=I(new URL(c).hostname)}catch{n=""}let e=s.filter(l=>l.internal),a=r.length,o=r.filter(l=>l.orphan).length,g=e.filter(l=>l.targetStatus!=null&&l.targetStatus>=400).length,p=r.reduce((l,h)=>l+h.inlinks,0),m=r.reduce((l,h)=>l+h.outlinksInternal+h.outlinksExternal,0),u={zero:0,oneToTwo:0,threeToTen:0,elevenPlus:0};for(let l of r)l.inlinks===0?u.zero++:l.inlinks<=2?u.oneToTwo++:l.inlinks<=10?u.threeToTen++:u.elevenPlus++;let t=[...r].sort((l,h)=>h.inlinks-l.inlinks).slice(0,20).map(l=>({url:l.url,inlinks:l.inlinks,outlinksInternal:l.outlinksInternal,outlinksExternal:l.outlinksExternal})),f=new Map,d=0;for(let l of s){let h;try{h=I(new URL(l.to).hostname)}catch{continue}if(!h||h===n)continue;d++;let k=f.get(h)??{links:0,nofollow:0,pages:new Set};k.links++,l.nofollow&&k.nofollow++,k.pages.add(l.from),f.set(h,k)}let i=[...f.entries()].map(([l,h])=>({domain:l,links:h.links,nofollow:h.nofollow,pages:h.pages.size})).sort((l,h)=>h.links-l.links),b=l=>Math.round(l*10)/10;return{summary:{internal:{totalLinks:e.length,pages:a,orphans:o,brokenInternal:g,avgInlinks:a?b(p/a):0,avgOutlinks:a?b(m/a):0,distribution:u,topByInlinks:t},external:{totalLinks:d,uniqueDomains:i.length,topDomains:i.slice(0,20)}},externalDomains:i}}function X(s){let r=s.summary;return["## Link analysis",`**Internal:** ${r.internal.totalLinks} links \xB7 ${r.internal.pages} pages \xB7 avg ${r.internal.avgInlinks} inlinks/page \xB7 ${r.internal.orphans} orphans \xB7 ${r.internal.brokenInternal} broken`,`**External:** ${r.external.totalLinks} links to ${r.external.uniqueDomains} domains`,"","### Inlink distribution",`- 0 inlinks (orphans): ${r.internal.distribution.zero}`,`- 1\u20132: ${r.internal.distribution.oneToTwo}`,`- 3\u201310: ${r.internal.distribution.threeToTen}`,`- 11+: ${r.internal.distribution.elevenPlus}`,"","### Top 20 internal pages by inlinks","| inlinks | out (int/ext) | URL |","|---|---|---|",...r.internal.topByInlinks.map(n=>`| ${n.inlinks} | ${n.outlinksInternal}/${n.outlinksExternal} | ${n.url} |`),"","### Top 20 external domains by links","| links | nofollow | from pages | domain |","|---|---|---|---|",...r.external.topDomains.map(n=>`| ${n.links} | ${n.nofollow} | ${n.pages} | ${n.domain} |`)].join(`
|
|
3
|
+
`)}function w(s,r){let c=new Map;for(let n of s){let e=r(n);if(e==null||e==="")continue;let a=String(e);c.has(a)||c.set(a,[]),c.get(a).push(n.url)}return[...c.values()].filter(n=>n.length>1).flat()}function B(s,r,c){let n=t=>({count:t.length,urls:t.slice(0,200)}),e=t=>s.filter(t).map(f=>f.url),a=t=>t.extractionStatus!=="failed",o=t=>a(t)&&t.status===200,g=new Set(s.filter(a).map(t=>t.url)),p=t=>{try{return new URL(t).pathname}catch{return""}},m={"title.missing":n(e(t=>o(t)&&!t.title)),"title.duplicate":n(w(s.filter(o),t=>t.title)),"title.tooLong":n(e(t=>(t.titleLength??0)>60||(t.titlePixels??0)>561)),"title.tooShort":n(e(t=>t.title!=null&&(t.titleLength??0)<30)),"title.sameAsH1":n(e(t=>!!t.title&&!!t.h1&&t.title.trim()===t.h1.trim())),"meta.missing":n(e(t=>o(t)&&!t.metaDescription)),"meta.duplicate":n(w(s.filter(o),t=>t.metaDescription)),"meta.tooLong":n(e(t=>(t.metaDescLength??0)>155)),"meta.tooShort":n(e(t=>t.metaDescription!=null&&(t.metaDescLength??0)<70)),"h1.missing":n(e(t=>o(t)&&!t.h1)),"h1.multiple":n(e(t=>!!t.h1_2)),"h1.duplicate":n(w(s.filter(o),t=>t.h1)),"h1.tooLong":n(e(t=>(t.h1?.length??0)>70)),"h2.missing":n(e(t=>o(t)&&t.h2Count===0)),"crawl.extractionFailed":n(e(t=>!a(t))),"indexability.nonIndexable":n(e(t=>a(t)&&!t.indexable)),"indexability.noindex":n(e(t=>t.indexabilityReason==="noindex")),"canonical.missing":n(e(t=>o(t)&&!t.canonicalUrl)),"canonical.canonicalised":n(e(t=>t.indexabilityReason==="canonicalised")),"response.broken4xx":n(e(t=>(t.status??0)>=400&&(t.status??0)<500)),"response.error5xx":n(e(t=>(t.status??0)>=500)),"response.redirect3xx":n(e(t=>(t.status??0)>=300&&(t.status??0)<400)),"response.noResponse":n(e(t=>t.status===null)),"content.thin":n(e(t=>o(t)&&t.wordCount>0&&t.wordCount<200)),"content.exactDuplicate":n(w(s.filter(t=>o(t)&&t.contentHash),t=>t.contentHash)),"images.missingAlt":n(e(t=>a(t)&&t.imagesMissingAlt>0)),"schema.missing":n(e(t=>o(t)&&t.schemaTypes.length===0)),"url.tooLong":n(e(t=>t.url.length>115)),"url.uppercase":n(e(t=>/[A-Z]/.test(p(t.url)))),"url.underscores":n(e(t=>p(t.url).includes("_"))),"links.orphan":n([...r.values()].filter(t=>t.orphan&&g.has(t.url)).map(t=>t.url))},u=c;if(!u){let t=d=>{try{let i=new URL(d);return i.hash="",(i.origin+i.pathname).replace(/\/+$/,"")+i.search}catch{return d.replace(/#.*$/,"").replace(/\/+$/,"")}},f=new Map(s.map(d=>[t(d.url),d.status]));u=new Set;for(let d of s)for(let i of d.outlinks??[]){if(!i.internal)continue;let b=f.get(t(i.href));b!=null&&b>=400&&u.add(d.url)}}return m["links.brokenInternal"]=n([...u]),m}function C(s,r,c,n){let e=r.length,a=r.filter(i=>i.extractionStatus!=="failed").length,o=e-a,g=i=>i.startsWith("response.broken")||i.startsWith("response.error")||i.startsWith("links.broken")?"\u{1F534}":i.startsWith("title.missing")||i.startsWith("h1.missing")||i.startsWith("indexability")||i.startsWith("canonical.missing")?"\u{1F7E0}":"\u{1F7E1}",p=Object.entries(c).filter(([,i])=>i.count>0).sort((i,b)=>b[1].count-i[1].count).map(([i,b])=>`| ${g(i)} | \`${i}\` | ${b.count} | ${b.urls.slice(0,3).join(" \xB7 ")}${b.urls.length>3?" \u2026":""} |`),m=[...n.values()].map(i=>i.crawlDepth).filter(i=>i!=null),u=m.length?Math.max(...m):0,t=new Set(r.filter(i=>i.extractionStatus!=="failed").map(i=>i.url)),f=[...n.values()].filter(i=>i.orphan&&t.has(i.url)).length,d=[...n.values()].sort((i,b)=>b.inlinks-i.inlinks).slice(0,5);return[`# SEO Crawl Report: ${s}`,`**${e} pages attempted** \xB7 ${a} extracted \xB7 ${o} failed \xB7 max crawl depth ${u} \xB7 ${f} orphan page(s)`,`
|
|
4
4
|
## Issues
|
|
5
5
|
| | Issue | Count | Examples |
|
|
6
6
|
|---|-------|-------|----------|
|
|
7
7
|
${p.join(`
|
|
8
8
|
`)||"| \u2705 | none | 0 | \u2014 |"}`,`
|
|
9
9
|
## Most-linked pages
|
|
10
|
-
${d.map(
|
|
10
|
+
${d.map(i=>`- ${i.inlinks} inlinks \xB7 depth ${i.crawlDepth??"\u2014"} \xB7 ${i.url}`).join(`
|
|
11
11
|
`)}`,`
|
|
12
12
|
_Thresholds: title 60ch/561px, meta 155ch, H1 70ch, thin <200 words, URL 115ch. Pixel widths are estimates._`].join(`
|
|
13
13
|
`)}export{S as a,A as b,_ as c,U as d,X as e,B as f,C as g};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as t,h as o,i as _,j as l}from"./chunk-5LYINW7Z.js";import{a as g}from"./chunk-WP3QS4CJ.js";var F=t.browser_minute/6e4;function de(e){return Math.round(e*F)}var pe=1e3,ue=_,$=t.serp_headful/o,me=t.paa_base/o,ge=t.paa/o,be=t.page_scrape/o,V={billingMode:"credits",chargeEvent:"accepted_write",validationCredits:0,create:{credits:t.listing_create/o,mc:t.listing_create},edit:{credits:t.listing_edit/o,mc:t.listing_edit},surfaces:["local_sourcebook","transparent_commons"],sourcebookAcquisitionIncluded:!0},H=2e6,K=3,v=1e4,y=1e9,j=75,fe=j*o,Q=15e3,Ce=Q/v,Ee=5e3,W="2026-08-03.connected-usage-v3",S=15e3,Re=S*o,I=2,A=2,x=5,N=I*o,k=A*o,M=x*o,he={schemaVersion:2,ratePolicyVersion:W,effectiveAt:"2026-08-03T00:00:00.000Z",provider:"nango",entitlement:"paid_plan_add_on",connection:{billingMode:"flat_recurring_credits",amountCredits:S,interval:"month",unit:"active_connection",proration:"none"},usage:{billingMode:"credits",settlementMode:"authoritative_receipt",rounding:"aggregate_then_ceil_to_mc",functionRun:{credits:I,mc:N,unit:"per_function_run"},proxyRequest:{credits:A,mc:k,unit:"per_proxy_request"},compute:{creditsPerSecond:x,mcPerSecond:M,sourceUnit:"millisecond",unit:"per_compute_second",formula:"ceil(computeMilliseconds * mcPerSecond / 1000)"}}};function b(e,r){if(!Number.isSafeInteger(e)||e<0)throw new Error(`${r} must be a non-negative safe integer`);return BigInt(e)}function p(e){if(e>BigInt(Number.MAX_SAFE_INTEGER))throw new Error("connected usage charge exceeds the safe integer range");return Number(e)}function ye(e){let r=b(e.functionRuns,"functionRuns"),a=b(e.proxyRequests,"proxyRequests"),s=b(e.computeMilliseconds,"computeMilliseconds"),n=r*BigInt(N),c=a*BigInt(k),d=s*BigInt(M),m=d===0n?0n:(d+999n)/1000n,h=n+c+m;return{functionRunMc:p(n),proxyRequestMc:p(c),computeMc:p(m),amountMc:p(h),credits:p(h)/o}}function Te(e,r){if(!Number.isFinite(e)||e<0)throw new Error("costUsd must be a finite non-negative number");if(!Number.isInteger(r)||r<0)throw new Error("markupBps must be a non-negative integer");let s=BigInt(Math.round(e*y))*BigInt(r)*BigInt(H),n=BigInt(y)*BigInt(v)*BigInt(K),c=s===0n?0n:(s+n-1n)/n;if(c>BigInt(Number.MAX_SAFE_INTEGER))throw new Error("calculated mc exceeds the safe integer range");return Number(c)}var Z=[{key:"serp",label:"SERP search",aliases:["search_serp","serp","google search","organic results"],credits:_,unit:"per page (ordinary search)",notes:`search_serp light mode returns one page of organic positions, URLs, titles, and descriptions by default. Unfiltered one-page full mode returns available same-page SERP features. Either mode requests two pages only when pages:2 is explicit. Unfiltered light searches cost ${_} credits per delivered page, or ${l} credits per delivered page if the backup supplies the result. One-page full searches cost ${l} credits. Setting recency to week or month applies Google's date filter through a managed browser and returns organic results only. It costs ${l} credits for one page; filtered pages:2 follows Google's page-two control and costs ${l*2} credits when both pages are delivered. Unfiltered searches use the managed browser only as the last backup. Unfiltered two-page full searches return organic results only for ${_*2} credits, with rich features marked unsupported. If only page 1 is delivered, the search is marked partial and billed for one delivered page. A temporary ${l}-credit-per-requested-page hold is settled to the delivered rate. Location, language, device, and optional module fields do not change ordinary searches. Saved browser identity and capture_serp_snapshot searches retain their ${$}-credit base price; optional page snapshots add ${i(t.page_scrape)} credit per attempted URL.`},{key:"paa",label:"PAA harvest",aliases:["harvest_paa","paa","people also ask","questions"],credits:i(t.paa),unit:`per question (+${i(t.paa_base)} credit base)`,notes:`Returns original-query PAA questions with answers and sources plus organic results and Google entity IDs. Optional same-page SERP modules are flag-controlled. Billed ${i(t.paa_base)} credit base + ${i(t.paa)} per question actually returned (unused estimate refunded).`},{key:"page_scrape",label:"Page crawl / extract",aliases:["extract_url","extract_site","page scrape","url scrape","single page","site crawl"],credits:i(t.page_scrape),unit:"per page",notes:"Applies to both single-URL extraction and per-page site crawls."},{key:"listing_create",label:"Directory or wiki listing creation",aliases:["local-sourcebook-capture","commons_submit_entity","directory listing","wiki listing"],credits:i(t.listing_create),unit:"per new listing",notes:"Fixed total price for a new Local Sourcebook directory listing or Transparent Commons wiki entity. Sourcebook acquisition is included."},{key:"listing_edit",label:"Directory or wiki listing edit",aliases:["local_sourcebook_refresh","commons_submit_entity","listing refresh","wiki edit"],credits:i(t.listing_edit),unit:"per existing listing revision",notes:"Fixed total price for refreshing an existing Local Sourcebook listing or revising an existing Transparent Commons entity."},{key:"url_map",label:"Site URL mapping",aliases:["map_site_urls","url map","site map","crawl urls"],credits:i(t.url_map),unit:"per mapping operation",notes:"Flat rate for the full /map-urls call regardless of URL count discovered."},{key:"yt_channel",label:"YouTube search / channel harvest",aliases:["youtube_harvest","youtube search","youtube channel","yt_channel"],credits:i(t.yt_channel),unit:"per call"},{key:"yt_transcription",label:"YouTube transcription",aliases:["youtube_transcribe","youtube transcript","transcription","yt_transcription"],credits:i(2e4),unit:"base + 2/min of video",notes:"Flat 200-credit base plus 2 credits per minute of video, reconciled to the real video length after transcription (e.g. a 10-minute video is about 220 credits)."},{key:"fb_ad",label:"Facebook search / ad lookup",aliases:["facebook_page_intel","facebook_ad_search","facebook_ad","facebook ads","fb ads"],credits:i(t.fb_ad),unit:"per call"},{key:"maps_search",label:"Maps business search",aliases:["maps_search","google maps search","gmb search","gbp search","business profiles"],credits:i(t.maps_search),unit:"per search",notes:"Returns up to 50 Google Maps business/profile candidates. Use maps_place_intel to hydrate selected businesses."},{key:"maps_place",label:"Maps business lookup",aliases:["maps_place_intel","google maps","maps place","place intel"],credits:i(t.maps_place),unit:"per business",notes:"Maps place runs reserve the 60-credit base plus the declared review maximum before browsing. Completed runs settle to the base plus unique saved reviews; stopped attempts are refunded."},{key:"maps_review",label:"Maps review",aliases:["maps_reviews","google reviews","review cards","reviews"],credits:i(t.maps_review),unit:"per review card",notes:"One credit per unique saved review on a completed Maps place run. The declared maximum is held before browsing; unused credits are refunded."},{key:"fb_search",label:"Facebook ad library search",aliases:["facebook_search","fb_search","fb ad search"],credits:i(t.fb_search),unit:"per search",notes:"Browser automation to search Facebook Ads Library by keyword."},{key:"google_ads_search",label:"Google Ads Transparency search",aliases:["google_ads_search","google ads search","ads transparency search"],credits:i(t.google_ads_search),unit:"per search",notes:"Browser automation to find advertisers in Google Ads Transparency Center by domain or name."},{key:"google_ads_intel",label:"Google Ads Transparency advertiser intel",aliases:["google_ads_page_intel","google ads intel","ads transparency intel"],credits:i(t.google_ads_intel),unit:"per call",notes:"Lists and hydrates an advertiser's creatives with image URLs and video references."},{key:"google_ads_transcribe",label:"Google ad video transcription",aliases:["google_ads_transcribe","google ad transcript"],credits:i(2e4),unit:"base + 2/min of video",notes:"Flat 200-credit base plus 2 credits per minute of video, reconciled to the real video length after transcription (e.g. a 10-minute video is about 220 credits)."},{key:"fb_transcribe",label:"Facebook video / ad transcription",aliases:["facebook_transcribe","facebook_video_transcribe","fb_transcribe","fb ad transcript"],credits:i(2e4),unit:"base + 2/min of video",notes:"Flat 200-credit base plus 2 credits per minute of video, reconciled to the real video length after transcription (e.g. a 10-minute video is about 220 credits)."},{key:"fb_reels_inventory",label:"Facebook Reel URL inventory",aliases:["facebook_reels_inventory","facebook reels","facebook reel urls","facebook profile reels"],credits:i(t.fb_reels_inventory),unit:"per profile scan",notes:"Collects canonical Reel URLs from a Facebook profile. Logged-out scans are capped at 60 URLs; saved browser profiles can request larger inventories."},{key:"tiktok_transcribe",label:"TikTok video transcription",aliases:["tiktok_video_transcribe","tiktok transcript"],credits:i(2e4),unit:"base + 2/min of video",notes:"Transcribes a public TikTok video from its playback media. Flat 200-credit base plus 2 credits per minute, reconciled after transcription."},{key:"instagram_profile",label:"Instagram profile content discovery",aliases:["instagram_profile_content","instagram profile","instagram content list","ig profile"],credits:i(t.instagram_profile),unit:"per profile scan",notes:"Browser extraction of public Instagram profile grid links. Complete history may require a logged-in profile."},{key:"instagram_media",label:"Instagram media download",aliases:["instagram_media_download","instagram reel download","instagram post download","ig media"],credits:i(t.instagram_media),unit:"per post or reel",notes:"Extracts post text, image URLs, and reel audio/video tracks, with local downloads when the server can write files."},{key:"instagram_transcribe",label:"Instagram media transcription",aliases:["instagram transcript","instagram reel transcript","ig transcribe"],credits:i(2e4),unit:"base + 2/min of video",notes:"Whisper transcription of selected Instagram audio/video media. Flat 200-credit base plus 2 credits per minute of video, reconciled to the real video length after transcription (e.g. a 10-minute video is about 220 credits)."},{key:"browser_minute",label:"Interactive browser session",aliases:["browser_open","browser agent","browser_agent","live browser","browse","browser control","interactive browser"],credits:i(t.browser_minute),unit:"per minute of use",notes:"Metered per minute of use for the whole time the browser session is open. Close the session to stop the meter; abandoned sessions are auto-closed after a short idle window."},{key:"reddit_thread",label:"Reddit thread base lookup",aliases:["reddit","reddit_thread","reddit comments","subreddit","reddit post"],credits:i(t.reddit_thread),unit:"per thread",notes:"Base lookup for the post itself. Comments billed separately per comment at reddit_comment rate. Refunded if the thread cannot be retrieved."},{key:"reddit_comment",label:"Reddit comment",aliases:["reddit_comment","reddit comments"],credits:i(t.reddit_comment),unit:"per comment",notes:"Charged per comment actually extracted, billed down automatically if the balance runs out mid-thread."},{key:"video_analysis",label:"Video breakdown (frame-by-frame + transcript)",aliases:["video_frame_analysis","video breakdown","video analysis","analyze video"],credits:i(t.video_analysis),unit:"per 120 frames (max 480)",notes:`Full video breakdown: samples frames across a video up to 60 minutes, analyzes visuals and audio, then produces a summary, pacing, topic outline, key points, hook, style, and replication guidance. Reserves ${i(t.video_analysis)} Credits per requested 120-frame tier, up to 480 frames. Unused frame tiers and failed-run frame charges are refunded. Incurred analysis-model usage is billed separately, including usage before a failure; terminal status checks settle the original run.`},{key:"trustpilot_reviews",label:"Trustpilot review harvest",aliases:["trustpilot_reviews","trustpilot","trustpilot reviews"],credits:i(t.trustpilot_reviews),unit:"per call",notes:"Base lookup. Reviews billed separately per card at trustpilot_review rate. Refunded if no reviews are found."},{key:"trustpilot_review",label:"Trustpilot review card",aliases:["trustpilot_review","trustpilot card"],credits:i(t.trustpilot_review),unit:"per review card",notes:"Charged per review actually extracted, billed down automatically if the balance runs out mid-page."},{key:"g2_reviews",label:"G2 review harvest",aliases:["g2_reviews","g2","g2 reviews"],credits:i(t.g2_reviews),unit:"per call",notes:"Base lookup. Reviews billed separately per card at g2_review rate. Refunded if no reviews are found."},{key:"g2_review",label:"G2 review card",aliases:["g2_review","g2 card"],credits:i(t.g2_review),unit:"per review card",notes:"Charged per review actually extracted (each card carries up to 3 Q&A sections), billed down automatically if the balance runs out mid-page."},{key:"diff_page",label:"Page change check",aliases:["diff_page","page diff","change detection"],credits:i(t.diff_page),unit:"per check",notes:"Same cost as a single page extract \u2014 one scrape is performed per check, then compared against your last stored snapshot for that URL."}],ve="price_1Ta1NRS8aAcsk3TGwsRnYbix";function w(e){return e.billing_interval??"month"}function P(e){return e.billed_usd??e.monthly_usd}function O(e){return e.credits_mc/o}var u={price_1TmiHRS8aAcsk3TGwmSNfNIa:{tier:"starter",label:"Starter",price_id:"price_1TmiHRS8aAcsk3TGwmSNfNIa",monthly_usd:12,credits_mc:8e6,concurrency:3,intro_coupon:"mcp-starter-1dollar-intro-12"},price_1TrgihS8aAcsk3TG5cglrq4D:{tier:"growth",label:"Growth",price_id:"price_1TrgihS8aAcsk3TG5cglrq4D",monthly_usd:40,credits_mc:26666700,concurrency:10,intro_coupon:null},price_1TrgihS8aAcsk3TG4HnG4gbY:{tier:"scale",label:"Scale",price_id:"price_1TrgihS8aAcsk3TG4HnG4gbY",monthly_usd:100,credits_mc:8e7,concurrency:20,intro_coupon:null},price_1TwqIVS8aAcsk3TG0FE3ddDM:{tier:"sonic",label:"Sonic Annual",price_id:"price_1TwqIVS8aAcsk3TG0FE3ddDM",monthly_usd:25,billed_usd:300,credits_mc:25e7,concurrency:3,intro_coupon:null,billing_interval:"year",credits_never_expire:!0,private_offer:!0},price_1TwqIWS8aAcsk3TGbGbtlLak:{tier:"sonic-memory",label:"Sonic Annual + Memory Pro",price_id:"price_1TwqIWS8aAcsk3TGbGbtlLak",monthly_usd:34.5,billed_usd:414,credits_mc:25e7,concurrency:3,intro_coupon:null,billing_interval:"year",credits_never_expire:!0,includes_memory:!0,private_offer:!0}},C=Object.fromEntries(Object.values(u).map(e=>[e.tier,e])),E={sonic:{slug:"sonic",label:"Sonic annual partner offer",headline:"A full year of MCP Scraper, with Credits that never expire.",tier_keys:["sonic","sonic-memory"]}};function X(e){let r=Object.values(E).find(a=>a.tier_keys.includes(e));return r?`/${r.slug}/claim`:null}function z(e){return e?Object.values(E).find(r=>r.tier_keys.includes(e))??null:null}function Se(e,r){let a=z(e);return a?!a.tier_keys.includes(r):!1}function Ie(e){if(!e)return null;let r=C[e];return r?{tier:r.tier,label:r.label,amount_usd:P(r),interval:w(r),monthly_equivalent_usd:r.monthly_usd,credits_per_interval:O(r),concurrency:r.concurrency,credits_never_expire:!!r.credits_never_expire,includes_memory:!!r.includes_memory,private_offer:!!r.private_offer,offer_claim_path:X(r.tier)}:null}function Ae(e){let r=E[e];if(!r)return null;let a=r.tier_keys.map(s=>C[s]).filter(s=>!!s).map(s=>({tier:s.tier,label:s.label,amount_usd:P(s),interval:w(s),monthly_equivalent_usd:s.monthly_usd,credits:O(s),credits_never_expire:!!s.credits_never_expire,includes_memory:!!s.includes_memory,concurrency:s.concurrency}));return a.length?{slug:r.slug,label:r.label,headline:r.headline,options:a}:null}function xe(){let e=C.starter,r=Z.map(({aliases:n,...c})=>({...c,...c.notes?{notes:c.notes.replace(/ via fal\.ai/g,"")}:{}})),a=Object.fromEntries(r.map(n=>[n.key,{credits:n.credits,unit:n.unit,label:n.label,...n.notes?{notes:n.notes}:{}}])),s=Object.values(u).filter(n=>!n.private_offer).map(n=>({tier:n.tier,label:n.label,monthlyUsd:n.monthly_usd,credits:n.credits_mc/o,concurrency:n.concurrency}));return{...g,operationCosts:a,publicTiers:s,concurrencyPack:{ratePolicyVersion:B,monthlyUsd:U,currency:L,interval:D,billingUnit:"pack",slotsPerPack:f},scaleCreditPack:{ratePolicyVersion:ee,eligibleTier:"scale",incrementUsd:G,creditsPerIncrement:R,maxQuantity:re,currency:"usd",expiryMonths:3},creditsPerDollarStarter:Math.round(i(e.credits_mc)/e.monthly_usd),costs:r,scheduling:g.addons.scheduling,connectedAccounts:g.addons.connectedAccounts,listingWrites:V}}var U=5,L="usd",D="month",f=2,B="2026-08-14.concurrency-pack-v1",J="$5/month for 2 extra browsers",Ne="MCP Scraper browser concurrency pack \u2014 each $5 monthly pack adds 2 browsers.",ke="Billed monthly until canceled. A receipt is emailed after each successful payment.",T="npx -y -p mcp-scraper@latest mcp-scraper-cli billing concurrency checkout",ee="2026-09-21.scale-credit-pack-v1",G=10,R=8e4,Me=R*o,re=100;function we(){let e=process.env.CONCURRENCY_BILLING_URL??"https://mcpscraper.dev/billing";return{product:"Extra browser concurrency pack",rate_policy_version:B,price_label:J,unit_amount_usd:U,currency:L,interval:D,billing_unit:"pack",pack_quantity:1,slots_per_pack:f,extra_slots:f,billing_url:e,terminal_command:T,terminal_command_with_api_key_env:`MCP_SCRAPER_API_KEY=sk_live_your_key ${T}`}}function i(e){return e/o}var te="npx -y -p mcp-scraper@latest mcp-scraper-cli billing subscribe";function ie(){return Object.values(u).filter(e=>!e.private_offer).sort((e,r)=>e.credits_mc-r.credits_mc)}function Pe(e,r){let a=process.env.TOPUP_URL??"https://mcpscraper.dev/billing",s=i(e),n=i(r),c=ie().map(m=>m.tier).join("|"),d=`${te} <${c}>`;return{error:"insufficient_balance",error_code:"insufficient_balance",message:`Insufficient credits. Balance: ${s} credits. This call requires ${n} credits. Credits refresh when your plan renews. Scale customers can buy ${R} Credits per $${G} increment; other customers can move to a larger plan at ${a} (terminal: ${d})`,balance_credits:s,required_credits:n,topup_url:a,upgrade_command:d}}var ne=1e7*1,Oe={price_1TnMSTS8aAcsk3TGBgiwuvqL:{plan:"pro",label:"Pro",price_id:"price_1TnMSTS8aAcsk3TGBgiwuvqL",interval:"month",monthly_usd:19,quota_bytes:5e9},price_1TnMSTS8aAcsk3TGWBVU2agY:{plan:"pro",label:"Pro",price_id:"price_1TnMSTS8aAcsk3TGWBVU2agY",interval:"year",monthly_usd:19,quota_bytes:5e9}},Ue={free:ne,pro:5e9,team:5e10},se=3;var Le=19/se,De=3,Be=1.5,Ge=1.5,Y=2e4,q=200;function Ye(e){let r=Math.max(1,Math.ceil((e||60)/60));return Y+q*r}var qe=Y+q*30,Fe=u.price_1TmiHRS8aAcsk3TGwmSNfNIa.credits_mc/u.price_1TmiHRS8aAcsk3TGwmSNfNIa.monthly_usd;export{de as a,pe as b,ue as c,$ as d,me as e,ge as f,be as g,V as h,y as i,j,fe as k,Q as l,Ce as m,Ee as n,W as o,S as p,Re as q,I as r,A as s,x as t,N as u,he as v,ye as w,Te as x,Z as y,ve as z,w as A,P as B,O as C,u as D,C as E,X as F,z as G,Se as H,Ie as I,Ae as J,xe as K,U as L,f as M,Ne as N,ke as O,G as P,R as Q,Me as R,re as S,we as T,i as U,Pe as V,Oe as W,Ue as X,De as Y,Be as Z,Ge as _,Y as $,q as aa,Ye as ba,qe as ca,Fe as da};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var o="0.96.1";export{o as a};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import{a as s}from"./chunk-WP3QS4CJ.js";var _={message:"PAA progress checks now wait at least 20 seconds. Expanded answers preserve loaded lists, mathematical notation and sources, and extraction avoids repeated reads and clicks. Full SERP responses now show initial PAA questions, available snippets and source labels."};var c={reset:"\x1B[0m",cyan:"\x1B[36m",lime:"\x1B[32m",amber:"\x1B[33m",red:"\x1B[31m",muted:"\x1B[90m",bold:"\x1B[1m"};function r(t,e,n){return n?`${c[e]}${t}${c.reset}`:t}function o(t,e,n){let a=t.padEnd(9," ");return` ${r(a,"muted",n)} ${e.join(r(" . ","muted",n))}`}function b(t){let e=t.color??!0,n=t.apiKeyConfigured?"$MCP_SCRAPER_API_KEY":"sk_live_your_key",a=String.raw`
|
|
2
|
+
__ __ ____ ____
|
|
3
|
+
| \/ |/ ___| _ \
|
|
4
|
+
| |\/| | | | |_) |
|
|
5
|
+
| | | | |___| __/
|
|
6
|
+
|_| |_|\____|_|
|
|
7
|
+
|
|
8
|
+
____ ____ ____ _ ____ _____ ____
|
|
9
|
+
/ ___| / ___| _ \ / \ | _ \| ____| _ \
|
|
10
|
+
\___ \| | | |_) | / _ \ | |_) | _| | |_) |
|
|
11
|
+
___) | |___| _ < / ___ \| __/| |___| _ <
|
|
12
|
+
|____/ \____|_| \_\/_/ \_\_| |_____|_| \_\
|
|
13
|
+
`,i=[`MCP_SCRAPER_API_KEY=${n} npx -y -p mcp-scraper@latest \\`," mcp-scraper-cli agent install claude --apply"].join(`
|
|
14
|
+
`),l=["[mcp_servers.mcp-scraper]",'command = "npx"','args = ["-y", "-p", "mcp-scraper@latest", "mcp-scraper"]',`env = { MCP_SCRAPER_API_KEY = "${n}" }`].join(`
|
|
15
|
+
`);return[r(`mcp-scraper v${t.version}`,"bold",e),r("> mcp-scraper-install","muted",e),r(a,"amber",e),`${r("MCP Scraper Agent","cyan",e)} . v${t.version} . mcpscraper.dev`,"1/1 install surfaces ready",r(`Newest in v${t.version}: ${_.message}`,"lime",e),"",`${r("Tools","cyan",e)} ${r(`(${s.inventory.totalTools} MCP tools)`,"muted",e)}`,o("search",["harvest_paa","search_serp","maps_search","maps_place_intel"],e),o("extract",["extract_url","map_site_urls","extract_site","audit_site","directory_workflow"],e),o("build",["create_editorial_reading_room","rank_tracker_workflow","portable HTML"],e),o("media",["youtube_harvest","youtube_transcribe","facebook_reels_inventory","facebook_ad_search","facebook_page_intel","facebook_ad_transcribe","facebook_video_transcribe","tiktok_video_transcribe","instagram_profile_content","instagram_media_download","reddit_thread"],e),o("browser",["serp_identity_create","serp_identity_list","browser_open","browser_profile_connect","browser_profile_list","browser_close","browser_screenshot","browser_read","browser_locate","browser_replay_mark","browser_replay_annotate"],e),o("connect",["list_service_connections","describe_service_connection_tool","import_service_connection_to_memory","export_connected_service_data","renew_connected_data_download","read_service_connection","call_service_connection_action"],e),o("commons",["commons_search_entities","commons_get_entity_linkset","commons_prepare_entity","commons_submit_entity","commons_prepare_publication","commons_claim_publication","commons_publish_editorial","commons_get_publication"],e),o("account",["credits_info","reports","MCP resources"],e),o("memory",["memory-put","memory-get","memory-search","list-vaults","record-fact","list-scheduled-actions"],e),`${r("Workflows","cyan",e)} ${r("(MCP + CLI + API)","muted",e)}`,o("route",["workflow_list","workflow_suggest","workflow_run","workflow_step","workflow_status","workflow_artifact_read"],e),o("seo",["directory","agent-packet","competitive audit","map/serp comparison","PAA/AIO briefs","scheduled runs"],e),"",r("Usage tips:","amber",e),"Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.","Run mcp-scraper in a human terminal to print this card; MCP clients get the same command as a silent stdio server.","Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install","Hosted browser sessions use direct/no-proxy egress by default.","Customer auth setup: run browser_profile_connect, send the watch_url, let the user sign in, then call browser_profile_list until AUTHENTICATED.","Connected account ranges: call export_connected_service_data once. It handles Gmail, Calendar, Zoom, and Resend pagination; do not loop read_service_connection over individual records.","Connected account RAG: call import_service_connection_to_memory for one bounded approved read. It writes a redacted, untrusted snapshot to a stable Memory path and embeds it for search.","Stack logins / reconnect: run browser_profile_connect again with the same profile name and another domain to add accounts or refresh a login.","Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.","For MCP clients, use mcp-scraper so one install can mix SERP, Maps, browser, reports, and saved MCP resources.","If you hit the concurrency limit, add 2 browsers for $5/month with mcp-scraper-cli billing concurrency checkout.","",`${r("Ready.","lime",e)} Install the combined MCP server with one command:`,"",r("Setup doctor","amber",e),"npx -y -p mcp-scraper@latest mcp-scraper-cli doctor","",r("Hosted profile setup","amber",e),'In your MCP client, call browser_profile_connect with email="seo@example.com" and domain="chatgpt.com".',"Give the returned watch_url to the user. After they sign in, call browser_profile_list, then browser_open with the returned profile. Add more logins by calling browser_profile_connect again with the same profile and a new domain.","",r("Claude Code one-command setup","amber",e),i,"Then fully exit Claude Code and open a new Claude terminal. Check with: claude mcp list","",r("Codex config","amber",e),l,"",r("Claude Desktop Extension","amber",e),"Download: https://mcpscraper.dev/downloads/mcp-scraper.mcpb","",r("Safety note:","muted",e),"mcp-scraper prints this card only when stdin/stdout are an interactive TTY. In MCP clients it writes only JSON-RPC to stdout.","Use --stdio or MCP_SCRAPER_FORCE_STDIO=1 to force server mode from a terminal.",""].join(`
|
|
16
|
+
`)}export{b as a};
|