mcp-scraper 0.43.3 → 0.43.5

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 (56) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/api-server.cjs +170 -76
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +3 -3
  5. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  6. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  7. package/dist/bin/mcp-scraper-cli.js +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  10. package/dist/bin/mcp-scraper-install.js +1 -1
  11. package/dist/bin/mcp-stdio-server.cjs +1 -1
  12. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  13. package/dist/bin/mcp-stdio-server.js +4 -4
  14. package/dist/bin/paa-harvest.cjs +42 -22
  15. package/dist/bin/paa-harvest.cjs.map +1 -1
  16. package/dist/bin/paa-harvest.js +3 -3
  17. package/dist/{chunk-FCFQ634B.js → chunk-5X7CJEK3.js} +25 -8
  18. package/dist/chunk-5X7CJEK3.js.map +1 -0
  19. package/dist/chunk-H4RPUFRR.js +7 -0
  20. package/dist/chunk-H4RPUFRR.js.map +1 -0
  21. package/dist/{chunk-ZBQ6EXZW.js → chunk-OVD4E4AP.js} +2 -2
  22. package/dist/{chunk-H7LQ6PTI.js → chunk-RJMOOII7.js} +50 -24
  23. package/dist/chunk-RJMOOII7.js.map +1 -0
  24. package/dist/{chunk-CHC3RCFA.js → chunk-RSGUS5V6.js} +3 -3
  25. package/dist/{chunk-DAS7L4SF.js → chunk-T5AFM4G5.js} +4 -2
  26. package/dist/chunk-T5AFM4G5.js.map +1 -0
  27. package/dist/{chunk-QOWBJ4YY.js → chunk-WT2UHDSE.js} +2 -2
  28. package/dist/{chunk-GUI33Q27.js → chunk-WWIJ2NID.js} +2 -2
  29. package/dist/{db-C5ELI55I.js → db-4ABEYNDM.js} +4 -2
  30. package/dist/{extract-bundle-IOM45SFL.js → extract-bundle-ITPFWRNI.js} +3 -3
  31. package/dist/index.cjs +42 -22
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +1 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +3 -3
  36. package/dist/{location-data-repository-SHEOIBTL.js → location-data-repository-HDNCU4Z4.js} +3 -3
  37. package/dist/{server-7CLMCX42.js → server-BTDXZA5U.js} +101 -57
  38. package/dist/server-BTDXZA5U.js.map +1 -0
  39. package/dist/{site-extract-repository-XNI4ZK2M.js → site-extract-repository-CKCJDP7N.js} +3 -3
  40. package/dist/{worker-2LIOH744.js → worker-73AYU4IK.js} +5 -5
  41. package/package.json +3 -1
  42. package/dist/chunk-4LQVW2EK.js +0 -7
  43. package/dist/chunk-4LQVW2EK.js.map +0 -1
  44. package/dist/chunk-DAS7L4SF.js.map +0 -1
  45. package/dist/chunk-FCFQ634B.js.map +0 -1
  46. package/dist/chunk-H7LQ6PTI.js.map +0 -1
  47. package/dist/server-7CLMCX42.js.map +0 -1
  48. /package/dist/{chunk-ZBQ6EXZW.js.map → chunk-OVD4E4AP.js.map} +0 -0
  49. /package/dist/{chunk-CHC3RCFA.js.map → chunk-RSGUS5V6.js.map} +0 -0
  50. /package/dist/{chunk-QOWBJ4YY.js.map → chunk-WT2UHDSE.js.map} +0 -0
  51. /package/dist/{chunk-GUI33Q27.js.map → chunk-WWIJ2NID.js.map} +0 -0
  52. /package/dist/{db-C5ELI55I.js.map → db-4ABEYNDM.js.map} +0 -0
  53. /package/dist/{extract-bundle-IOM45SFL.js.map → extract-bundle-ITPFWRNI.js.map} +0 -0
  54. /package/dist/{location-data-repository-SHEOIBTL.js.map → location-data-repository-HDNCU4Z4.js.map} +0 -0
  55. /package/dist/{site-extract-repository-XNI4ZK2M.js.map → site-extract-repository-CKCJDP7N.js.map} +0 -0
  56. /package/dist/{worker-2LIOH744.js.map → worker-73AYU4IK.js.map} +0 -0
package/dist/index.d.cts CHANGED
@@ -150,7 +150,7 @@ interface BrowserContextDebug {
150
150
  };
151
151
  locale: string;
152
152
  device: SerpDevice;
153
- userAgent: string;
153
+ userAgent: string | null;
154
154
  deviceScaleFactor: number | null;
155
155
  isMobile: boolean;
156
156
  hasTouch: boolean;
package/dist/index.d.ts CHANGED
@@ -150,7 +150,7 @@ interface BrowserContextDebug {
150
150
  };
151
151
  locale: string;
152
152
  device: SerpDevice;
