mcp-scraper 0.4.5 → 0.4.6

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 (47) hide show
  1. package/dist/bin/api-server.cjs +315 -6
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +3 -3
  4. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  5. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  6. package/dist/bin/mcp-scraper-cli.js +1 -1
  7. package/dist/bin/mcp-scraper-install.cjs +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-install.js +1 -1
  10. package/dist/bin/mcp-stdio-server.cjs +89 -1
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +2 -2
  13. package/dist/bin/paa-harvest.cjs.map +1 -1
  14. package/dist/bin/paa-harvest.js +2 -2
  15. package/dist/chunk-5BB6Y3BB.js +7 -0
  16. package/dist/chunk-5BB6Y3BB.js.map +1 -0
  17. package/dist/{chunk-NJK5BTUK.js → chunk-5GAD2AN2.js} +2 -2
  18. package/dist/{chunk-FUVQR6GK.js → chunk-GL4BW4CP.js} +49 -1
  19. package/dist/chunk-GL4BW4CP.js.map +1 -0
  20. package/dist/{chunk-3UH3BEIZ.js → chunk-HE2LQPJ2.js} +2 -2
  21. package/dist/{chunk-WI4A3KFQ.js → chunk-IFXACD4K.js} +90 -2
  22. package/dist/chunk-IFXACD4K.js.map +1 -0
  23. package/dist/{chunk-5LO6EHEH.js → chunk-IVQYNY45.js} +14 -3
  24. package/dist/chunk-IVQYNY45.js.map +1 -0
  25. package/dist/{chunk-4ZZWFI6L.js → chunk-NYAWN7CZ.js} +3 -3
  26. package/dist/{db-H3S3M6KK.js → db-LIOTIWVN.js} +6 -2
  27. package/dist/{extract-bundle-UKE273TV.js → extract-bundle-COS56ZDO.js} +2 -2
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.js +2 -2
  30. package/dist/{server-XCDFHHLE.js → server-R3KVQNOT.js} +170 -13
  31. package/dist/server-R3KVQNOT.js.map +1 -0
  32. package/dist/{site-extract-repository-THBVEXMP.js → site-extract-repository-LI2S3S6E.js} +4 -4
  33. package/dist/{worker-YHKUJR5P.js → worker-VASAJ7FZ.js} +5 -5
  34. package/package.json +2 -1
  35. package/dist/chunk-5LO6EHEH.js.map +0 -1
  36. package/dist/chunk-FUVQR6GK.js.map +0 -1
  37. package/dist/chunk-UGQC2FOX.js +0 -7
  38. package/dist/chunk-UGQC2FOX.js.map +0 -1
  39. package/dist/chunk-WI4A3KFQ.js.map +0 -1
  40. package/dist/server-XCDFHHLE.js.map +0 -1
  41. /package/dist/{chunk-NJK5BTUK.js.map → chunk-5GAD2AN2.js.map} +0 -0
  42. /package/dist/{chunk-3UH3BEIZ.js.map → chunk-HE2LQPJ2.js.map} +0 -0
  43. /package/dist/{chunk-4ZZWFI6L.js.map → chunk-NYAWN7CZ.js.map} +0 -0
  44. /package/dist/{db-H3S3M6KK.js.map → db-LIOTIWVN.js.map} +0 -0
  45. /package/dist/{extract-bundle-UKE273TV.js.map → extract-bundle-COS56ZDO.js.map} +0 -0
  46. /package/dist/{site-extract-repository-THBVEXMP.js.map → site-extract-repository-LI2S3S6E.js.map} +0 -0
  47. /package/dist/{worker-YHKUJR5P.js.map → worker-VASAJ7FZ.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  harvest
3
- } from "./chunk-NJK5BTUK.js";
3
+ } from "./chunk-5GAD2AN2.js";
4
4
  import "./chunk-HPV4VOQX.js";
5
5
  import "./chunk-K443GQY5.js";
6
6
  import "./chunk-XGIPATLV.js";
7
7
  import "./chunk-M2S27J6Z.js";
8
- import "./chunk-FUVQR6GK.js";
8
+ import "./chunk-GL4BW4CP.js";
9
9
 
