mcp-scraper 0.81.0 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +2 -2
  3. package/dist/{analytics-repository-YOY5KMOD.js → analytics-repository-GA6P5S5J.js} +1 -1
  4. package/dist/bin/api-server.js +3 -3
  5. package/dist/bin/mcp-scraper-cli.js +61 -46
  6. package/dist/bin/mcp-scraper-core.js +6 -6
  7. package/dist/bin/mcp-scraper-install.js +2 -2
  8. package/dist/bin/mcp-stdio-server.js +6 -6
  9. package/dist/{chunk-PMONKQGJ.js → chunk-4NZ6JFK5.js} +2 -2
  10. package/dist/{chunk-VEQPA65D.js → chunk-ACDNW7TG.js} +1 -1
  11. package/dist/{chunk-Z63DQAO7.js → chunk-AIVNZZO3.js} +4 -3
  12. package/dist/{chunk-VXLU74YZ.js → chunk-BGD7U3AE.js} +207 -1
  13. package/dist/{chunk-HEUMDX4I.js → chunk-CNAJWRJT.js} +131 -1
  14. package/dist/{chunk-RSXDHQBA.js → chunk-FUG2IRKP.js} +1 -1
  15. package/dist/{chunk-XBEUZJHS.js → chunk-HGJJQY24.js} +49 -5
  16. package/dist/{chunk-XWXCLKXF.js → chunk-QSZXAA5F.js} +3107 -2210
  17. package/dist/{chunk-M2A7YSJV.js → chunk-UXVT7FWW.js} +2 -2
  18. package/dist/{chunk-VWC7XATD.js → chunk-ZHPGPVJJ.js} +1 -1
  19. package/dist/{extract-bundle-232SA7MI.js → extract-bundle-HTWWJD6N.js} +2 -2
  20. package/dist/{gmail-service-2A3ZZT3M.js → gmail-service-VXE7OYNA.js} +2 -2
  21. package/dist/{server-QRYEMF4H.js → server-ICMRDYI4.js} +2111 -791
  22. package/dist/{site-extract-repository-VDONDDMR.js → site-extract-repository-Y5WQFSQW.js} +6 -2
  23. package/dist/{worker-2MY77WYY.js → worker-FM3ZRDY4.js} +1 -1
  24. package/package.json +6 -2
  25. package/public/.well-known/xray-install.json +178 -0
  26. package/public/agent-install.md +194 -0
