mcp-scraper 0.19.0 → 0.20.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 (51) hide show
  1. package/dist/bin/api-server.cjs +189 -37
  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 +100 -14
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +4 -4
  13. package/dist/bin/paa-harvest.cjs.map +1 -1
  14. package/dist/bin/paa-harvest.js +3 -3
  15. package/dist/{chunk-GL4BW4CP.js → chunk-22VEGGTW.js} +21 -9
  16. package/dist/{chunk-GL4BW4CP.js.map → chunk-22VEGGTW.js.map} +1 -1
  17. package/dist/{chunk-RUGJE5EB.js → chunk-EJK25QOW.js} +2 -2
  18. package/dist/chunk-JWIE5NCR.js +284 -0
  19. package/dist/chunk-JWIE5NCR.js.map +1 -0
  20. package/dist/{chunk-O2S5TOCG.js → chunk-KE7KE2Q2.js} +102 -16
  21. package/dist/chunk-KE7KE2Q2.js.map +1 -0
  22. package/dist/chunk-PZB3TJWK.js +7 -0
  23. package/dist/chunk-PZB3TJWK.js.map +1 -0
  24. package/dist/{chunk-D7ZT27HY.js → chunk-UN7VMHZL.js} +2 -2
  25. package/dist/{chunk-2HDMYW4B.js → chunk-XDFSLSSH.js} +2 -2
  26. package/dist/{chunk-HE2LQPJ2.js → chunk-ZRKFW5FB.js} +2 -2
  27. package/dist/{db-LIOTIWVN.js → db-YHZYG7D2.js} +2 -2
  28. package/dist/{extract-bundle-U4D5LW5W.js → extract-bundle-OSUPAHCE.js} +58 -5
  29. package/dist/extract-bundle-OSUPAHCE.js.map +1 -0
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +3 -3
  32. package/dist/{server-VWXDE64Y.js → server-VDWIPV7F.js} +37 -310
  33. package/dist/server-VDWIPV7F.js.map +1 -0
  34. package/dist/{site-extract-repository-NVSZH35Y.js → site-extract-repository-GWKGK46Z.js} +3 -3
  35. package/dist/{worker-AM2DHUWG.js → worker-O5PZTTPY.js} +5 -5
  36. package/docs/mcp-tool-manifest.generated.json +166 -23
  37. package/docs/specs/meta-ad-creative-media-resolution-spec.md +3 -3
  38. package/docs/specs/unified-credit-and-scheduled-execution-billing-spec.md +1007 -0
  39. package/package.json +1 -1
  40. package/dist/chunk-O2S5TOCG.js.map +0 -1
  41. package/dist/chunk-OQHYDW4Q.js +0 -7
  42. package/dist/chunk-OQHYDW4Q.js.map +0 -1
  43. package/dist/extract-bundle-U4D5LW5W.js.map +0 -1
  44. package/dist/server-VWXDE64Y.js.map +0 -1
  45. /package/dist/{chunk-RUGJE5EB.js.map → chunk-EJK25QOW.js.map} +0 -0
  46. /package/dist/{chunk-D7ZT27HY.js.map → chunk-UN7VMHZL.js.map} +0 -0
  47. /package/dist/{chunk-2HDMYW4B.js.map → chunk-XDFSLSSH.js.map} +0 -0
  48. /package/dist/{chunk-HE2LQPJ2.js.map → chunk-ZRKFW5FB.js.map} +0 -0
  49. /package/dist/{db-LIOTIWVN.js.map → db-YHZYG7D2.js.map} +0 -0
  50. /package/dist/{site-extract-repository-NVSZH35Y.js.map → site-extract-repository-GWKGK46Z.js.map} +0 -0
  51. /package/dist/{worker-AM2DHUWG.js.map → worker-O5PZTTPY.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  harvest
3
- } from "./chunk-2HDMYW4B.js";
3
+ } from "./chunk-XDFSLSSH.js";
4
4
  import {
5
5
  recordVendorUsage
6
- } from "./chunk-D7ZT27HY.js";
6
+ } from "./chunk-UN7VMHZL.js";
7
7
  import "./chunk-K443GQY5.js";