153
- userAgent: string;
153
+ userAgent: string | null;
154
154
  deviceScaleFactor: number | null;
155
155
  isMobile: boolean;
156
156
  hasTouch: boolean;
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  harvest
3
- } from "./chunk-H7LQ6PTI.js";
3
+ } from "./chunk-RJMOOII7.js";
4
4
  import {
5
5
  recordVendorUsage
6
- } from "./chunk-DAS7L4SF.js";
6
+ } from "./chunk-T5AFM4G5.js";
7
7
  import "./chunk-3ZUBQQPQ.js";
8
8
  import "./chunk-5UN33CGU.js";
9
- import "./chunk-FCFQ634B.js";
9
+ import "./chunk-5X7CJEK3.js";
10
10
 
11
11
  // src/video/VideoGenerator.ts
12
12
  import { execSync as execSync2 } from "child_process";
@@ -13,9 +13,9 @@ import {
13
13
  parseHostedCensusPlacesCsv,
14
14
  parseHostedZipGroupsCsv,
15
15
  queryHostedLocationMarkets
16
- } from "./chunk-QOWBJ4YY.js";
16
+ } from "./chunk-WT2UHDSE.js";
17
17
  import "./chunk-G3P3ZDB4.js";
18
- import "./chunk-FCFQ634B.js";
18
+ import "./chunk-5X7CJEK3.js";
19
19
  export {
20
20
  HOSTED_CENSUS_DATASET_PREFIX,
21
21
  HOSTED_ZIP_DATASET_KIND,
@@ -32,4 +32,4 @@ export {
32
32
  parseHostedZipGroupsCsv,
33
33
  queryHostedLocationMarkets
34
34
  };
35
- //# sourceMappingURL=location-data-repository-SHEOIBTL.js.map
35
+ //# sourceMappingURL=location-data-repository-HDNCU4Z4.js.map
@@ -10,14 +10,14 @@ import {
10
10
  importHostedCensusPlacesCsv,
11
11
  importHostedZipGroupsCsv,
12
12
  listActiveHostedLocationDatasets
13
- } from "./chunk-QOWBJ4YY.js";
13
+ } from "./chunk-WT2UHDSE.js";
14
14
  import {
15
15
  classifyHarvestProblem,
16
16
  createHarvestAttemptRecorder,
17
17
  deliverWebhook,
18
18
  harvestProblemResponse,
19
19
  serializeHarvestProblem
20
- } from "./chunk-ZBQ6EXZW.js";
20
+ } from "./chunk-OVD4E4AP.js";
21
21
  import {
22
22
  AIModeSelectors,
23
23
  AIOverviewSelectors,
@@ -35,13 +35,16 @@ import {
35
35
  cleanupBackupProxyId,
36
36
  createBackupProxyIdSafe,
37
37
  deleteKernelProxyId,
38
+ detectPageUserAgent,
39
+ detectRuntimeUserAgent,
38
40
  encodeUule,
39
41
  harvest,
40
42
  normalizeLocation,
41
43
  resolveBackupKernelProxyId,
42
44
  resolveKernelProxyId,
43
- serpArrivalUrl
44
- } from "./chunk-H7LQ6PTI.js";
45
+ serpArrivalUrl,
46
+ withRuntimeUserAgent
47
+ } from "./chunk-RJMOOII7.js";
45
48
  import {
46
49
  ApiErrorResponseSchema,
47
50
  ArtifactTemplateConfigSchema,
@@ -83,7 +86,7 @@ import {
83
86
  renewDirectoryArtifactDownload,
84
87
  resolveDeploymentProfile,
85
88
  transcribeMediaUrl
86
- } from "./chunk-CHC3RCFA.js";
89
+ } from "./chunk-RSGUS5V6.js";
87
90
  import {
88
91
  auditImageUrls,
89
92
  auditImages,
@@ -111,7 +114,7 @@ import {
111
114
  recordKernelSession,
112
115
  recordVendorUsage,
113
116
  runWithCostContext
114
- } from "./chunk-DAS7L4SF.js";
117
+ } from "./chunk-T5AFM4G5.js";
115
118
  import {
116
119
  listWorkflowDefinitions,
117
120
  runWorkflow,
@@ -139,7 +142,7 @@ import {
139
142
  } from "./chunk-3ZUBQQPQ.js";
140
143
  import {
141
144
  PACKAGE_VERSION
142
- } from "./chunk-4LQVW2EK.js";
145
+ } from "./chunk-H4RPUFRR.js";
143
146
  import {
144
147
  abandonExtractSettlement,
145
148
  countSuccessfulPages,
@@ -162,7 +165,7 @@ import {
162
165
  setExtractJobTotal,
163
166
  settleExtractJob,
164
167
  terminalExtractJobStatus
165
- } from "./chunk-GUI33Q27.js";
168
+ } from "./chunk-WWIJ2NID.js";
166
169
  import {
167
170
  BROWSER_OPEN_MIN_BALANCE_MC,
168
171
  CONCURRENCY_PRICE_ID,
@@ -318,7 +321,7 @@ import {
318
321
  startHarvestAttempt,
319
322
  upsertPageSnapshot,
320
323
  verifyPassword
321
- } from "./chunk-FCFQ634B.js";
324
+ } from "./chunk-5X7CJEK3.js";
322
325
 