10
10
  // src/video/VideoGenerator.ts
11
11
  import { execSync as execSync2 } from "child_process";
@@ -3,7 +3,7 @@ import {
3
3
  createHarvestAttemptRecorder,
4
4
  harvestProblemResponse,
5
5
  serializeHarvestProblem
6
- } from "./chunk-3UH3BEIZ.js";
6
+ } from "./chunk-HE2LQPJ2.js";
7
7
  import {
8
8
  BrowserDriver,
9
9
  MapsSelectors,
@@ -16,7 +16,7 @@ import {
16
16
  recordVendorUsage,
17
17
  resolveKernelProxyId,
18
18
  runWithCostContext
19
- } from "./chunk-NJK5BTUK.js";
19
+ } from "./chunk-5GAD2AN2.js";
20
20
  import {
21
21
  HttpMcpToolExecutor,
22
22
  MemoryMcpToolExecutor,
@@ -31,7 +31,7 @@ import {
31
31
  registerSerpIntelligenceCaptureTools,
32
32
  sanitizeAttempts,
33
33
  sanitizeHarvestResult
34
- } from "./chunk-WI4A3KFQ.js";
34
+ } from "./chunk-IFXACD4K.js";
35
35
  import {
36
36
  auditImages,
37
37
  getBlobStore
@@ -65,7 +65,7 @@ import {
65
65
  RawMapsOverviewSchema,
66
66
  RawMapsReviewStatsSchema
67
67
  } from "./chunk-XGIPATLV.js";
68
- import "./chunk-UGQC2FOX.js";
68
+ import "./chunk-5BB6Y3BB.js";
69
69
  import {
70
70
  completeExtractJob,
71
71
  countSuccessfulPages,
@@ -76,7 +76,7 @@ import {
76
76
  saveExtractPages,
77
77
  setExtractJobTotal,
78
78
  settleExtractJob
79
- } from "./chunk-4ZZWFI6L.js";
79
+ } from "./chunk-NYAWN7CZ.js";
80
80
  import {
81
81
  BROWSER_OPEN_MIN_BALANCE_MC,
82
82
  CONCURRENCY_PRICE_ID,
@@ -95,7 +95,7 @@ import {
95
95
  browserActiveCostMc,
96
96
  concurrencySlotBillingInfo,
97
97
  insufficientBalanceResponse
98
- } from "./chunk-5LO6EHEH.js";
98
+ } from "./chunk-IVQYNY45.js";
99
99
  import {
100
100
  CaptchaError,
101
101
  RECAPTCHA_INSTRUCTIONS,
@@ -131,6 +131,7 @@ import {
131
131
  getLedger,
132
132
  getMemoryPlan,
133
133
  getMemoryProvisioned,
134
+ getPageSnapshot,
134
135
  getRefresh,
135
136
  getUserByApiKey,
136
137
  getUserByEmail,
@@ -175,8 +176,9 @@ import {
175
176
  setStripeCustomerId,
176
177
  setSubscriptionTier,
177
178
  setUserMemoryCreds,
179
+ upsertPageSnapshot,
178
180
  verifyPassword
179
- } from "./chunk-FUVQR6GK.js";
181
+ } from "./chunk-GL4BW4CP.js";
180
182
 
181
183
  // src/blog/registry.ts
182
184
  var posts = [
@@ -7529,7 +7531,7 @@ var siteExtractFn = inngest.createFunction(
7529
7531
  return auditImages(pages, { concurrency: 12, timeoutMs: 12e3 });
7530
7532
  }) : null;
7531
7533
  const artifacts = await step.run("finalize", async () => {
7532
- const { assembleExtractArtifacts } = await import("./extract-bundle-UKE273TV.js");
7534
+ const { assembleExtractArtifacts } = await import("./extract-bundle-COS56ZDO.js");
7533
7535
  const stored = await assembleExtractArtifacts(job, { branding, imageAudit });
7534
7536
  await completeExtractJob(jobId, stored);
7535
7537
  return stored;
@@ -8935,6 +8937,11 @@ var ExtractUrlBodySchema = z11.object({
8935
8937
  depositToVault: z11.boolean().optional(),
8936
8938
  vaultName: z11.string().trim().min(1).max(120).optional()
8937
8939
  });
8940
+ var DiffPageBodySchema = z11.object({
8941
+ url: z11.string().min(1, "url is required"),
8942
+ allowLocal: z11.boolean().optional(),
8943
+ resetBaseline: z11.boolean().optional()
8944
+ });
8938
8945
  var MapUrlsBodySchema = z11.object({
8939
8946
  url: z11.string().min(1, "url is required"),
8940
8947
  maxUrls: z11.number().int().min(1).max(2e3).optional(),
@@ -19769,6 +19776,71 @@ import { createMiddleware as createMiddleware3 } from "hono/factory";
19769
19776
  import { getCookie as getCookie2, setCookie as setCookie2, deleteCookie } from "hono/cookie";
19770
19777
  import Stripe2 from "stripe";
19771
19778
 
19779
+ // src/api/page-diff.ts
19780
+ import { createHash as createHash4 } from "crypto";
19781
+ import { diffLines } from "diff";
19782
+ var MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
19783
+ var MAX_DIFF_HUNKS = 200;
19784
+ var MAX_DIFF_LINES_PER_RESPONSE = 2e3;
19785
+ function sha256Hex(value) {
19786
+ return createHash4("sha256").update(value).digest("hex");
19787
+ }
19788
+ function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
19789
+ if (value.length <= maxChars) return { value, truncated: false };
19790
+ return { value: value.slice(0, maxChars), truncated: true };
19791
+ }
19792
+ function changeLines(change) {
19793
+ const lines = change.value.split("\n");
19794
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
19795
+ return lines;
19796
+ }
19797
+ function diffPageContent(oldContent, newContent) {
19798
+ const changes = diffLines(oldContent, newContent);
19799
+ const rawHunks = [];
19800
+ let linesAdded = 0;
19801
+ let linesRemoved = 0;
19802
+ let totalChangedLineCount = 0;
19803
+ for (const change of changes) {
19804
+ if (!change.added && !change.removed) continue;
19805
+ const lines = changeLines(change);
19806
+ totalChangedLineCount += lines.length;
19807
+ if (change.added) linesAdded += lines.length;
19808
+ if (change.removed) linesRemoved += lines.length;
19809
+ rawHunks.push({ type: change.added ? "added" : "removed", lines });
19810
+ }
19811
+ let hunksTruncated = false;
19812
+ const hunksWithinCount = rawHunks.length > MAX_DIFF_HUNKS ? rawHunks.slice(0, MAX_DIFF_HUNKS) : rawHunks;
19813
+ if (rawHunks.length > MAX_DIFF_HUNKS) hunksTruncated = true;
19814
+ const hunks = [];
19815
+ let emittedLines = 0;
19816
+ for (const hunk of hunksWithinCount) {
19817
+ if (emittedLines >= MAX_DIFF_LINES_PER_RESPONSE) {
19818
+ hunksTruncated = true;
19819
+ break;
19820
+ }
19821
+ const remaining = MAX_DIFF_LINES_PER_RESPONSE - emittedLines;
19822
+ if (hunk.lines.length > remaining) {
19823
+ hunks.push({ type: hunk.type, lines: hunk.lines.slice(0, remaining) });
19824
+ emittedLines += remaining;
19825
+ hunksTruncated = true;
19826
+ break;
19827
+ }
19828
+ hunks.push(hunk);
19829
+ emittedLines += hunk.lines.length;
19830
+ }
19831
+ const denominator = Math.max(oldContent.split("\n").length, newContent.split("\n").length, 1);
19832
+ const percentChanged = totalChangedLineCount > 0 ? Math.min(100, Math.round(totalChangedLineCount / denominator * 1e3) / 10) : 0;
19833
+ return {
19834
+ hunks,
19835
+ linesAdded,
19836
+ linesRemoved,
19837
+ percentChanged,
19838
+ hunksTruncated,
19839
+ hunksTruncatedReason: hunksTruncated ? `Showing ${emittedLines} of ${totalChangedLineCount} changed lines (capped at ${MAX_DIFF_HUNKS} hunks / ${MAX_DIFF_LINES_PER_RESPONSE} lines). Use contentHash/previousContentHash to confirm the full scope of change.` : null,
19840
+ totalChangedLineCount
19841
+ };
19842
+ }
19843
+
19772
19844
  // src/api/scrape-vault-sink.ts
19773
19845
  var MAX_DEPOSIT_CHARS = 5e6;
19774
19846
  var SCRAPE_FALLBACK_PREFIX = "scrape-fallback/";
@@ -21098,6 +21170,91 @@ app.post("/extract-url", auth2, async (c) => {
21098
21170
  await releaseConcurrencyGate(gate.lockId);
21099
21171
  }
21100
21172
  });
21173
+ app.post("/diff-page", auth2, async (c) => {
21174
+ const raw = await c.req.json().catch(() => ({}));
21175
+ const bodyResult = DiffPageBodySchema.safeParse(raw);
21176
+ if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? "Invalid request" }, 400);
21177
+ const { url, allowLocal, resetBaseline } = bodyResult.data;
21178
+ if (!allowLocal) {
21179
+ const checked = await validatePublicHttpUrl(url, { field: "URL" });
21180
+ if (checked.error || !checked.parsed) return c.json({ error: checked.error ?? "Invalid URL" }, 400);
21181
+ } else {
21182
+ try {
21183
+ new URL(url);
21184
+ } catch {
21185
+ return c.json({ error: "Invalid URL" }, 400);
21186
+ }
21187
+ }
21188
+ const canonicalUrl = (() => {
21189
+ try {
21190
+ return new URL(url).href;
21191
+ } catch {
21192
+ return url;
21193
+ }
21194
+ })();
21195
+ const user = c.get("user");
21196
+ const gate = await acquireConcurrencyGate(user, "diff_page", {
21197
+ reuseLockId: c.req.header("x-mcp-scraper-concurrency-lock"),
21198
+ metadata: { url: canonicalUrl }
21199
+ });
21200
+ if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
21201
+ let debited = false;
21202
+ try {
21203
+ const { ok: dpOk, balance_mc: dpBal } = await debitMc(user.id, MC_COSTS.diff_page, LedgerOperation.DIFF_PAGE, new URL(canonicalUrl).hostname);
21204
+ if (!dpOk) return c.json(insufficientBalanceResponse(dpBal, MC_COSTS.diff_page), 402);
21205
+ debited = true;
21206
+ const existingSnapshot = await getPageSnapshot(user.id, canonicalUrl);
21207
+ const previous = resetBaseline ? null : existingSnapshot;
21208
+ const kernelApiKey = browserServiceApiKey();
21209
+ const result = await extractKpo({ url: canonicalUrl, kernelApiKey });
21210
+ const currentContent = result.bodyMarkdown ?? "";
21211
+ const currentHash = sha256Hex(currentContent);
21212
+ const { value: storedContent, truncated: contentTruncated } = truncateForStorage(currentContent);
21213
+ const currentCheckedAt = (/* @__PURE__ */ new Date()).toISOString();
21214
+ await upsertPageSnapshot(user.id, canonicalUrl, {
21215
+ contentHash: currentHash,
21216
+ content: storedContent,
21217
+ title: result.title ?? null,
21218
+ contentBytes: Buffer.byteLength(currentContent, "utf8"),
21219
+ truncated: contentTruncated
21220
+ });
21221
+ let status;
21222
+ let diff = { hunks: [], linesAdded: 0, linesRemoved: 0, percentChanged: 0, hunksTruncated: false, hunksTruncatedReason: null, totalChangedLineCount: 0 };
21223
+ if (!previous) {
21224
+ status = "baseline";
21225
+ diff.percentChanged = null;
21226
+ } else if (previous.contentHash === currentHash) {
21227
+ status = "unchanged";
21228
+ } else {
21229
+ status = "changed";
21230
+ diff = diffPageContent(previous.content, storedContent);
21231
+ }
21232
+ await logRequestEvent({ userId: user.id, source: "diff_page", status: "done", query: canonicalUrl, result: { status } });
21233
+ return c.json({
21234
+ url: canonicalUrl,
21235
+ title: result.title ?? null,
21236
+ status,
21237
+ isReset: !!resetBaseline && !!existingSnapshot,
21238
+ previousCheckedAt: previous?.checkedAt ?? null,
21239
+ currentCheckedAt,
21240
+ contentHash: currentHash,
21241
+ previousContentHash: previous?.contentHash ?? null,
21242
+ summary: { linesAdded: diff.linesAdded, linesRemoved: diff.linesRemoved, percentChanged: diff.percentChanged },
21243
+ hunks: diff.hunks,
21244
+ contentTruncated,
21245
+ hunksTruncated: diff.hunksTruncated,
21246
+ hunksTruncatedReason: diff.hunksTruncatedReason,
21247
+ totalChangedLineCount: diff.totalChangedLineCount
21248
+ });
21249
+ } catch (err) {
21250
+ const msg = err instanceof Error ? err.message : String(err);
21251
+ if (debited) await creditMc(user.id, MC_COSTS.diff_page, LedgerOperation.DIFF_PAGE, "failed call");
21252
+ await logRequestEvent({ userId: user.id, source: "diff_page", status: "failed", query: canonicalUrl, error: msg });
21253
+ return c.json({ error: msg }, 500);
21254
+ } finally {
21255
+ await releaseConcurrencyGate(gate.lockId);
21256
+ }
21257
+ });
21101
21258
  app.post("/map-urls", auth2, async (c) => {
21102
21259
  const raw = await c.req.json().catch(() => ({}));
21103
21260
  const bodyResult = MapUrlsBodySchema.safeParse(raw);
@@ -21524,7 +21681,7 @@ app.get("/cron/tick", async (c) => {
21524
21681
  if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
21525
21682
  return c.json({ error: "Unauthorized" }, 401);
21526
21683
  }
21527
- const { drainQueue } = await import("./worker-YHKUJR5P.js");
21684
+ const { drainQueue } = await import("./worker-VASAJ7FZ.js");
21528
21685
  const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
21529
21686
  const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
21530
21687
  const [results, sweepResult, reapResult, expiredResult, blobCleanup] = await Promise.all([
@@ -21542,15 +21699,15 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
21542
21699
  return c.json({ error: "Unauthorized" }, 401);
21543
21700
  }
21544
21701
  const jobId = c.req.param("id");
21545
- const { getExtractJob: getExtractJob2, completeExtractJob: completeExtractJob2 } = await import("./site-extract-repository-THBVEXMP.js");
21546
- const { assembleExtractArtifacts } = await import("./extract-bundle-UKE273TV.js");
21702
+ const { getExtractJob: getExtractJob2, completeExtractJob: completeExtractJob2 } = await import("./site-extract-repository-LI2S3S6E.js");
21703
+ const { assembleExtractArtifacts } = await import("./extract-bundle-COS56ZDO.js");
21547
21704
  const job = await getExtractJob2(jobId);
21548
21705
  if (!job) return c.json({ error: "job not found" }, 404);
21549
21706
  const stored = await assembleExtractArtifacts(job, {});
21550
21707
  await completeExtractJob2(jobId, stored);
21551
21708
  let settlement = "already_settled_or_refunded";
21552
21709
  if (job.billedMc == null && job.userId != null) {
21553
- const { settleExtractJob: settleExtractJob2, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-THBVEXMP.js");
21710
+ const { settleExtractJob: settleExtractJob2, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-LI2S3S6E.js");
21554
21711
  const heldMc = Number(job.options.heldMc ?? 0);
21555
21712
  const successful = await countSuccessfulPages2(jobId);
21556
21713
  const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
@@ -21714,4 +21871,4 @@ app.get("/blog/:slug/", (c) => {
21714
21871
  export {
21715
21872
  app
21716
21873
  };
21717
- //# sourceMappingURL=server-XCDFHHLE.js.map
21874
+ //# sourceMappingURL=server-R3KVQNOT.js.map