8
8
  import "./chunk-XGIPATLV.js";
9
9
  import "./chunk-M2S27J6Z.js";
10
- import "./chunk-GL4BW4CP.js";
10
+ import "./chunk-22VEGGTW.js";
11
11
 
12
12
  // src/video/VideoGenerator.ts
13
13
  import { execSync as execSync2 } from "child_process";
@@ -1,9 +1,12 @@
1
+ import {
2
+ harvestPageMedia
3
+ } from "./chunk-JWIE5NCR.js";
1
4
  import {
2
5
  classifyHarvestProblem,
3
6
  createHarvestAttemptRecorder,
4
7
  harvestProblemResponse,
5
8
  serializeHarvestProblem
6
- } from "./chunk-HE2LQPJ2.js";
9
+ } from "./chunk-ZRKFW5FB.js";
7
10
  import {
8
11
  BrowserDriver,
9
12
  MapsSelectors,
@@ -11,7 +14,7 @@ import {
11
14
  deleteKernelProxyId,
12
15
  harvest,
13
16
  resolveKernelProxyId
14
- } from "./chunk-2HDMYW4B.js";
17
+ } from "./chunk-XDFSLSSH.js";
15
18
  import {
16
19
  HttpMcpToolExecutor,
17
20
  MemoryMcpToolExecutor,
@@ -32,7 +35,7 @@ import {
32
35
  sanitizeAttempts,
33
36
  sanitizeHarvestResult,
34
37
  transcribeMediaUrl
35
- } from "./chunk-O2S5TOCG.js";
38
+ } from "./chunk-KE7KE2Q2.js";
36
39
  import {
37
40
  auditImages,
38
41
  buildLinkReport,
@@ -50,7 +53,7 @@ import {
50
53
  recordKernelSession,
51
54
  recordVendorUsage,
52
55
  runWithCostContext
53
- } from "./chunk-D7ZT27HY.js";
56
+ } from "./chunk-UN7VMHZL.js";
54
57
  import {
55
58
  csvRecords,
56
59
  listWorkflowDefinitions,
@@ -74,7 +77,7 @@ import {
74
77
  RawMapsOverviewSchema,
75
78
  RawMapsReviewStatsSchema
76
79
  } from "./chunk-XGIPATLV.js";
77
- import "./chunk-OQHYDW4Q.js";
80
+ import "./chunk-PZB3TJWK.js";
78
81
  import {
79
82
  completeExtractJob,
80
83
  countSuccessfulPages,
@@ -85,7 +88,7 @@ import {
85
88
  saveExtractPages,
86
89
  setExtractJobTotal,
87
90
  settleExtractJob
88
- } from "./chunk-RUGJE5EB.js";
91
+ } from "./chunk-EJK25QOW.js";
89
92
  import {
90
93
  BROWSER_OPEN_MIN_BALANCE_MC,
91
94
  CONCURRENCY_PRICE_ID,
@@ -191,7 +194,7 @@ import {
191
194
  setUserMemoryCreds,
192
195
  upsertPageSnapshot,
193
196
  verifyPassword
194
- } from "./chunk-GL4BW4CP.js";
197
+ } from "./chunk-22VEGGTW.js";
195
198
 
196
199
  // src/blog/registry.ts
197
200
  var posts = [
@@ -4226,285 +4229,6 @@ async function capturePageData(url, opts = {}) {
4226
4229
  }
4227
4230
  }
4228
4231
 
4229
- // src/lib/media-extractor.ts
4230
- import { createWriteStream, mkdirSync } from "fs";
4231
- import { homedir } from "os";
4232
- import { join as join2, extname, basename } from "path";
4233
- import { pipeline } from "stream/promises";
4234
- import { Readable } from "stream";
4235
- var AD_PATTERNS = [
4236
- "doubleclick.net",
4237
- "googlesyndication.com",
4238
- "googletagmanager.com",
4239
- "google-analytics.com",
4240
- "googletagservices.com",
4241
- "adservice.google",
4242
- "googletag.",
4243
- "pagead2.googlesyndication",
4244
- "facebook.net/tr",
4245
- "connect.facebook.net",
4246
- "fbcdn.net/rsrc",
4247
- "analytics.twitter.com",
4248
- "static.ads-twitter.com",
4249
- "ads.twitter.com",
4250
- "t.co/i/adsct",
4251
- "pixel.advertising.com",
4252
- "hotjar.com",
4253
- "clarity.ms",
4254
- "quantserve.com",
4255
- "scorecardresearch.com",
4256
- "newrelic.com",
4257
- "nr-data.net",
4258
- "segment.io",
4259
- "segment.com",
4260
- "amplitude.com",
4261
- "mixpanel.com",
4262
- "heap.io",
4263
- "fullstory.com",
4264
- "moatads.com",
4265
- "criteo.com",
4266
- "adsrvr.org",
4267
- "rubiconproject.com",
4268
- "pubmatic.com",
4269
- "openx.net",
4270
- "appnexus.com",
4271
- "amazon-adsystem.com",
4272
- "media.net",
4273
- "yieldmo.com",
4274
- "triplelift.com",
4275
- "sharethrough.com",
4276
- "prebid.",
4277
- "smaato.net",
4278
- "indexworm.com",
4279
- "casalemedia.com",
4280
- "outbrain.com",
4281
- "taboola.com",
4282
- "revcontent.com",
4283
- "mgid.com",
4284
- "tawk.to",
4285
- "intercom.io",
4286
- "drift.com",
4287
- "hs-scripts.com",
4288
- "zopim.com",
4289
- "livechatinc.com",
4290
- "userlike.com",
4291
- "onetrust.com",
4292
- "cookielaw.org",
4293
- "cookieinformation.com",
4294
- "trustarc.com",
4295
- "/ads/",
4296
- "/ad/",
4297
- "/banner/",
4298
- "/banners/",
4299
- "/pixel/",
4300
- "/beacon/",
4301
- "/tracking/",
4302
- "/tracker/",
4303
- "/remarketing/",
4304
- "/conversion/",
4305
- "1x1.gif",
4306
- "spacer.gif",
4307
- "blank.gif",
4308
- "transparent.gif"
4309
- ];
4310
- var IMAGE_EXTS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".webp", ".gif", ".avif", ".svg", ".tiff"]);
4311
- var VIDEO_EXTS = /* @__PURE__ */ new Set([".mp4", ".webm", ".mov", ".avi", ".m4v", ".ogv", ".mkv"]);
4312
- var AUDIO_EXTS = /* @__PURE__ */ new Set([".mp3", ".wav", ".ogg", ".aac", ".m4a", ".flac", ".opus"]);
4313
- function isAdUrl(url) {
4314
- const lower = url.toLowerCase();
4315
- return AD_PATTERNS.some((p) => lower.includes(p));
4316
- }
4317
- function isDataUri(url) {
4318
- return url.startsWith("data:");
4319
- }
4320
- function typeFromUrl(url) {
4321
- try {
4322
- const ext = extname(new URL(url).pathname).toLowerCase();
4323
- if (IMAGE_EXTS.has(ext)) return "image";
4324
- if (VIDEO_EXTS.has(ext)) return "video";
4325
- if (AUDIO_EXTS.has(ext)) return "audio";
4326
- } catch {
4327
- }
4328
- return null;
4329
- }
4330
- function typeFromMime(mime) {
4331
- const lower = mime.toLowerCase();
4332
- if (lower.startsWith("image/")) return "image";
4333
- if (lower.startsWith("video/")) return "video";
4334
- if (lower.startsWith("audio/")) return "audio";
4335
- return null;
4336
- }
4337
- function resolveUrl(raw, base) {
4338
- if (!raw || isDataUri(raw)) return null;
4339
- try {
4340
- return new URL(raw, base).href;
4341
- } catch {
4342
- return null;
4343
- }
4344
- }
4345
- function safeFilename(url, index) {
4346
- try {
4347
- const u = new URL(url);
4348
- const base = basename(u.pathname).replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
4349
- return base || `asset-${index}`;
4350
- } catch {
4351
- return `asset-${index}`;
4352
- }
4353
- }
4354
- function extractMediaUrls(html, baseUrl) {
4355
- const seen = /* @__PURE__ */ new Set();
4356
- const urls = [];
4357
- const add = (raw) => {
4358
- const resolved = resolveUrl(raw.trim(), baseUrl);
4359
- if (!resolved || seen.has(resolved) || isAdUrl(resolved)) return;
4360
- seen.add(resolved);
4361
- urls.push(resolved);
4362
- };
4363
- for (const m of html.matchAll(/<img\s[^>]*>/gi)) {
4364
- const tag = m[0];
4365
- for (const attr of ["src", "data-src", "data-lazy-src", "data-original"]) {
4366
- const v = (tag.match(new RegExp(`\\b${attr}\\s*=\\s*["']([^"']+)["']`, "i")) ?? [])[1];
4367
- if (v) add(v);
4368
- }
4369
- const srcset = (tag.match(/\bsrcset\s*=\s*["']([^"']+)["']/i) ?? [])[1];
4370
- if (srcset) {
4371
- for (const part of srcset.split(",")) {
4372
- const u = part.trim().split(/\s+/)[0];
4373
- if (u) add(u);
4374
- }
4375
- }
4376
- }
4377
- for (const m of html.matchAll(/<source\s[^>]*>/gi)) {
4378
- const tag = m[0];
4379
- const src = (tag.match(/\bsrc\s*=\s*["']([^"']+)["']/i) ?? [])[1];
4380
- if (src) add(src);
4381
- const srcset = (tag.match(/\bsrcset\s*=\s*["']([^"']+)["']/i) ?? [])[1];
4382
- if (srcset) {
4383
- for (const part of srcset.split(",")) {
4384
- const u = part.trim().split(/\s+/)[0];
4385
- if (u) add(u);
4386
- }
4387
- }
4388
- }
4389
- for (const m of html.matchAll(/<video\s[^>]*>/gi)) {
4390
- const tag = m[0];
4391
- for (const attr of ["src", "poster"]) {
4392
- const v = (tag.match(new RegExp(`\\b${attr}\\s*=\\s*["']([^"']+)["']`, "i")) ?? [])[1];
4393
- if (v) add(v);
4394
- }
4395
- }
4396
- for (const m of html.matchAll(/<audio\s[^>]*>/gi)) {
4397
- const tag = m[0];
4398
- const v = (tag.match(/\bsrc\s*=\s*["']([^"']+)["']/i) ?? [])[1];
4399
- if (v) add(v);
4400
- }
4401
- for (const m of html.matchAll(/<meta\s[^>]*>/gi)) {
4402
- const tag = m[0];
4403
- const prop = (tag.match(/\b(?:property|name)\s*=\s*["']([^"']+)["']/i) ?? [])[1]?.toLowerCase();
4404
- if (prop === "og:image" || prop === "twitter:image") {
4405
- const content = (tag.match(/\bcontent\s*=\s*["']([^"']+)["']/i) ?? [])[1];
4406
- if (content) add(content);
4407
- }
4408
- }
4409
- for (const m of html.matchAll(/background(?:-image)?\s*:\s*url\(\s*["']?([^"')]+)["']?\s*\)/gi)) {
4410
- add(m[1]);
4411
- }
4412
- return urls;
4413
- }
4414
- async function downloadAsset(url, destDir, filename) {
4415
- const res = await fetch(url, {
4416
- headers: { "User-Agent": "Mozilla/5.0 (compatible; ThorbitBot/1.0)" },
4417
- signal: AbortSignal.timeout(15e3)
4418
- });
4419
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
4420
- if (!res.body) throw new Error("Empty response body");
4421
- const mimeType = res.headers.get("content-type")?.split(";")[0].trim() ?? null;
4422
- let dest = join2(destDir, filename);
4423
- if (mimeType && !extname(filename)) {
4424
- const mimeExt = {
4425
- "image/jpeg": ".jpg",
4426
- "image/png": ".png",
4427
- "image/webp": ".webp",
4428
- "image/gif": ".gif",
4429
- "image/svg+xml": ".svg",
4430
- "image/avif": ".avif",
4431
- "video/mp4": ".mp4",
4432
- "video/webm": ".webm",
4433
- "audio/mpeg": ".mp3",
4434
- "audio/ogg": ".ogg",
4435
- "audio/wav": ".wav"
4436
- };
4437
- const ext = mimeExt[mimeType];
4438
- if (ext) dest = dest + ext;
4439
- }
4440
- const writer = createWriteStream(dest);
4441
- await pipeline(Readable.fromWeb(res.body), writer);
4442
- const { statSync } = await import("fs");
4443
- const sizeBytes = statSync(dest).size;
4444
- return { savedPath: dest, sizeBytes, mimeType };
4445
- }
4446
- async function harvestPageMedia(html, pageUrl, options = {}) {
4447
- const types = options.types ?? ["image", "video", "audio"];
4448
- const typesSet = new Set(types);
4449
- const rawUrls = extractMediaUrls(html, pageUrl);
4450
- const totalFound = rawUrls.length;
4451
- const filteredUrls = [];
4452
- const kept = [];
4453
- for (const url of rawUrls) {
4454
- const type = typeFromUrl(url);
4455
- if (!type || !typesSet.has(type)) {
4456
- filteredUrls.push(url);
4457
- continue;
4458
- }
4459
- kept.push({ url, type });
4460
- }
4461
- if (options.outputDir === null) {
4462
- return {
4463
- pageUrl,
4464
- outputDir: null,
4465
- assets: kept.map(({ url, type }, i) => ({
4466
- url,
4467
- type,
4468
- mimeType: null,
4469
- filename: safeFilename(url, i),
4470
- savedPath: null,
4471
- sizeBytes: null
4472
- })),
4473
- filteredCount: filteredUrls.length,
4474
- totalFound
4475
- };
4476
- }
4477
- const domain = (() => {
4478
- try {
4479
- return new URL(pageUrl).hostname.replace(/^www\./, "");
4480
- } catch {
4481
- return "unknown";
4482
- }
4483
- })();
4484
- const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
4485
- const outDir = options.outputDir ?? join2(homedir(), "Downloads", "mcp-scraper", "media", `${stamp}-${domain}`);
4486
- mkdirSync(outDir, { recursive: true });
4487
- const assets = [];
4488
- await Promise.allSettled(
4489
- kept.map(async ({ url, type }, i) => {
4490
- const filename = safeFilename(url, i);
4491
- try {
4492
- const { savedPath, sizeBytes, mimeType } = await downloadAsset(url, outDir, filename);
4493
- const resolvedType = mimeType ? typeFromMime(mimeType) ?? type : type;
4494
- assets.push({ url, type: resolvedType, mimeType, filename: basename(savedPath), savedPath, sizeBytes });
4495
- } catch {
4496
- assets.push({ url, type, mimeType: null, filename, savedPath: null, sizeBytes: null });
4497
- }
4498
- })
4499
- );
4500
- assets.sort((a, b) => {
4501
- if (a.savedPath && !b.savedPath) return -1;
4502
- if (!a.savedPath && b.savedPath) return 1;
4503
- return a.url.localeCompare(b.url);
4504
- });
4505
- return { pageUrl, outputDir: outDir, assets, filteredCount: filteredUrls.length, totalFound };
4506
- }
4507
-
4508
4232
  // src/api/site-mapper.ts