323
326
  // src/blog/registry.ts
324
327
  var posts = [
@@ -3749,10 +3752,10 @@ async function fetchWithKernel(url) {
3749
3752
  const kb = await client2.browsers.create({ stealth: true, ...useHeadless ? { headless: true } : {}, timeout_seconds: 60 });
3750
3753
  const browser = await chromium.connectOverCDP(kb.cdp_ws_url);
3751
3754
  try {
3752
- const context = await browser.newContext({
3753
- userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
3755
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
3756
+ const context = await browser.newContext(withRuntimeUserAgent({
3754
3757
  serviceWorkers: "block"
3755
- });
3758
+ }, runtimeUserAgent));
3756
3759
  const page = context.pages()[0] ?? await context.newPage();
3757
3760
  const guard = await installBrowserRequestGuard(page, auditedUrl);
3758
3761
  await page.goto(auditedUrl, { waitUntil: "domcontentloaded", timeout: 3e4 });
@@ -4296,8 +4299,6 @@ async function extractBrandingFromPage(page) {
4296
4299
  // src/lib/screenshot.ts
4297
4300
  var DESKTOP_VIEWPORT = { width: 1440, height: 900 };
4298
4301
  var MOBILE_VIEWPORT = { width: 390, height: 844 };
4299
- var DESKTOP_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
4300
- var MOBILE_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1";
4301
4302
  var SCROLL_STEP = 900;
4302
4303
  var SETTLE_MS = 400;
4303
4304
  async function scrollAndStitch(page, device = "desktop") {
@@ -4348,14 +4349,13 @@ async function scrollAndStitch(page, device = "desktop") {
4348
4349
  }
4349
4350
  async function captureLocal(url, device, wantScreenshot, wantBranding) {
4350
4351
  const vp = device === "mobile" ? MOBILE_VIEWPORT : DESKTOP_VIEWPORT;
4351
- const ua = device === "mobile" ? MOBILE_UA : DESKTOP_UA;
4352
4352
  const browser = await chromium2.launch({ headless: true });
4353
- const context = await browser.newContext({
4353
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
4354
+ const context = await browser.newContext(withRuntimeUserAgent({
4354
4355
  viewport: vp,
4355
4356
  locale: "en-US",
4356
- userAgent: ua,
4357
4357
  isMobile: device === "mobile"
4358
- });
4358
+ }, runtimeUserAgent));
4359
4359
  const page = await context.newPage();
4360
4360
  try {
4361
4361
  await page.goto(url, { waitUntil: "networkidle", timeout: 6e4 });
@@ -4374,12 +4374,11 @@ async function captureKernel(url, kernelApiKey, device, wantScreenshot, wantBran
4374
4374
  const session = await kernel.browsers.create({ stealth: true, timeout_seconds: 240 });
4375
4375
  const browser = await chromium2.connectOverCDP(session.cdp_ws_url);
4376
4376
  const vp = device === "mobile" ? MOBILE_VIEWPORT : DESKTOP_VIEWPORT;
4377
- const ua = device === "mobile" ? MOBILE_UA : DESKTOP_UA;
4378
- const context = browser.contexts()[0] ?? await browser.newContext({
4377
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
4378
+ const context = browser.contexts()[0] ?? await browser.newContext(withRuntimeUserAgent({
4379
4379
  viewport: vp,
4380
- userAgent: ua,
4381
4380
  isMobile: device === "mobile"
4382
- });
4381
+ }, runtimeUserAgent));
4383
4382
  const page = context.pages()[0] ?? await context.newPage();
4384
4383
  try {
4385
4384
  await page.goto(url, { waitUntil: "networkidle", timeout: 6e4 });
@@ -4885,6 +4884,7 @@ async function crawlWithRotation(urls, opts) {
4885
4884
  const worker = async () => {
4886
4885
  let kb = null;
4887
4886
  let browser = null;
4887
+ let runtimeUserAgent = null;
4888
4888
  let inBrowser = 0;
4889
4889
  let kbOpenedAtMs = 0;
4890
4890
  const retire = async () => {
@@ -4892,6 +4892,7 @@ async function crawlWithRotation(urls, opts) {
4892
4892
  browser = null;
4893
4893
  kb = null;
4894
4894
  kbOpenedAtMs = 0;
4895
+ runtimeUserAgent = null;
4895
4896
  if (b) await b.close().catch(() => {
4896
4897
  });
4897
4898
  if (k) {
@@ -4913,9 +4914,10 @@ async function crawlWithRotation(urls, opts) {
4913
4914
  kb = await createBrowserWithRetry(client2, headless);
4914
4915
  kbOpenedAtMs = Date.now();
4915
4916
  browser = await withTimeout(chromium3.connectOverCDP(kb.cdp_ws_url), 3e4);
4917
+ runtimeUserAgent = await detectRuntimeUserAgent(browser);
4916
4918
  inBrowser = 0;
4917
4919
  }
4918
- const context = await browser.newContext({ serviceWorkers: "block" });
4920
+ const context = await browser.newContext(withRuntimeUserAgent({ serviceWorkers: "block" }, runtimeUserAgent));
4919
4921
  try {
4920
4922
  r = await crawlPage(context, url);
4921
4923
  } finally {
@@ -8664,7 +8666,7 @@ var siteExtractFn = inngest.createFunction(
8664
8666
  });
8665
8667
  }) : null;
8666
8668
  const finalized = await step.run("finalize", async () => {
8667
- const { assembleExtractArtifacts } = await import("./extract-bundle-IOM45SFL.js");
8669
+ const { assembleExtractArtifacts } = await import("./extract-bundle-ITPFWRNI.js");
8668
8670
  const current = await getExtractJob(jobId2);
8669
8671
  if (!current) throw new Error("extract job disappeared before finalization");
8670
8672
  const stored = await assembleExtractArtifacts(current, { branding, imageAudit });
@@ -9606,7 +9608,7 @@ async function resolveUsZipsPath(requestedPath) {
9606
9608
  async function loadZipGroups(stateAbbr, requestedPath, warnings, sourceMode) {
9607
9609
  const mode = resolvedLocationDataMode({ zipSource: sourceMode });
9608
9610
  if (mode === "hosted") {
9609
- const { getHostedZipGroups } = await import("./location-data-repository-SHEOIBTL.js");
9611
+ const { getHostedZipGroups } = await import("./location-data-repository-HDNCU4Z4.js");
9610
9612
  const hosted = await getHostedZipGroups(stateAbbr);
9611
9613
  if (!hosted.dataset) {
9612
9614
  throw new DirectoryLocationError(
@@ -9669,7 +9671,7 @@ async function resolveDirectoryMarkets(options, config = {}) {
9669
9671
  const sourceUrl = censusStateUrl(state.fips);
9670
9672
  const warnings = [];
9671
9673
  if (resolvedLocationDataMode(config) === "hosted") {
9672
- const { queryHostedLocationMarkets } = await import("./location-data-repository-SHEOIBTL.js");
9674
+ const { queryHostedLocationMarkets } = await import("./location-data-repository-HDNCU4Z4.js");
9673
9675
  const hosted = await queryHostedLocationMarkets({
9674
9676
  state: state.abbr,
9675
9677
  minPopulation: options.minPopulation,
@@ -16681,7 +16683,6 @@ facebookAdApp.post("/media", createApiKeyAuth(), async (c) => {
16681
16683
  try {
16682
16684
  res = await fetch(parsedUrl.href, {
16683
16685
  headers: {
16684
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
16685
16686
  "Referer": "https://www.facebook.com/"
16686
16687
  },
16687
16688
  signal: AbortSignal.timeout(6e4)
@@ -17998,10 +17999,10 @@ function blobKeyPrefix(shortcode, sourceUrl) {
17998
17999
  const slug = shortcode ? `instagram-${shortcode}` : safeFilePart(sourceUrl);
17999
18000
  return `instagram/${stamp}-${slug}`;
18000
18001
  }
18001
- async function fetchMediaBytes(url, referer) {
18002
+ async function fetchMediaBytes(url, referer, runtimeUserAgent) {
18002
18003
  const res = await fetch(url, {
18003
18004
  headers: {
18004
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
18005
+ ...runtimeUserAgent ? { "User-Agent": runtimeUserAgent } : {},
18005
18006
  "Referer": referer
18006
18007
  },
18007
18008
  signal: AbortSignal.timeout(9e4)
@@ -18121,6 +18122,7 @@ instagramApp.post("/media-download", createApiKeyAuth(), async (c) => {
18121
18122
  const launch = await resolveInstagramLaunch(body);
18122
18123
  await driver.launch(launch.config);
18123
18124
  const page = driver.getPage();
18125
+ const runtimeUserAgent = await detectPageUserAgent(page);
18124
18126
  const capturedMediaUrls = [];
18125
18127
  page.on("response", (response) => {
18126
18128
  const url = normalizeInstagramMediaUrl(response.url());
@@ -18149,7 +18151,7 @@ instagramApp.post("/media-download", createApiKeyAuth(), async (c) => {
18149
18151
  downloads.push({ kind: "text", url: null, savedPath: storedText.url, sizeBytes: storedText.bytes, mimeType: "text/plain", error: null });
18150
18152
  if (mediaTypes.has("image") && extraction.imageUrl) {
18151
18153
  try {
18152
- const { bytes, mimeType } = await fetchMediaBytes(extraction.imageUrl, extraction.pageUrl);
18154
+ const { bytes, mimeType } = await fetchMediaBytes(extraction.imageUrl, extraction.pageUrl, runtimeUserAgent);
18153
18155
  const ext = extFromMime(mimeType, "jpg");
18154
18156
  const stored = await store.put(`${prefix}/image.${ext}`, bytes, mimeType ?? "application/octet-stream");
18155
18157
  downloads.push({ kind: "image", url: extraction.imageUrl, savedPath: stored.url, sizeBytes: stored.bytes, mimeType, error: null });
@@ -18165,7 +18167,7 @@ instagramApp.post("/media-download", createApiKeyAuth(), async (c) => {
18165
18167
  if (track.streamType === "audio" && !mediaTypes.has("audio")) continue;
18166
18168
  const kind = track.streamType === "audio" ? "audio" : "video";
18167
18169
  try {
18168
- const { bytes, mimeType } = await fetchMediaBytes(track.url, extraction.pageUrl);
18170
+ const { bytes, mimeType } = await fetchMediaBytes(track.url, extraction.pageUrl, runtimeUserAgent);
18169
18171
  const stored = await store.put(`${prefix}/${trackFilename(track, index, extraction.shortcode)}`, bytes, mimeType ?? "video/mp4");
18170
18172
  downloads.push({ kind, url: track.url, savedPath: stored.url, sizeBytes: stored.bytes, mimeType, error: null });
18171
18173
  const isMuxInput = muxEligible && (track.url === extraction.selectedVideoTrack.url || track.url === extraction.selectedAudioTrack.url);
@@ -24041,6 +24043,12 @@ async function runFanoutCapture(page, input) {
24041
24043
  // src/services/browser-agent/browser-agent-service.ts
24042
24044
  function keepHostedBrowserAlive(_browser) {
24043
24045
  }
24046
+ async function currentOrRuntimeContext(browser) {
24047
+ const current = browser.contexts()[0];
24048
+ if (current) return current;
24049
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
24050
+ return browser.newContext(withRuntimeUserAgent({}, runtimeUserAgent));
24051
+ }
24044
24052
  var DEFAULT_TIMEOUT_SECONDS = 600;
24045
24053
  function client() {
24046
24054
  const apiKey = browserServiceApiKey();
@@ -24165,7 +24173,7 @@ async function pressKeys(runtimeSessionId, keys) {
24165
24173
  async function goto(cdpWsUrl, url) {
24166
24174
  const browser = await playwrightChromium.connectOverCDP(cdpWsUrl);
24167
24175
  try {
24168
- const context = browser.contexts()[0] ?? await browser.newContext();
24176
+ const context = await currentOrRuntimeContext(browser);
24169
24177
  const page = context.pages()[0] ?? await context.newPage();
24170
24178
  await page.goto(url, { waitUntil: "domcontentloaded", timeout: 45e3 });
24171
24179
  return { url: page.url(), title: await page.title() };
@@ -24176,7 +24184,7 @@ async function goto(cdpWsUrl, url) {
24176
24184
  async function captureFanout(cdpWsUrl, input) {
24177
24185
  const browser = await playwrightChromium.connectOverCDP(cdpWsUrl);
24178
24186
  try {
24179
- const context = browser.contexts()[0] ?? await browser.newContext();
24187
+ const context = await currentOrRuntimeContext(browser);
24180
24188
  const page = context.pages()[0] ?? await context.newPage();
24181
24189
  return await runFanoutCapture(page, input);
24182
24190
  } finally {
@@ -24186,7 +24194,7 @@ async function captureFanout(cdpWsUrl, input) {
24186
24194
  async function readPage(cdpWsUrl) {
24187
24195
  const browser = await playwrightChromium.connectOverCDP(cdpWsUrl);
24188
24196
  try {
24189
- const context = browser.contexts()[0] ?? await browser.newContext();
24197
+ const context = await currentOrRuntimeContext(browser);
24190
24198
  const page = context.pages()[0] ?? await context.newPage();
24191
24199
  const url = page.url();
24192
24200
  const title = await page.title().catch(() => "");
@@ -24241,7 +24249,7 @@ async function readPage(cdpWsUrl) {
24241
24249
  async function locatePageTargets(cdpWsUrl, targets) {
24242
24250
  const browser = await playwrightChromium.connectOverCDP(cdpWsUrl);
24243
24251
  try {
24244
- const context = browser.contexts()[0] ?? await browser.newContext();
24252
+ const context = await currentOrRuntimeContext(browser);
24245
24253
  const page = context.pages()[0] ?? await context.newPage();
24246
24254
  const url = page.url();
24247
24255
  const title = await page.title().catch(() => "");
@@ -25541,6 +25549,31 @@ var ACCESS_TTL_SECONDS = 3600;
25541
25549
  var REFRESH_TTL_SECONDS = 60 * 60 * 24 * 30;
25542
25550
  var CODE_TTL_SECONDS = 60;
25543
25551
  var ROTATION_GRACE_SECONDS = 120;
25552
+ var OAUTH_DATABASE_TIMEOUT_MS = 5e3;
25553
+ var OAuthDatabaseUnavailableError = class extends Error {
25554
+ stage;
25555
+ constructor(stage) {
25556
+ super("The authorization service database is temporarily unavailable.");
25557
+ this.name = "OAuthDatabaseUnavailableError";
25558
+ this.stage = stage;
25559
+ }
25560
+ };
25561
+ async function withOAuthDatabaseDeadline(stage, operation, timeoutMs = OAUTH_DATABASE_TIMEOUT_MS) {
25562
+ const startedAt = Date.now();
25563
+ let timeout;
25564
+ try {
25565
+ const value = await Promise.race([
25566
+ operation,
25567
+ new Promise((_, reject) => {
25568
+ timeout = setTimeout(() => reject(new OAuthDatabaseUnavailableError(stage)), timeoutMs);
25569
+ })
25570
+ ]);
25571
+ console.log("[oauth-db] stage=%s status=ok duration_ms=%d", stage, Date.now() - startedAt);
25572
+ return value;
25573
+ } finally {
25574
+ if (timeout) clearTimeout(timeout);
25575
+ }
25576
+ }
25544
25577
  var secureCookies = process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
25545
25578
  var sessionCookieOptions = {
25546
25579
  httpOnly: true,
@@ -25603,7 +25636,7 @@ async function sessionUser(token4) {
25603
25636
  if (!token4) return null;
25604
25637
  const userId = verifySession(token4);
25605
25638
  if (!userId) return null;
25606
- const user = await getUserById(userId);
25639
+ const user = await withOAuthDatabaseDeadline("session-user", getUserById(userId));
25607
25640
  return user ?? null;
25608
25641
  }
25609
25642
  function escapeHtml(value) {
@@ -25693,6 +25726,18 @@ function redirectWithError(redirectUri, state, error) {
25693
25726
  return new Response(null, { status: 302, headers: { Location: u.toString() } });
25694
25727
  }
25695
25728
  var oauthApp = new Hono23();
25729
+ oauthApp.onError((error, c) => {
25730
+ if (error instanceof OAuthDatabaseUnavailableError) {
25731
+ console.error("[oauth-db] stage=%s status=timeout timeout_ms=%d", error.stage, OAUTH_DATABASE_TIMEOUT_MS);
25732
+ c.header("Retry-After", "5");
25733
+ return c.json({
25734
+ error: "temporarily_unavailable",
25735
+ error_description: "Authorization is temporarily unavailable. Please retry shortly."
25736
+ }, 503);
25737
+ }
25738
+ console.error("[oauth] unhandled error:", error);
25739
+ return c.json({ error: "server_error", error_description: "Authorization could not be completed." }, 500);
25740
+ });
25696
25741
  oauthApp.use("*", async (c, next) => {
25697
25742
  c.header("Access-Control-Allow-Origin", "*");
25698
25743
  c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
@@ -25775,7 +25820,7 @@ oauthApp.post("/oauth/register", async (c) => {
25775
25820
  }
25776
25821
  const clientName = typeof body.client_name === "string" ? body.client_name : null;
25777
25822
  const clientId = `client_${randomBytes2(16).toString("hex")}`;
25778
- await registerClient(clientId, redirectUris, clientName);
25823
+ await withOAuthDatabaseDeadline("register-client", registerClient(clientId, redirectUris, clientName));
25779
25824
  console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
25780
25825
  return c.json({
25781
25826
  client_id: clientId,
@@ -25802,7 +25847,7 @@ function parseAuthParams(get) {
25802
25847
  async function validateAuthRequest(p) {
25803
25848
  if (p.response_type !== "code") return { ok: false, status: 400, message: "response_type must be code" };
25804
25849
  if (!p.client_id) return { ok: false, status: 400, message: "client_id is required" };
25805
- const client2 = await getClient(p.client_id);
25850
+ const client2 = await withOAuthDatabaseDeadline("get-client", getClient(p.client_id));
25806
25851
  if (!client2) return { ok: false, status: 400, message: "unknown client_id" };
25807
25852
  if (!p.redirect_uri || !client2.redirect_uris.includes(p.redirect_uri)) return { ok: false, status: 400, message: "redirect_uri does not match the registered set" };
25808
25853
  if (!isAllowedRedirect(p.redirect_uri)) return { ok: false, status: 400, message: "redirect_uri host not allowed" };
@@ -25835,7 +25880,7 @@ oauthApp.post("/oauth/authorize", async (c) => {
25835
25880
  if (action === "login") {
25836
25881
  const email = (get("email") ?? "").trim().toLowerCase();
25837
25882
  const password = get("password") ?? "";
25838
- const user2 = email ? await getUserByEmail(email) : void 0;
25883
+ const user2 = email ? await withOAuthDatabaseDeadline("login-user", getUserByEmail(email)) : void 0;
25839
25884
  const okPw = !!(user2 && user2.password_hash && verifyPassword(password, user2.password_hash));
25840
25885
  console.log("[oauth-dcr] LOGIN email=%s found=%s hasHash=%s pwLen=%d ok=%s", email, !!user2, !!(user2 && user2.password_hash), password.length, okPw);
25841
25886
  if (!okPw) {
@@ -25851,7 +25896,7 @@ oauthApp.post("/oauth/authorize", async (c) => {
25851
25896
  const scope = negotiateScope(p.scope, user, p.resource);
25852
25897
  const code = `code_${randomBytes2(32).toString("base64url")}`;
25853
25898
  const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
25854
- await putCode({
25899
+ await withOAuthDatabaseDeadline("put-authorization-code", putCode({
25855
25900
  code,
25856
25901
  client_id: p.client_id,
25857
25902
  redirect_uri: p.redirect_uri,
@@ -25860,7 +25905,7 @@ oauthApp.post("/oauth/authorize", async (c) => {
25860
25905
  scope,
25861
25906
  resource: p.resource || RESOURCE(),
25862
25907
  expires_at: expiresAt
25863
- });
25908
+ }));
25864
25909
  const u = new URL(p.redirect_uri);
25865
25910
  u.searchParams.set("code", code);
25866
25911
  if (p.state) u.searchParams.set("state", p.state);
@@ -25890,18 +25935,18 @@ oauthApp.post("/oauth/token", async (c) => {
25890
25935
  const clientId = get("client_id") ?? "";
25891
25936
  const redirectUri = get("redirect_uri") ?? "";
25892
25937
  const verifier = get("code_verifier") ?? "";
25893
- const record = await consumeCode(code);
25938
+ const record = await withOAuthDatabaseDeadline("consume-authorization-code", consumeCode(code));
25894
25939
  if (!record) return tokenErrorResponse(c, "invalid_grant", "Authorization code is invalid or already used", 400);
25895
25940
  if (new Date(record.expires_at).getTime() < Date.now()) return tokenErrorResponse(c, "invalid_grant", "Authorization code has expired", 400);
25896
25941
  if (record.client_id !== clientId) return tokenErrorResponse(c, "invalid_grant", "client_id mismatch", 400);
25897
25942
  if (record.redirect_uri !== redirectUri) return tokenErrorResponse(c, "invalid_grant", "redirect_uri mismatch", 400);
25898
25943
  if (!pkceMatches(verifier, record.code_challenge)) return tokenErrorResponse(c, "invalid_grant", "PKCE verification failed", 400);
25899
- const user = await getUserByEmail(record.identity);
25944
+ const user = await withOAuthDatabaseDeadline("token-user", getUserByEmail(record.identity));
25900
25945
  const plan = user ? resolvePlan(user) : "free";
25901
25946
  const audience = record.resource ?? RESOURCE();
25902
25947
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
25903
25948
  const refreshToken = `rt_${randomBytes2(40).toString("base64url")}`;
25904
- await putRefresh({
25949
+ await withOAuthDatabaseDeadline("put-refresh-token", putRefresh({
25905
25950
  refresh_token: refreshToken,
25906
25951
  client_id: clientId,
25907
25952
  identity: record.identity,
@@ -25909,15 +25954,15 @@ oauthApp.post("/oauth/token", async (c) => {
25909
25954
  resource: audience,
25910
25955
  expires_at: new Date(Date.now() + REFRESH_TTL_SECONDS * 1e3).toISOString(),
25911
25956
  revoked: 0
25912
- });
25957
+ }));
25913
25958
  return c.json({ access_token: accessToken, token_type: "Bearer", expires_in: ACCESS_TTL_SECONDS, refresh_token: refreshToken, scope: record.scope });
25914
25959
  }
25915
25960
  if (grantType === "refresh_token") {
25916
25961
  const refreshToken = get("refresh_token") ?? "";
25917
25962
  const clientId = get("client_id") ?? "";
25918
- const record = await getRefresh(refreshToken);
25963
+ const record = await withOAuthDatabaseDeadline("get-refresh-token", getRefresh(refreshToken));
25919
25964
  if (record && record.revoked && record.replaced_by && record.rotated_at && Date.now() - new Date(record.rotated_at).getTime() < ROTATION_GRACE_SECONDS * 1e3 && (!clientId || record.client_id === clientId)) {
25920
- const graceUser = await getUserByEmail(record.identity);
25965
+ const graceUser = await withOAuthDatabaseDeadline("refresh-grace-user", getUserByEmail(record.identity));
25921
25966
  const gracePlan = graceUser ? resolvePlan(graceUser) : "free";
25922
25967
  const graceAudience = record.resource ?? RESOURCE();
25923
25968
  const graceAccess = await mintAccessToken(record.identity, record.scope, gracePlan, graceAudience);
@@ -25926,12 +25971,12 @@ oauthApp.post("/oauth/token", async (c) => {
25926
25971
  if (!record || record.revoked) return tokenErrorResponse(c, "invalid_grant", "Refresh token is invalid or revoked", 400);
25927
25972
  if (new Date(record.expires_at).getTime() < Date.now()) return tokenErrorResponse(c, "invalid_grant", "Refresh token has expired", 400);
25928
25973
  if (clientId && record.client_id !== clientId) return tokenErrorResponse(c, "invalid_grant", "client_id mismatch", 400);
25929
- const user = await getUserByEmail(record.identity);
25974
+ const user = await withOAuthDatabaseDeadline("refresh-user", getUserByEmail(record.identity));
25930
25975
  const plan = user ? resolvePlan(user) : "free";
25931
25976
  const audience = record.resource ?? RESOURCE();
25932
25977
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
25933
25978
  const nextRefresh = `rt_${randomBytes2(40).toString("base64url")}`;
25934
- await rotateRefresh(refreshToken, {
25979
+ await withOAuthDatabaseDeadline("rotate-refresh-token", rotateRefresh(refreshToken, {
25935
25980
  refresh_token: nextRefresh,
25936
25981
  client_id: record.client_id,
25937
25982
  identity: record.identity,
@@ -25939,7 +25984,7 @@ oauthApp.post("/oauth/token", async (c) => {
25939
25984
  resource: audience,
25940
25985
  expires_at: new Date(Date.now() + REFRESH_TTL_SECONDS * 1e3).toISOString(),
25941
25986
  revoked: 0
25942
- });
25987
+ }));
25943
25988
  return c.json({ access_token: accessToken, token_type: "Bearer", expires_in: ACCESS_TTL_SECONDS, refresh_token: nextRefresh, scope: record.scope });
25944
25989
  }
25945
25990
  return tokenErrorResponse(c, "unsupported_grant_type", "grant_type must be authorization_code or refresh_token", 400);
@@ -34346,8 +34391,7 @@ app.use("*", (c, next) => {
34346
34391
  const op = opForCostPath(new URL(c.req.url).pathname);
34347
34392
  if (!op) return next();
34348
34393
  const probeRunId = c.req.header("x-cost-probe-id") ?? null;
34349
- const forceHeadless = c.req.header("x-kernel-headless") === "true";
34350
- return runWithCostContext({ op, probeRunId, forceHeadless }, () => next());
34394
+ return runWithCostContext({ op, probeRunId }, () => next());
34351
34395
  });
34352
34396
  app.post("/auth/register", requireAllowedOrigin, async (c) => {
34353
34397
  const { email, password } = await c.req.json();
@@ -37165,7 +37209,7 @@ app.get("/cron/tick", async (c) => {
37165
37209
  if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
37166
37210
  return c.json({ error: "Unauthorized" }, 401);
37167
37211
  }
37168
- const { drainQueue } = await import("./worker-2LIOH744.js");
37212
+ const { drainQueue } = await import("./worker-73AYU4IK.js");
37169
37213
  const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
37170
37214
  const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
37171
37215
  const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation] = await Promise.all([
@@ -37218,8 +37262,8 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
37218
37262
  finishExtractJob: finishExtractJob2,
37219
37263
  terminalExtractJobStatus: terminalExtractJobStatus2,
37220
37264
  extractJobLimitInfo: extractJobLimitInfo2
37221
- } = await import("./site-extract-repository-XNI4ZK2M.js");
37222
- const { assembleExtractArtifacts } = await import("./extract-bundle-IOM45SFL.js");
37265
+ } = await import("./site-extract-repository-CKCJDP7N.js");
37266
+ const { assembleExtractArtifacts } = await import("./extract-bundle-ITPFWRNI.js");
37223
37267
  const existing = await getExtractJob2(jobId2);
37224
37268
  if (!existing) return c.json({ error: "job not found" }, 404);
37225
37269
  if (existing.status !== "failed") {
@@ -37246,7 +37290,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
37246
37290
  if (!updated) return c.json({ error: "job state changed during re-finalization" }, 409);
37247
37291
  let settlement = "already_settled_or_refunded";
37248
37292
  if (job.billedMc == null && job.userId != null) {
37249
- const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-XNI4ZK2M.js");
37293
+ const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-CKCJDP7N.js");
37250
37294
  const heldMc = Number(job.options.heldMc ?? 0);
37251
37295
  const successful = await countSuccessfulPages2(jobId2);
37252
37296
  const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
@@ -37425,4 +37469,4 @@ app.get("/blog/:slug/", (c) => {
37425
37469
  export {
37426
37470
  app
37427
37471
  };
37428
- //# sourceMappingURL=server-7CLMCX42.js.map
37472
+ //# sourceMappingURL=server-BTDXZA5U.js.map