@@ -3263,9 +3263,139 @@ async function migrateAnalytics() {
3263
3263
  await client.query(
3264
3264
  "CREATE INDEX IF NOT EXISTS analytics_spend_facts_hierarchy ON analytics_ad_spend_facts(site_id, platform, account_id, campaign_id, ad_set_id, ad_id, bucket DESC)"
3265
3265
  );
3266
+ await client.query(`
3267
+ CREATE TABLE IF NOT EXISTS analytics_runtime_manifests (
3268
+ manifest_id uuid PRIMARY KEY,
3269
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3270
+ revision integer NOT NULL CHECK (revision > 0),
3271
+ digest text NOT NULL CHECK (digest ~ '^[a-f0-9]{64}$'),
3272
+ manifest jsonb NOT NULL,
3273
+ is_current boolean NOT NULL DEFAULT false,
3274
+ created_by_user_id bigint NOT NULL,
3275
+ expires_at timestamptz NOT NULL,
3276
+ created_at timestamptz NOT NULL DEFAULT now(),
3277
+ UNIQUE(site_id, revision)
3278
+ )
3279
+ `);
3280
+ await client.query("CREATE UNIQUE INDEX IF NOT EXISTS analytics_runtime_manifests_one_current ON analytics_runtime_manifests(site_id) WHERE is_current");
3281
+ await client.query("CREATE INDEX IF NOT EXISTS analytics_runtime_manifests_site_created ON analytics_runtime_manifests(site_id, created_at DESC)");
3282
+ await client.query(`
3283
+ CREATE TABLE IF NOT EXISTS analytics_runtime_manifest_publications (
3284
+ publication_id uuid PRIMARY KEY,
3285
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3286
+ manifest_id uuid NOT NULL REFERENCES analytics_runtime_manifests(manifest_id) ON DELETE CASCADE,
3287
+ setup_id uuid,
3288
+ setup_revision integer,
3289
+ idempotency_key text NOT NULL,
3290
+ request_fingerprint text NOT NULL CHECK (request_fingerprint ~ '^[a-f0-9]{64}$'),
3291
+ manifest jsonb NOT NULL,
3292
+ published_at timestamptz NOT NULL DEFAULT now(),
3293
+ UNIQUE(site_id, idempotency_key)
3294
+ )
3295
+ `);
3296
+ await client.query(`
3297
+ CREATE TABLE IF NOT EXISTS analytics_runtime_rule_health (
3298
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3299
+ manifest_id uuid NOT NULL REFERENCES analytics_runtime_manifests(manifest_id) ON DELETE CASCADE,
3300
+ rule_id uuid NOT NULL,
3301
+ rule_version integer NOT NULL,
3302
+ route_path text NOT NULL,
3303
+ selector_quality text NOT NULL CHECK (selector_quality IN ('portable','stable','fragile')),
3304
+ match_count integer NOT NULL CHECK (match_count BETWEEN 0 AND 100),
3305
+ structural_fingerprint text NOT NULL CHECK (structural_fingerprint ~ '^[a-f0-9]{64}$'),
3306
+ observed_at timestamptz NOT NULL,
3307
+ PRIMARY KEY(manifest_id, rule_id, route_path)
3308
+ )
3309
+ `);
3310
+ await client.query(`
3311
+ CREATE TABLE IF NOT EXISTS analytics_experiments (
3312
+ experiment_id uuid PRIMARY KEY,
3313
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3314
+ current_revision integer NOT NULL CHECK (current_revision > 0),
3315
+ state text NOT NULL CHECK (state IN ('draft','approved','canary','active','paused','killed','archived')),
3316
+ created_by_user_id bigint NOT NULL,
3317
+ create_idempotency_key text,
3318
+ create_request_fingerprint text,
3319
+ started_at timestamptz,
3320
+ created_at timestamptz NOT NULL DEFAULT now(),
3321
+ updated_at timestamptz NOT NULL DEFAULT now()
3322
+ )
3323
+ `);
3324
+ await client.query("ALTER TABLE analytics_experiments ADD COLUMN IF NOT EXISTS create_idempotency_key text");
3325
+ await client.query("ALTER TABLE analytics_experiments ADD COLUMN IF NOT EXISTS create_request_fingerprint text");
3326
+ await client.query("ALTER TABLE analytics_experiments ADD COLUMN IF NOT EXISTS started_at timestamptz");
3327
+ await client.query("CREATE UNIQUE INDEX IF NOT EXISTS analytics_experiments_site_create_idempotency ON analytics_experiments(site_id, create_idempotency_key) WHERE create_idempotency_key IS NOT NULL");
3328
+ await client.query(`
3329
+ CREATE TABLE IF NOT EXISTS analytics_experiment_revisions (
3330
+ experiment_id uuid NOT NULL REFERENCES analytics_experiments(experiment_id) ON DELETE CASCADE,
3331
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3332
+ revision integer NOT NULL CHECK (revision > 0),
3333
+ plan jsonb NOT NULL,
3334
+ approved_by_user_id bigint,
3335
+ approved_at timestamptz,
3336
+ created_at timestamptz NOT NULL DEFAULT now(),
3337
+ PRIMARY KEY(experiment_id, revision)
3338
+ )
3339
+ `);
3340
+ await client.query(`
3341
+ CREATE TABLE IF NOT EXISTS analytics_experiment_receipts (
3342
+ receipt_id uuid PRIMARY KEY,
3343
+ experiment_id uuid NOT NULL REFERENCES analytics_experiments(experiment_id) ON DELETE CASCADE,
3344
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3345
+ revision integer NOT NULL,
3346
+ operation text NOT NULL,
3347
+ idempotency_key text,
3348
+ receipt jsonb NOT NULL,
3349
+ occurred_at timestamptz NOT NULL DEFAULT now(),
3350
+ UNIQUE(experiment_id, idempotency_key)
3351
+ )
3352
+ `);
3353
+ await client.query("CREATE INDEX IF NOT EXISTS analytics_experiments_site_updated ON analytics_experiments(site_id, updated_at DESC)");
3354
+ await client.query(`
3355
+ CREATE TABLE IF NOT EXISTS analytics_site_setups (
3356
+ setup_id uuid PRIMARY KEY,
3357
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3358
+ owner_user_id bigint NOT NULL,
3359
+ revision integer NOT NULL CHECK (revision > 0),
3360
+ status text NOT NULL,
3361
+ start_url text NOT NULL,
3362
+ state jsonb NOT NULL,
3363
+ expires_at timestamptz NOT NULL,
3364
+ created_at timestamptz NOT NULL,
3365
+ updated_at timestamptz NOT NULL
3366
+ )
3367
+ `);
3368
+ await client.query(`
3369
+ CREATE TABLE IF NOT EXISTS analytics_site_setup_revisions (
3370
+ setup_id uuid NOT NULL REFERENCES analytics_site_setups(setup_id) ON DELETE CASCADE,
3371
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3372
+ owner_user_id bigint NOT NULL,
3373
+ revision integer NOT NULL CHECK (revision > 0),
3374
+ status text NOT NULL,
3375
+ state jsonb NOT NULL,
3376
+ created_at timestamptz NOT NULL,
3377
+ PRIMARY KEY(setup_id, revision)
3378
+ )
3379
+ `);
3380
+ await client.query(`
3381
+ CREATE TABLE IF NOT EXISTS analytics_site_setup_receipts (
3382
+ receipt_id uuid PRIMARY KEY,
3383
+ setup_id uuid NOT NULL REFERENCES analytics_site_setups(setup_id) ON DELETE CASCADE,
3384
+ site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,
3385
+ owner_user_id bigint NOT NULL,
3386
+ revision integer NOT NULL,
3387
+ operation text NOT NULL,
3388
+ status text NOT NULL,
3389
+ receipt jsonb NOT NULL,
3390
+ occurred_at timestamptz NOT NULL
3391
+ )
3392
+ `);
3393
+ await client.query("CREATE INDEX IF NOT EXISTS analytics_site_setups_owner_updated ON analytics_site_setups(owner_user_id, updated_at DESC)");
3394
+ await client.query("CREATE INDEX IF NOT EXISTS analytics_site_setups_site_updated ON analytics_site_setups(site_id, updated_at DESC)");
3395
+ await client.query("CREATE INDEX IF NOT EXISTS analytics_site_setup_receipts_setup_revision ON analytics_site_setup_receipts(setup_id, revision, occurred_at)");
3266
3396
  await client.query(`
3267
3397
  INSERT INTO analytics_schema_migrations(version)
3268
- VALUES ('2026-08-05.1'), ('2026-08-05.2'), ('2026-08-05.3'), ('2026-08-05.4'), ('2026-08-05.5'), ('2026-08-05.6'), ('2026-08-05.7'), ('2026-08-05.8'), ('2026-08-05.9'), ('2026-08-05.10'), ('2026-08-05.11'), ('2026-08-05.12'), ('2026-08-05.13'), ('2026-08-05.14'), ('2026-08-26.1'), ('2026-08-26.2'), ('2026-08-26.3'), ('2026-08-26.4'), ('2026-08-27.1'), ('2026-08-28.1'), ('2026-08-28.2'), ('2026-08-28.3'), ('2026-08-28.4')
3398
+ VALUES ('2026-08-05.1'), ('2026-08-05.2'), ('2026-08-05.3'), ('2026-08-05.4'), ('2026-08-05.5'), ('2026-08-05.6'), ('2026-08-05.7'), ('2026-08-05.8'), ('2026-08-05.9'), ('2026-08-05.10'), ('2026-08-05.11'), ('2026-08-05.12'), ('2026-08-05.13'), ('2026-08-05.14'), ('2026-08-26.1'), ('2026-08-26.2'), ('2026-08-26.3'), ('2026-08-26.4'), ('2026-08-27.1'), ('2026-08-28.1'), ('2026-08-28.2'), ('2026-08-28.3'), ('2026-08-28.4'), ('2026-08-31.1'), ('2026-09-01.1')
3269
3399
  ON CONFLICT (version) DO NOTHING
3270
3400
  `);
3271
3401
  } catch (error) {
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var PACKAGE_VERSION = "0.81.0";
2
+ var PACKAGE_VERSION = "0.83.0";
3
3
 
4
4
  export {
5
5
  PACKAGE_VERSION
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-DNM65UCK.js";
8
8
  import {
9
9
  LedgerOperation
10
- } from "./chunk-PMONKQGJ.js";
10
+ } from "./chunk-4NZ6JFK5.js";
11
11
  import {
12
12
  CREDIT_LOT_TTL,
13
13
  getDb,
@@ -255,6 +255,7 @@ function createSiteExtractContentReader(ownerId) {
255
255
  }
256
256
 
257
257
  // src/api/site-extract-repository.ts
258
+ var XRAY_ENTITLED_EXTRACT_BILLING_CLASS = "xray_entitlement";
258
259
  function extractJobLimitInfo(job) {
259
260
  const effectiveMaxPages = Math.max(1, Number(job.options.effectiveMaxPages ?? job.options.maxPages ?? 1));
260
261
  const requestedMaxPages = Math.max(effectiveMaxPages, Number(job.options.requestedMaxPages ?? effectiveMaxPages));
@@ -366,16 +367,31 @@ async function listFundedPendingExtractJobs(limit = 25, staleMinutes = 2) {
366
367
  const safeStaleMinutes = Math.max(1, Math.min(60, Math.round(staleMinutes)));
367
368
  const res = await db.execute({
368
369
  sql: `SELECT j.* FROM site_extract_jobs j
369
- JOIN billing_debits d
370
+ LEFT JOIN billing_debits d
370
371
  ON d.idempotency_key = json_extract(j.options, '$.debitKey')
371
372
  AND d.user_id = j.user_id
372
373
  AND d.status = 'applied'
373
374
  WHERE j.status = 'pending'
374
375
  AND j.billed_mc IS NULL
376
+ AND (
377
+ d.idempotency_key IS NOT NULL
378
+ OR (
379
+ json_extract(j.options, '$.billingClass') = ?
380
+ AND json_extract(j.options, '$.heldMc') = 0
381
+ AND json_extract(j.options, '$.debitKey') IS NULL
382
+ AND json_extract(j.options, '$.xraySetupReceipt.billingClass') = ?
383
+ AND json_extract(j.options, '$.xraySetupReceipt.consumesMcpScraperCredits') = 0
384
+ )
385
+ )
375
386
  AND (j.next_dispatch_at IS NULL OR j.next_dispatch_at <= datetime('now'))
376
387
  AND j.updated_at <= datetime('now', ?)
377
388
  ORDER BY j.updated_at ASC LIMIT ?`,
378
- args: [`-${safeStaleMinutes} minutes`, Math.max(1, Math.min(100, Math.round(limit)))]
389
+ args: [
390
+ XRAY_ENTITLED_EXTRACT_BILLING_CLASS,
391
+ XRAY_ENTITLED_EXTRACT_BILLING_CLASS,
392
+ `-${safeStaleMinutes} minutes`,
393
+ Math.max(1, Math.min(100, Math.round(limit)))
394
+ ]
379
395
  });
380
396
  return res.rows.map((row) => rowToJob(row));
381
397
  }
@@ -687,16 +703,24 @@ async function failUnfundedExtractJob(jobId, error, publicError) {
687
703
  });
688
704
  }
689
705
  async function setExtractJobPublicError(jobId, publicError) {
706
+ const job = await getExtractJob(jobId);
707
+ const normalized = job?.options.billingClass === XRAY_ENTITLED_EXTRACT_BILLING_CLASS && publicError ? { ...publicError, charge_status: "not_charged" } : publicError;
690
708
  await getDb().execute({
691
709
  sql: `UPDATE site_extract_jobs SET public_error_json = ?, updated_at = datetime('now')
692
710
  WHERE id = ? AND status IN ('complete', 'partial', 'failed')`,
693
- args: [serializePublicErrorEnvelope(publicError), jobId]
711
+ args: [serializePublicErrorEnvelope(normalized), jobId]
694
712
  });
695
713
  }
696
714
  async function settleExtractJob(jobId, userId, refundMc, netChargeMc, reference) {
697
715
  const db = getDb();
698
716
  const job = await getExtractJob(jobId);
699
717
  if (!job || job.billedMc != null) return;
718
+ if (job.options.billingClass === XRAY_ENTITLED_EXTRACT_BILLING_CLASS) {
719
+ if (!await finalizeXRayEntitledExtractJobCharge(jobId)) {
720
+ throw new Error("xray_extract_zero_charge_finalization_rejected");
721
+ }
722
+ return;
723
+ }
700
724
  const debitKey = typeof job.options.debitKey === "string" ? job.options.debitKey : null;
701
725
  if (debitKey) {
702
726
  const settlement = await settleDebitMcIdempotent(
@@ -757,6 +781,24 @@ async function settleExtractJob(jobId, userId, refundMc, netChargeMc, reference)
757
781
  }
758
782
  ], "write");
759
783
  }