4509
4233
  var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
4510
4234
  "jpg",
@@ -7628,7 +7352,7 @@ var siteExtractFn = inngest.createFunction(
7628
7352
  return buildSeoAudit(pages, job.startUrl);
7629
7353
  }) : null;
7630
7354
  const artifacts = await step.run("finalize", async () => {
7631
- const { assembleExtractArtifacts } = await import("./extract-bundle-U4D5LW5W.js");
7355
+ const { assembleExtractArtifacts } = await import("./extract-bundle-OSUPAHCE.js");
7632
7356
  const stored = await assembleExtractArtifacts(job, { branding, imageAudit, seoAudit });
7633
7357
  await completeExtractJob(jobId, stored);
7634
7358
  return stored;
@@ -9100,6 +8824,7 @@ var ExtractSiteBodySchema = z11.object({
9100
8824
  rotateProxies: z11.boolean().optional(),
9101
8825
  rotateProxyEvery: z11.number().int().min(1).max(100).optional(),
9102
8826
  background: z11.boolean().optional(),
8827
+ downloadImages: z11.boolean().optional(),
9103
8828
  formats: z11.array(z11.enum(["markdown", "links", "json", "images", "branding", "issues"])).optional()
9104
8829
  });
9105
8830
  var YoutubeHarvestBodySchema = z11.object({
@@ -11187,7 +10912,7 @@ import { Hono as Hono7 } from "hono";
11187
10912
  import { z as z15 } from "zod";
11188
10913
  import { mkdtempSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
11189
10914
  import { tmpdir } from "os";
11190
- import { join as join4 } from "path";
10915
+ import { join as join3 } from "path";
11191
10916
  import { spawn } from "child_process";
11192
10917
 
11193
10918
  // src/extractor/InstagramContentExtractor.ts
@@ -11785,8 +11510,8 @@ instagramApp.post("/media-download", createApiKeyAuth(), async (c) => {
11785
11510
  downloads.push({ kind, url: track.url, savedPath: stored.url, sizeBytes: stored.bytes, mimeType, error: null });
11786
11511
  const isMuxInput = muxEligible && (track.url === extraction.selectedVideoTrack.url || track.url === extraction.selectedAudioTrack.url);
11787
11512
  if (isMuxInput) {
11788
- if (!muxWorkDir) muxWorkDir = mkdtempSync(join4(tmpdir(), "ig-mux-"));
11789
- const localPath = join4(muxWorkDir, trackFilename(track, index, extraction.shortcode));
11513
+ if (!muxWorkDir) muxWorkDir = mkdtempSync(join3(tmpdir(), "ig-mux-"));
11514
+ const localPath = join3(muxWorkDir, trackFilename(track, index, extraction.shortcode));
11790
11515
  writeFileSync(localPath, bytes);
11791
11516
  muxInputs.set(track.url, localPath);
11792
11517
  }
@@ -11795,7 +11520,7 @@ instagramApp.post("/media-download", createApiKeyAuth(), async (c) => {
11795
11520
  }
11796
11521
  }
11797
11522
  if (muxEligible && muxWorkDir && muxInputs.has(extraction.selectedVideoTrack.url) && muxInputs.has(extraction.selectedAudioTrack.url)) {
11798
- const outPath = join4(muxWorkDir, "muxed.mp4");
11523
+ const outPath = join3(muxWorkDir, "muxed.mp4");
11799
11524
  const muxed = await runFfmpegMux(muxInputs.get(extraction.selectedVideoTrack.url), muxInputs.get(extraction.selectedAudioTrack.url), outPath);
11800
11525
  if (muxed.ok) {
11801
11526
  const stored = await store.put(`${prefix}/muxed.mp4`, readFileSync2(outPath), "video/mp4");
@@ -13706,7 +13431,7 @@ import { Hono as Hono13 } from "hono";
13706
13431
 
13707
13432
  // src/directory/directory-workflow.ts
13708
13433
  import { mkdir as mkdir2, writeFile } from "fs/promises";
13709
- import { join as join5 } from "path";
13434
+ import { join as join4 } from "path";
13710
13435
  import { z as z21 } from "zod";
13711
13436
 
13712
13437
  // src/directory/location-db.ts
@@ -14042,11 +13767,11 @@ function csvRowsFor(result) {
14042
13767
  return rows;
14043
13768
  }
14044
13769
  async function saveDirectoryCsv(result) {
14045
- const outDir = join5(outputBaseDir(), "directory-workflows");
13770
+ const outDir = join4(outputBaseDir(), "directory-workflows");
14046
13771
  await mkdir2(outDir, { recursive: true });
14047
13772
  const stamp = result.extractedAt.replace(/[:.]/g, "-");
14048
13773
  const slug = `${result.state}-${result.query}`.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
14049
- const path5 = join5(outDir, `${stamp}-${slug}-directory-workflow.csv`);
13774
+ const path5 = join4(outDir, `${stamp}-${slug}-directory-workflow.csv`);
14050
13775
  const headers = [
14051
13776
  "source_query",
14052
13777
  "source_location",
@@ -20618,8 +20343,8 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
20618
20343
 
20619
20344
  // src/api/scrape-blob-cleanup.ts
20620
20345
  import { readdir, stat, unlink } from "fs/promises";
20621
- import { homedir as homedir2 } from "os";
20622
- import { join as join6 } from "path";
20346
+ import { homedir } from "os";
20347
+ import { join as join5 } from "path";
20623
20348
  async function cleanupVercel(token, cutoff) {
20624
20349
  const { list, del } = await import("@vercel/blob");
20625
20350
  let cursor;
@@ -20636,8 +20361,8 @@ async function cleanupVercel(token, cutoff) {
20636
20361
  return { deleted, store: "vercel-blob" };
20637
20362
  }
20638
20363
  async function cleanupLocal(cutoff) {
20639
- const baseDir = process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || join6(homedir2(), "Downloads", "mcp-scraper");
20640
- const dir = join6(baseDir, "blobs", SCRAPE_FALLBACK_PREFIX.replace(/\/$/, ""));
20364
+ const baseDir = process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || join5(homedir(), "Downloads", "mcp-scraper");
20365
+ const dir = join5(baseDir, "blobs", SCRAPE_FALLBACK_PREFIX.replace(/\/$/, ""));
20641
20366
  let deleted = 0;
20642
20367
  let entries;
20643
20368
  try {
@@ -20646,7 +20371,7 @@ async function cleanupLocal(cutoff) {
20646
20371
  return { deleted: 0, store: "local" };
20647
20372
  }
20648
20373
  for (const name of entries) {
20649
- const path5 = join6(dir, name);
20374
+ const path5 = join5(dir, name);
20650
20375
  try {
20651
20376
  const s = await stat(path5);
20652
20377
  if (s.isFile() && s.mtimeMs < cutoff) {
@@ -22219,7 +21944,7 @@ var auth2 = createMiddleware3(async (c, next) => {
22219
21944
  const user = await getUserByApiKey(key);
22220
21945
  if (!user) return c.json({ error: "Invalid or inactive API key" }, 401);
22221
21946
  const refreshed = await applyMonthlyFreeRefresh(user);
22222
- const balanceMc = await reconcileBalanceMc(refreshed.id);
21947
+ const balanceMc = await reconcileBalanceMc(refreshed.id, refreshed.balance_mc);
22223
21948
  c.set("user", { ...refreshed, balance_mc: balanceMc });
22224
21949
  return next();
22225
21950
  });
@@ -22238,7 +21963,7 @@ var sessionAuth = createMiddleware3(async (c, next) => {
22238
21963
  const user = await getUserById(userId);
22239
21964
  if (!user) return c.json({ error: "User not found" }, 401);
22240
21965
  const refreshed = await applyMonthlyFreeRefresh(user);
22241
- const balanceMc = await reconcileBalanceMc(refreshed.id);
21966
+ const balanceMc = await reconcileBalanceMc(refreshed.id, refreshed.balance_mc);
22242
21967
  c.set("sessionUser", { ...refreshed, balance_mc: balanceMc });
22243
21968
  return next();
22244
21969
  });
@@ -22478,7 +22203,7 @@ app.get("/me", async (c) => {
22478
22203
  }
22479
22204
  const refreshed = await applyMonthlyFreeRefresh(foundUser);
22480
22205
  const [balanceMc, stats] = await Promise.all([
22481
- reconcileBalanceMc(refreshed.id),
22206
+ reconcileBalanceMc(refreshed.id, refreshed.balance_mc),
22482
22207
  getUserStats(refreshed.id)
22483
22208
  ]);
22484
22209
  const user = { ...refreshed, balance_mc: balanceMc };
@@ -23902,7 +23627,8 @@ app.post("/extract-site", auth2, async (c) => {
23902
23627
  if (checked.error || !checked.parsed) return c.json({ error: checked.error ?? "Invalid URL" }, 400);
23903
23628
  const parsed = checked.parsed;
23904
23629
  const user = c.get("user");
23905
- if (body.background === true) {
23630
+ const downloadImages = body.downloadImages === true;
23631
+ if (body.background === true || downloadImages) {
23906
23632
  const affordablePages = Math.floor(user.balance_mc / MC_COSTS.page_scrape);
23907
23633
  if (affordablePages < 1) return c.json(insufficientBalanceResponse(user.balance_mc, MC_COSTS.page_scrape), 402);
23908
23634
  const maxPages = Math.min(Math.min(1e4, Math.max(1, body.maxPages ?? 1e4)), affordablePages);
@@ -23917,6 +23643,7 @@ app.post("/extract-site", auth2, async (c) => {
23917
23643
  concurrency: concurrencyLimitForUser(user),
23918
23644
  urlsPerBrowser: body.rotateProxyEvery ?? 10,
23919
23645
  formats: body.formats,
23646
+ downloadImages,
23920
23647
  heldMc
23921
23648
  });
23922
23649
  jobCreated = true;
@@ -24225,7 +23952,7 @@ app.post("/billing/concurrency/cancel", requireAllowedOrigin, sessionAuth, async
24225
23952
  });
24226
23953
  app.get("/billing/balance", auth2, async (c) => {
24227
23954
  const user = c.get("user");
24228
- const balanceMc = await reconcileBalanceMc(user.id);
23955
+ const balanceMc = user.balance_mc;
24229
23956
  const ledger = await getLedger(user.id, 20);
24230
23957
  const freeCredits = await getFreeCreditBreakdown(user.id);
24231
23958
  return c.json({
@@ -24237,7 +23964,7 @@ app.get("/billing/balance", auth2, async (c) => {
24237
23964
  });
24238
23965
  app.post("/billing/credits", auth2, async (c) => {
24239
23966
  const user = c.get("user");
24240
- const balanceMc = await reconcileBalanceMc(user.id);
23967
+ const balanceMc = user.balance_mc;
24241
23968
  const body = await c.req.json().catch(() => ({}));
24242
23969
  const query = body.item?.trim().toLowerCase();
24243
23970
  const costs = CREDIT_COST_CATALOG.map(({ aliases, ...cost }) => cost);
@@ -24270,7 +23997,7 @@ app.get("/cron/tick", async (c) => {
24270
23997
  if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
24271
23998
  return c.json({ error: "Unauthorized" }, 401);
24272
23999
  }
24273
- const { drainQueue } = await import("./worker-AM2DHUWG.js");
24000
+ const { drainQueue } = await import("./worker-O5PZTTPY.js");
24274
24001
  const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
24275
24002
  const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
24276
24003
  const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup] = await Promise.all([
@@ -24298,15 +24025,15 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
24298
24025
  return c.json({ error: "Unauthorized" }, 401);
24299
24026
  }
24300
24027
  const jobId = c.req.param("id");
24301
- const { getExtractJob: getExtractJob2, completeExtractJob: completeExtractJob2 } = await import("./site-extract-repository-NVSZH35Y.js");
24302
- const { assembleExtractArtifacts } = await import("./extract-bundle-U4D5LW5W.js");
24028
+ const { getExtractJob: getExtractJob2, completeExtractJob: completeExtractJob2 } = await import("./site-extract-repository-GWKGK46Z.js");
24029
+ const { assembleExtractArtifacts } = await import("./extract-bundle-OSUPAHCE.js");
24303
24030
  const job = await getExtractJob2(jobId);
24304
24031
  if (!job) return c.json({ error: "job not found" }, 404);
24305
24032
  const stored = await assembleExtractArtifacts(job, {});
24306
24033
  await completeExtractJob2(jobId, stored);
24307
24034
  let settlement = "already_settled_or_refunded";
24308
24035
  if (job.billedMc == null && job.userId != null) {
24309
- const { settleExtractJob: settleExtractJob2, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-NVSZH35Y.js");
24036
+ const { settleExtractJob: settleExtractJob2, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-GWKGK46Z.js");
24310
24037
  const heldMc = Number(job.options.heldMc ?? 0);
24311
24038
  const successful = await countSuccessfulPages2(jobId);
24312
24039
  const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
@@ -24471,4 +24198,4 @@ app.get("/blog/:slug/", (c) => {
24471
24198
  export {
24472
24199
  app
24473
24200
  };
24474
- //# sourceMappingURL=server-VWXDE64Y.js.map
24201
+ //# sourceMappingURL=server-VDWIPV7F.js.map