784
+ async function finalizeXRayEntitledExtractJobCharge(jobId) {
785
+ const db = getDb();
786
+ const result = await db.execute({
787
+ sql: `UPDATE site_extract_jobs
788
+ SET billed_mc = 0, updated_at = datetime('now')
789
+ WHERE id = ?
790
+ AND billed_mc IS NULL
791
+ AND json_extract(options, '$.billingClass') = ?
792
+ AND json_extract(options, '$.heldMc') = 0
793
+ AND json_extract(options, '$.debitKey') IS NULL
794
+ AND json_extract(options, '$.xraySetupReceipt.billingClass') = ?
795
+ AND json_extract(options, '$.xraySetupReceipt.consumesMcpScraperCredits') = 0
796
+ AND json_extract(options, '$.xraySetupReceipt.heldMc') = 0
797
+ AND json_extract(options, '$.xraySetupReceipt.billedMc') = 0`,
798
+ args: [jobId, XRAY_ENTITLED_EXTRACT_BILLING_CLASS, XRAY_ENTITLED_EXTRACT_BILLING_CLASS]
799
+ });
800
+ return result.rowsAffected === 1;
801
+ }
760
802
 
761
803
  export {
762
804
  SITE_EXTRACT_ARTIFACT_PREFIX,
@@ -767,6 +809,7 @@ export {
767
809
  readOwnedSiteExtractImageArtifact,
768
810
  cleanupExpiredSiteExtractArtifacts,
769
811
  createSiteExtractContentReader,
812
+ XRAY_ENTITLED_EXTRACT_BILLING_CLASS,
770
813
  extractJobLimitInfo,
771
814
  terminalExtractJobStatus,
772
815
  createExtractJob,
@@ -795,5 +838,6 @@ export {
795
838
  failExtractJob,
796
839
  failUnfundedExtractJob,
797
840
  setExtractJobPublicError,
798
- settleExtractJob
841
+ settleExtractJob,
842
+ finalizeXRayEntitledExtractJobCharge
799
843
  };