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
@@ -3671,6 +3671,7 @@ __export(db_exports, {
3671
3671
  CORE_SCHEMA_VERSION: () => CORE_SCHEMA_VERSION,
3672
3672
  CREDIT_LOTS_EPOCH: () => CREDIT_LOTS_EPOCH,
3673
3673
  CREDIT_LOT_TTL: () => CREDIT_LOT_TTL,
3674
+ SITE_EXTRACT_COUNTER_REPAIR_VERSION: () => SITE_EXTRACT_COUNTER_REPAIR_VERSION,
3674
3675
  SiteAuditJobRowSchema: () => SiteAuditJobRowSchema,
3675
3676
  SiteAuditPhaseLogRowSchema: () => SiteAuditPhaseLogRowSchema,
3676
3677
  advanceWorkflowRunStep: () => advanceWorkflowRunStep,
@@ -3808,7 +3809,7 @@ async function hasCurrentCoreSchema(db) {
3808
3809
  const result = await db.execute(`SELECT ${checks.join(" AND ")} AS ready`);
3809
3810
  return Number(result.rows[0]?.ready ?? 0) === 1;
3810
3811
  }
3811
- async function runIdempotentCoreDataBackfills(db) {
3812
+ async function runSiteExtractCounterRepair(db) {
3812
3813
  await db.execute(`
3813
3814
  UPDATE site_extract_jobs SET
3814
3815
  attempted_urls = done_urls,
@@ -3848,6 +3849,18 @@ async function runIdempotentCoreDataBackfills(db) {
3848
3849
  AND failed_urls = 0
3849
3850
  `);
3850
3851
  }
3852
+ async function runPendingDataRepairs(db) {
3853
+ const applied = await db.execute({
3854
+ sql: "SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1",
3855
+ args: [SITE_EXTRACT_COUNTER_REPAIR_VERSION]
3856
+ });
3857
+ if (applied.rows.length > 0) return;
3858
+ await runSiteExtractCounterRepair(db);
3859
+ await db.execute({
3860
+ sql: "INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",
3861
+ args: [SITE_EXTRACT_COUNTER_REPAIR_VERSION]
3862
+ });
3863
+ }
3851
3864
  function getDb() {
3852
3865
  if (!_db) _db = (0, import_http.createClient)({ url: DB_URL, authToken: DB_TOKEN });
3853
3866
  return _db;
@@ -3855,7 +3868,7 @@ function getDb() {
3855
3868
  function setDbForTesting(client2) {
3856
3869
  _db = client2;
3857
3870
  }
3858
- async function migrate() {
3871
+ async function migrate(options = {}) {
3859
3872
  const db = getDb();
3860
3873
  let applied;
3861
3874
  try {
@@ -3874,12 +3887,15 @@ async function migrate() {
3874
3887
  `);
3875
3888
  applied = { rows: [] };
3876
3889
  }
3877
- if (applied.rows.length > 0 && await hasCurrentCoreSchema(db)) {
3878
- await runIdempotentCoreDataBackfills(db);
3879
- return;
3890
+ if (applied.rows.length > 0) {
3891
+ if (!options.verifyCurrentSchema) return;
3892
+ if (await hasCurrentCoreSchema(db)) {
3893
+ if (options.runDataRepairs) await runPendingDataRepairs(db);
3894
+ return;
3895
+ }
3880
3896
  }
3881
3897
  if (await hasCurrentCoreSchema(db)) {
3882
- await runIdempotentCoreDataBackfills(db);
3898
+ await runPendingDataRepairs(db);
3883
3899
  await db.execute({
3884
3900
  sql: "INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",
3885
3901
  args: [CORE_SCHEMA_VERSION]
@@ -4692,7 +4708,7 @@ async function migrate() {
4692
4708
  if (!await hasCurrentCoreSchema(db)) {
4693
4709
  throw new Error("core schema migration finished without all required tables, indexes, and columns");
4694
4710
  }
4695
- await runIdempotentCoreDataBackfills(db);
4711
+ await runPendingDataRepairs(db);
4696
4712
  await db.execute({
4697
4713
  sql: "INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",
4698
4714
  args: [CORE_SCHEMA_VERSION]
@@ -6666,7 +6682,7 @@ async function listActiveUserIds() {
6666
6682
  const res = await getDb().execute("SELECT id FROM users WHERE active = 1");
6667
6683
  return res.rows.map((r) => Number(r.id));
6668
6684
  }
6669
- var import_http, import_node_crypto, import_zod, DB_URL, DB_TOKEN, _db, _rateLimitSchemaReady, CORE_SCHEMA_VERSION, CORE_SCHEMA_TABLES, CORE_SCHEMA_INDEXES, CORE_SCHEMA_TRIGGERS, CORE_SCHEMA_COLUMNS, REQUEST_EVENT_RESULT_MAX_BYTES, CREDIT_LOT_TTL, SiteAuditJobRowSchema, SiteAuditPhaseLogRowSchema;
6685
+ var import_http, import_node_crypto, import_zod, DB_URL, DB_TOKEN, _db, _rateLimitSchemaReady, CORE_SCHEMA_VERSION, SITE_EXTRACT_COUNTER_REPAIR_VERSION, CORE_SCHEMA_TABLES, CORE_SCHEMA_INDEXES, CORE_SCHEMA_TRIGGERS, CORE_SCHEMA_COLUMNS, REQUEST_EVENT_RESULT_MAX_BYTES, CREDIT_LOT_TTL, SiteAuditJobRowSchema, SiteAuditPhaseLogRowSchema;
6670
6686
  var init_db = __esm({
6671
6687
  "src/api/db.ts"() {
6672
6688
  "use strict";
@@ -6680,6 +6696,7 @@ var init_db = __esm({
6680
6696
  _db = null;
6681
6697
  _rateLimitSchemaReady = false;
6682
6698
  CORE_SCHEMA_VERSION = "2026-07-30.1";
6699
+ SITE_EXTRACT_COUNTER_REPAIR_VERSION = "data-2026-07-28-site-extract-counters.1";
6683
6700
  CORE_SCHEMA_TABLES = [
6684
6701
  "admin_credit_adjustment_events",
6685
6702
  "admin_credit_adjustments",
@@ -6842,11 +6859,12 @@ function runWithCostContext(ctx, fn) {
6842
6859
  function currentCostContext() {
6843
6860
  return storage.getStore();
6844
6861
  }
6845
- var import_node_async_hooks, storage;
6862
+ var import_node_async_hooks, HEADLESS_HARVEST_TEST_ENV, storage;
6846
6863
  var init_cost_context = __esm({
6847
6864
  "src/api/cost-context.ts"() {
6848
6865
  "use strict";
6849
6866
  import_node_async_hooks = require("async_hooks");
6867
+ HEADLESS_HARVEST_TEST_ENV = "MCP_SCRAPER_ALLOW_HEADLESS_HARVEST_TEST";
6850
6868
  storage = new import_node_async_hooks.AsyncLocalStorage();
6851
6869
  }
6852
6870
  });
@@ -7197,6 +7215,42 @@ var init_bounded_response = __esm({
7197
7215
  }
7198
7216
  });
7199
7217
 
7218
+ // src/lib/browser-user-agent.ts
7219
+ async function detectPageUserAgent(page) {
7220
+ try {
7221
+ const value = await page.evaluate(() => navigator.userAgent);
7222
+ return typeof value === "string" && RUNTIME_BROWSER_UA.test(value) ? value : null;
7223
+ } catch {
7224
+ return null;
7225
+ }
7226
+ }
7227
+ async function detectRuntimeUserAgent(browser) {
7228
+ let probeContext = null;
7229
+ try {
7230
+ let page = browser.contexts().flatMap((context) => context.pages())[0];
7231
+ if (!page) {
7232
+ probeContext = await browser.newContext();
7233
+ page = probeContext.pages()[0] ?? await probeContext.newPage();
7234
+ }
7235
+ return await detectPageUserAgent(page);
7236
+ } catch {
7237
+ return null;
7238
+ } finally {
7239
+ if (probeContext) await probeContext.close().catch(() => {
7240
+ });
7241
+ }
7242
+ }
7243
+ function withRuntimeUserAgent(options, runtimeUserAgent) {
7244
+ return runtimeUserAgent ? { ...options, userAgent: runtimeUserAgent } : options;
7245
+ }
7246
+ var RUNTIME_BROWSER_UA;
7247
+ var init_browser_user_agent = __esm({
7248
+ "src/lib/browser-user-agent.ts"() {
7249
+ "use strict";
7250
+ RUNTIME_BROWSER_UA = /\b(?:HeadlessChrome|Chrome|Chromium|Firefox)\/\d+|\bVersion\/\d+.*\bSafari\//;
7251
+ }
7252
+ });
7253
+
7200
7254
  // src/api/kernel-fetch.ts
7201
7255
  async function fetchWithKernel(url) {
7202
7256
  const checked = await validatePublicHttpUrl(url, { field: "browser URL" });
@@ -7210,10 +7264,10 @@ async function fetchWithKernel(url) {
7210
7264
  const kb = await client2.browsers.create({ stealth: true, ...useHeadless ? { headless: true } : {}, timeout_seconds: 60 });
7211
7265
  const browser = await import_playwright.chromium.connectOverCDP(kb.cdp_ws_url);
7212
7266
  try {
7213
- const context = await browser.newContext({
7214
- 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",
7267
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
7268
+ const context = await browser.newContext(withRuntimeUserAgent({
7215
7269
  serviceWorkers: "block"
7216
- });
7270
+ }, runtimeUserAgent));
7217
7271
  const page = context.pages()[0] ?? await context.newPage();
7218
7272
  const guard = await installBrowserRequestGuard(page, auditedUrl);
7219
7273
  await page.goto(auditedUrl, { waitUntil: "domcontentloaded", timeout: 3e4 });
@@ -7244,6 +7298,7 @@ var init_kernel_fetch = __esm({
7244
7298
  init_browser_request_guard();
7245
7299
  init_url_utils();
7246
7300
  init_bounded_response();
7301
+ init_browser_user_agent();
7247
7302
  }
7248
7303
  });
7249
7304
 
@@ -7827,14 +7882,13 @@ async function scrollAndStitch(page, device = "desktop") {
7827
7882
  }
7828
7883
  async function captureLocal(url, device, wantScreenshot, wantBranding) {
7829
7884
  const vp = device === "mobile" ? MOBILE_VIEWPORT : DESKTOP_VIEWPORT;
7830
- const ua = device === "mobile" ? MOBILE_UA : DESKTOP_UA;
7831
7885
  const browser = await import_playwright2.chromium.launch({ headless: true });
7832
- const context = await browser.newContext({
7886
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
7887
+ const context = await browser.newContext(withRuntimeUserAgent({
7833
7888
  viewport: vp,
7834
7889
  locale: "en-US",
7835
- userAgent: ua,
7836
7890
  isMobile: device === "mobile"
7837
- });
7891
+ }, runtimeUserAgent));
7838
7892
  const page = await context.newPage();
7839
7893
  try {
7840
7894
  await page.goto(url, { waitUntil: "networkidle", timeout: 6e4 });
@@ -7853,12 +7907,11 @@ async function captureKernel(url, kernelApiKey, device, wantScreenshot, wantBran
7853
7907
  const session = await kernel.browsers.create({ stealth: true, timeout_seconds: 240 });
7854
7908
  const browser = await import_playwright2.chromium.connectOverCDP(session.cdp_ws_url);
7855
7909
  const vp = device === "mobile" ? MOBILE_VIEWPORT : DESKTOP_VIEWPORT;
7856
- const ua = device === "mobile" ? MOBILE_UA : DESKTOP_UA;
7857
- const context = browser.contexts()[0] ?? await browser.newContext({
7910
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
7911
+ const context = browser.contexts()[0] ?? await browser.newContext(withRuntimeUserAgent({
7858
7912
  viewport: vp,
7859
- userAgent: ua,
7860
7913
  isMobile: device === "mobile"
7861
- });
7914
+ }, runtimeUserAgent));
7862
7915
  const page = context.pages()[0] ?? await context.newPage();
7863
7916
  try {
7864
7917
  await page.goto(url, { waitUntil: "networkidle", timeout: 6e4 });
@@ -7892,7 +7945,7 @@ async function capturePageData(url, opts = {}) {
7892
7945
  return await captureKernel(url, opts.kernelApiKey, device, wantShot, wantBranding);
7893
7946
  }
7894
7947
  }
7895
- var import_playwright2, import_sdk2, DESKTOP_VIEWPORT, MOBILE_VIEWPORT, DESKTOP_UA, MOBILE_UA, SCROLL_STEP, SETTLE_MS;
7948
+ var import_playwright2, import_sdk2, DESKTOP_VIEWPORT, MOBILE_VIEWPORT, SCROLL_STEP, SETTLE_MS;
7896
7949
  var init_screenshot = __esm({
7897
7950
  "src/lib/screenshot.ts"() {
7898
7951
  "use strict";
@@ -7900,10 +7953,9 @@ var init_screenshot = __esm({
7900
7953
  import_sdk2 = __toESM(require("@onkernel/sdk"), 1);
7901
7954
  init_branding_extractor();
7902
7955
  init_cost_telemetry();
7956
+ init_browser_user_agent();
7903
7957
  DESKTOP_VIEWPORT = { width: 1440, height: 900 };
7904
7958
  MOBILE_VIEWPORT = { width: 390, height: 844 };
7905
- 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";
7906
- 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";
7907
7959
  SCROLL_STEP = 900;
7908
7960
  SETTLE_MS = 400;
7909
7961
  }
@@ -8715,6 +8767,7 @@ async function crawlWithRotation(urls, opts) {
8715
8767
  const worker = async () => {
8716
8768
  let kb = null;
8717
8769
  let browser = null;
8770
+ let runtimeUserAgent = null;
8718
8771
  let inBrowser = 0;
8719
8772
  let kbOpenedAtMs = 0;
8720
8773
  const retire = async () => {
@@ -8722,6 +8775,7 @@ async function crawlWithRotation(urls, opts) {
8722
8775
  browser = null;
8723
8776
  kb = null;
8724
8777
  kbOpenedAtMs = 0;
8778
+ runtimeUserAgent = null;
8725
8779
  if (b) await b.close().catch(() => {
8726
8780
  });
8727
8781
  if (k) {
@@ -8743,9 +8797,10 @@ async function crawlWithRotation(urls, opts) {
8743
8797
  kb = await createBrowserWithRetry(client2, headless);
8744
8798
  kbOpenedAtMs = Date.now();
8745
8799
  browser = await withTimeout(import_playwright3.chromium.connectOverCDP(kb.cdp_ws_url), 3e4);
8800
+ runtimeUserAgent = await detectRuntimeUserAgent(browser);
8746
8801
  inBrowser = 0;
8747
8802
  }
8748
- const context = await browser.newContext({ serviceWorkers: "block" });
8803
+ const context = await browser.newContext(withRuntimeUserAgent({ serviceWorkers: "block" }, runtimeUserAgent));
8749
8804
  try {
8750
8805
  r = await crawlPage(context, url);
8751
8806
  } finally {
@@ -8812,6 +8867,7 @@ var init_rotating_proxy_crawl = __esm({
8812
8867
  init_cost_telemetry();
8813
8868
  init_browser_request_guard();
8814
8869
  init_bounded_response();
8870
+ init_browser_user_agent();
8815
8871
  init_browser_request_guard();
8816
8872
  DEFAULT_CONCURRENCY = 1;
8817
8873
  DEFAULT_URLS_PER_BROWSER = 10;
@@ -18981,21 +19037,11 @@ async function ensureKernelProfile(kernelClient, name) {
18981
19037
  if (!isKernelProfileConflict(err)) throw err;
18982
19038
  }
18983
19039
  }
18984
- async function detectRuntimeUserAgent(browser) {
18985
- try {
18986
- const page = browser.contexts()[0]?.pages()[0];
18987
- if (!page) return null;
18988
- const ua = await page.evaluate(() => navigator.userAgent);
18989
- return typeof ua === "string" && /Chrome\/\d+/.test(ua) ? ua : null;
18990
- } catch {
18991
- return null;
18992
- }
18993
- }
18994
19040
  function rankCheckContextOptions(config, runtimeUserAgent) {
18995
19041
  return {
18996
19042
  viewport: config.viewport,
18997
19043
  locale: config.locale,
18998
- userAgent: config.userAgent ?? (config.isMobile ? MOBILE_USER_AGENT : runtimeUserAgent ?? DESKTOP_USER_AGENT),
19044
+ ...runtimeUserAgent ? { userAgent: runtimeUserAgent } : {},
18999
19045
  ...config.deviceScaleFactor ? { deviceScaleFactor: config.deviceScaleFactor } : {},
19000
19046
  ...config.isMobile !== void 0 ? { isMobile: config.isMobile } : {},
19001
19047
  ...config.hasTouch !== void 0 ? { hasTouch: config.hasTouch } : {}
@@ -19041,7 +19087,7 @@ function buildYouTubeChannelVideosUrl(channelInput) {
19041
19087
  }
19042
19088
  return `https://www.youtube.com/${handle}/videos`;
19043
19089
  }
19044
- var import_playwright_extra, import_puppeteer_extra_plugin_stealth, import_playwright4, import_sdk5, DESKTOP_USER_AGENT, MOBILE_USER_AGENT, DEFAULT_KERNEL_BROWSER_TIMEOUT_SECONDS, KERNEL_BROWSER_CLOSE_TIMEOUT_MS, KERNEL_SESSION_DELETE_TIMEOUT_MS, BrowserDriver;
19090
+ var import_playwright_extra, import_puppeteer_extra_plugin_stealth, import_playwright4, import_sdk5, DEFAULT_KERNEL_BROWSER_TIMEOUT_SECONDS, KERNEL_BROWSER_CLOSE_TIMEOUT_MS, KERNEL_SESSION_DELETE_TIMEOUT_MS, BrowserDriver;
19045
19091
  var init_BrowserDriver = __esm({
19046
19092
  "src/driver/BrowserDriver.ts"() {
19047
19093
  "use strict";
@@ -19054,9 +19100,9 @@ var init_BrowserDriver = __esm({
19054
19100
  init_cost_telemetry();
19055
19101
  init_cost_context();
19056
19102
  init_cost_rates();
19103
+ init_browser_user_agent();
19104
+ init_browser_user_agent();
19057
19105
  import_playwright_extra.chromium.use((0, import_puppeteer_extra_plugin_stealth.default)());
19058
- DESKTOP_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";
19059
- MOBILE_USER_AGENT = "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";
19060
19106
  DEFAULT_KERNEL_BROWSER_TIMEOUT_SECONDS = 180;
19061
19107
  KERNEL_BROWSER_CLOSE_TIMEOUT_MS = 3e3;
19062
19108
  KERNEL_SESSION_DELETE_TIMEOUT_MS = 5e3;
@@ -19089,7 +19135,7 @@ var init_BrowserDriver = __esm({
19089
19135
  viewport: config.viewport,
19090
19136
  locale: config.locale,
19091
19137
  device,
19092
- userAgent: config.userAgent ?? (config.isMobile ? MOBILE_USER_AGENT : DESKTOP_USER_AGENT),
19138
+ userAgent: null,
19093
19139
  deviceScaleFactor: config.deviceScaleFactor ?? null,
19094
19140
  isMobile: config.isMobile === true,
19095
19141
  hasTouch: config.hasTouch === true
@@ -19177,7 +19223,7 @@ var init_BrowserDriver = __esm({
19177
19223
  }
19178
19224
  this.browser = await import_playwright4.chromium.connectOverCDP(kernelBrowser.cdp_ws_url);
19179
19225
  const runtimeUserAgent = await detectRuntimeUserAgent(this.browser);
19180
- this.debugSnapshot.context.userAgent = config.userAgent ?? (config.isMobile ? MOBILE_USER_AGENT : runtimeUserAgent ?? DESKTOP_USER_AGENT);
19226
+ this.debugSnapshot.context.userAgent = runtimeUserAgent;
19181
19227
  this.context = await this.browser.newContext(rankCheckContextOptions(config, runtimeUserAgent));
19182
19228
  await this.installEsbuildHelperShims(this.context);
19183
19229
  this.page = await this.context.newPage();
@@ -19199,17 +19245,19 @@ var init_BrowserDriver = __esm({
19199
19245
  ...config.browserExecutablePath ? { executablePath: config.browserExecutablePath } : {},
19200
19246
  ...config.ignoreDefaultArgs ? { ignoreDefaultArgs: config.ignoreDefaultArgs } : {}
19201
19247
  };
19202
- const ctxOpts = rankCheckContextOptions(config);
19203
19248
  if (config.profileDir) {
19204
19249
  this.context = await import_playwright_extra.chromium.launchPersistentContext(config.profileDir, {
19205
19250
  ...launchOpts,
19206
- ...ctxOpts
19251
+ ...rankCheckContextOptions(config)
19207
19252
  });
19208
19253
  await this.installEsbuildHelperShims(this.context);
19209
19254
  this.page = await this.context.newPage();
19255
+ this.debugSnapshot.context.userAgent = await detectPageUserAgent(this.page);
19210
19256
  } else {
19211
19257
  this.browser = await import_playwright_extra.chromium.launch(launchOpts);
19212
- this.context = await this.browser.newContext(ctxOpts);
19258
+ const runtimeUserAgent = await detectRuntimeUserAgent(this.browser);
19259
+ this.debugSnapshot.context.userAgent = runtimeUserAgent;
19260
+ this.context = await this.browser.newContext(rankCheckContextOptions(config, runtimeUserAgent));
19213
19261
  await this.installEsbuildHelperShims(this.context);
19214
19262
  this.page = await this.context.newPage();
19215
19263
  }
@@ -29553,7 +29601,6 @@ var init_facebook_ad_routes = __esm({
29553
29601
  try {
29554
29602
  res = await fetch(parsedUrl.href, {
29555
29603
  headers: {
29556
- "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",
29557
29604
  "Referer": "https://www.facebook.com/"
29558
29605
  },
29559
29606
  signal: AbortSignal.timeout(6e4)
@@ -30892,10 +30939,10 @@ function blobKeyPrefix(shortcode, sourceUrl) {
30892
30939
  const slug = shortcode ? `instagram-${shortcode}` : safeFilePart(sourceUrl);
30893
30940
  return `instagram/${stamp}-${slug}`;
30894
30941
  }
30895
- async function fetchMediaBytes(url, referer) {
30942
+ async function fetchMediaBytes(url, referer, runtimeUserAgent) {
30896
30943
  const res = await fetch(url, {
30897
30944
  headers: {
30898
- "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",
30945
+ ...runtimeUserAgent ? { "User-Agent": runtimeUserAgent } : {},
30899
30946
  "Referer": referer
30900
30947
  },
30901
30948
  signal: AbortSignal.timeout(9e4)
@@ -30946,6 +30993,7 @@ var init_instagram_routes = __esm({
30946
30993
  import_node_os8 = require("os");
30947
30994
  import_node_path11 = require("path");
30948
30995
  import_node_child_process2 = require("child_process");
30996
+ init_browser_user_agent();
30949
30997
  init_browser_service_env();
30950
30998
  init_BrowserDriver();
30951
30999
  init_api_auth();
@@ -31062,6 +31110,7 @@ var init_instagram_routes = __esm({
31062
31110
  const launch = await resolveInstagramLaunch(body);
31063
31111
  await driver.launch(launch.config);
31064
31112
  const page = driver.getPage();
31113
+ const runtimeUserAgent = await detectPageUserAgent(page);
31065
31114
  const capturedMediaUrls = [];
31066
31115
  page.on("response", (response) => {
31067
31116
  const url = normalizeInstagramMediaUrl(response.url());
@@ -31090,7 +31139,7 @@ var init_instagram_routes = __esm({
31090
31139
  downloads.push({ kind: "text", url: null, savedPath: storedText.url, sizeBytes: storedText.bytes, mimeType: "text/plain", error: null });
31091
31140
  if (mediaTypes.has("image") && extraction.imageUrl) {
31092
31141
  try {
31093
- const { bytes, mimeType } = await fetchMediaBytes(extraction.imageUrl, extraction.pageUrl);
31142
+ const { bytes, mimeType } = await fetchMediaBytes(extraction.imageUrl, extraction.pageUrl, runtimeUserAgent);
31094
31143
  const ext = extFromMime(mimeType, "jpg");
31095
31144
  const stored = await store.put(`${prefix}/image.${ext}`, bytes, mimeType ?? "application/octet-stream");
31096
31145
  downloads.push({ kind: "image", url: extraction.imageUrl, savedPath: stored.url, sizeBytes: stored.bytes, mimeType, error: null });
@@ -31106,7 +31155,7 @@ var init_instagram_routes = __esm({
31106
31155
  if (track.streamType === "audio" && !mediaTypes.has("audio")) continue;
31107
31156
  const kind = track.streamType === "audio" ? "audio" : "video";
31108
31157
  try {
31109
- const { bytes, mimeType } = await fetchMediaBytes(track.url, extraction.pageUrl);
31158
+ const { bytes, mimeType } = await fetchMediaBytes(track.url, extraction.pageUrl, runtimeUserAgent);
31110
31159
  const stored = await store.put(`${prefix}/${trackFilename(track, index, extraction.shortcode)}`, bytes, mimeType ?? "video/mp4");
31111
31160
  downloads.push({ kind, url: track.url, savedPath: stored.url, sizeBytes: stored.bytes, mimeType, error: null });
31112
31161
  const isMuxInput = muxEligible && (track.url === extraction.selectedVideoTrack.url || track.url === extraction.selectedAudioTrack.url);
@@ -31823,7 +31872,7 @@ var init_ai_surfaces = __esm({
31823
31872
  });
31824
31873
 
31825
31874
  // src/extractor/PAAExtractor.ts
31826
- var DESKTOP_USER_AGENT2, MOBILE_USER_AGENT2, PAAExtractor;
31875
+ var PAAExtractor;
31827
31876
  var init_PAAExtractor = __esm({
31828
31877
  "src/extractor/PAAExtractor.ts"() {
31829
31878
  "use strict";
@@ -31835,8 +31884,6 @@ var init_PAAExtractor = __esm({
31835
31884
  init_paa_answer_cleanup();
31836
31885
  init_errors();
31837
31886
  init_ai_surfaces();
31838
- DESKTOP_USER_AGENT2 = "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";
31839
- MOBILE_USER_AGENT2 = "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";
31840
31887
  PAAExtractor = class {
31841
31888
  constructor(driver, reporter) {
31842
31889
  this.driver = driver;
@@ -32646,7 +32693,6 @@ var init_PAAExtractor = __esm({
32646
32693
  requireUsEgress: options.requireUsEgress,
32647
32694
  viewport: isMobile ? { width: 390, height: 844 } : { width: 1280, height: 800 },
32648
32695
  locale: `${options.hl}-${options.gl.toUpperCase()}`,
32649
- userAgent: isMobile ? MOBILE_USER_AGENT2 : DESKTOP_USER_AGENT2,
32650
32696
  deviceScaleFactor: isMobile ? 3 : 1,
32651
32697
  isMobile,
32652
32698
  hasTouch: isMobile,
@@ -33265,7 +33311,12 @@ async function harvest(rawOptions) {
33265
33311
  max_questions: attemptOptions.maxQuestions
33266
33312
  }));
33267
33313
  const baseCtx = currentCostContext();
33268
- const attempt = baseCtx ? await runWithCostContext({ ...baseCtx, forceHeadful: true }, () => extractOnce2(attemptOptions, signal)) : await extractOnce2(attemptOptions, signal);
33314
+ const allowHeadlessTest = baseCtx?.harvestBrowserMode === "headless-test" && process.env[HEADLESS_HARVEST_TEST_ENV] === "true";
33315
+ const attempt = baseCtx ? await runWithCostContext({
33316
+ ...baseCtx,
33317
+ forceHeadless: allowHeadlessTest,
33318
+ forceHeadful: !allowHeadlessTest
33319
+ }, () => extractOnce2(attemptOptions, signal)) : await extractOnce2(attemptOptions, signal);
33269
33320
  if (attempt.error) {
33270
33321
  const err = attempt.error;
33271
33322
  const outcome2 = classifyAttemptError(err);
@@ -39483,7 +39534,7 @@ var PACKAGE_VERSION;
39483
39534
  var init_version = __esm({
39484
39535
  "src/version.ts"() {
39485
39536
  "use strict";
39486
- PACKAGE_VERSION = "0.43.3";
39537
+ PACKAGE_VERSION = "0.43.5";
39487
39538
  }
39488
39539
  });
39489
39540
 
@@ -50524,6 +50575,12 @@ var init_run_capture = __esm({
50524
50575
  // src/services/browser-agent/browser-agent-service.ts
50525
50576
  function keepHostedBrowserAlive(_browser) {
50526
50577
  }
50578
+ async function currentOrRuntimeContext(browser) {
50579
+ const current = browser.contexts()[0];
50580
+ if (current) return current;
50581
+ const runtimeUserAgent = await detectRuntimeUserAgent(browser);
50582
+ return browser.newContext(withRuntimeUserAgent({}, runtimeUserAgent));
50583
+ }
50527
50584
  function client() {
50528
50585
  const apiKey = browserServiceApiKey();
50529
50586
  if (!apiKey) throw new Error("Browser backend API key is required");
@@ -50647,7 +50704,7 @@ async function pressKeys(runtimeSessionId, keys) {
50647
50704
  async function goto(cdpWsUrl, url) {
50648
50705
  const browser = await import_playwright5.chromium.connectOverCDP(cdpWsUrl);
50649
50706
  try {
50650
- const context = browser.contexts()[0] ?? await browser.newContext();
50707
+ const context = await currentOrRuntimeContext(browser);
50651
50708
  const page = context.pages()[0] ?? await context.newPage();
50652
50709
  await page.goto(url, { waitUntil: "domcontentloaded", timeout: 45e3 });
50653
50710
  return { url: page.url(), title: await page.title() };
@@ -50658,7 +50715,7 @@ async function goto(cdpWsUrl, url) {
50658
50715
  async function captureFanout(cdpWsUrl, input) {
50659
50716
  const browser = await import_playwright5.chromium.connectOverCDP(cdpWsUrl);
50660
50717
  try {
50661
- const context = browser.contexts()[0] ?? await browser.newContext();
50718
+ const context = await currentOrRuntimeContext(browser);
50662
50719
  const page = context.pages()[0] ?? await context.newPage();
50663
50720
  return await runFanoutCapture(page, input);
50664
50721
  } finally {
@@ -50668,7 +50725,7 @@ async function captureFanout(cdpWsUrl, input) {
50668
50725
  async function readPage(cdpWsUrl) {
50669
50726
  const browser = await import_playwright5.chromium.connectOverCDP(cdpWsUrl);
50670
50727
  try {
50671
- const context = browser.contexts()[0] ?? await browser.newContext();
50728
+ const context = await currentOrRuntimeContext(browser);
50672
50729
  const page = context.pages()[0] ?? await context.newPage();
50673
50730
  const url = page.url();
50674
50731
  const title = await page.title().catch(() => "");
@@ -50723,7 +50780,7 @@ async function readPage(cdpWsUrl) {
50723
50780
  async function locatePageTargets(cdpWsUrl, targets) {
50724
50781
  const browser = await import_playwright5.chromium.connectOverCDP(cdpWsUrl);
50725
50782
  try {
50726
- const context = browser.contexts()[0] ?? await browser.newContext();
50783
+ const context = await currentOrRuntimeContext(browser);
50727
50784
  const page = context.pages()[0] ?? await context.newPage();
50728
50785
  const url = page.url();
50729
50786
  const title = await page.title().catch(() => "");
@@ -50893,6 +50950,7 @@ var init_browser_agent_service = __esm({
50893
50950
  import_playwright5 = require("playwright");
50894
50951
  init_browser_service_env();
50895
50952
  init_run_capture();
50953
+ init_browser_user_agent();
50896
50954
  DEFAULT_TIMEOUT_SECONDS = 600;
50897
50955
  }
50898
50956
  });
@@ -52055,6 +52113,22 @@ function isScraperResource(resource) {
52055
52113
  if (!resource) return false;
52056
52114
  return resource.replace(/\/$/, "") === SCRAPER_RESOURCE();
52057
52115
  }
52116
+ async function withOAuthDatabaseDeadline(stage, operation, timeoutMs = OAUTH_DATABASE_TIMEOUT_MS) {
52117
+ const startedAt = Date.now();
52118
+ let timeout;
52119
+ try {
52120
+ const value = await Promise.race([
52121
+ operation,
52122
+ new Promise((_, reject) => {
52123
+ timeout = setTimeout(() => reject(new OAuthDatabaseUnavailableError(stage)), timeoutMs);
52124
+ })
52125
+ ]);
52126
+ console.log("[oauth-db] stage=%s status=ok duration_ms=%d", stage, Date.now() - startedAt);
52127
+ return value;
52128
+ } finally {
52129
+ if (timeout) clearTimeout(timeout);
52130
+ }
52131
+ }
52058
52132
  async function getKeys() {
52059
52133
  if (cachedKeys) return cachedKeys;
52060
52134
  const raw = process.env.OAUTH_SIGNING_KEY;
@@ -52108,7 +52182,7 @@ async function sessionUser(token4) {
52108
52182
  if (!token4) return null;
52109
52183
  const userId = verifySession(token4);
52110
52184
  if (!userId) return null;
52111
- const user = await getUserById(userId);
52185
+ const user = await withOAuthDatabaseDeadline("session-user", getUserById(userId));
52112
52186
  return user ?? null;
52113
52187
  }
52114
52188
  function escapeHtml2(value) {
@@ -52233,7 +52307,7 @@ function parseAuthParams(get) {
52233
52307
  async function validateAuthRequest(p) {
52234
52308
  if (p.response_type !== "code") return { ok: false, status: 400, message: "response_type must be code" };
52235
52309
  if (!p.client_id) return { ok: false, status: 400, message: "client_id is required" };
52236
- const client2 = await getClient(p.client_id);
52310
+ const client2 = await withOAuthDatabaseDeadline("get-client", getClient(p.client_id));
52237
52311
  if (!client2) return { ok: false, status: 400, message: "unknown client_id" };
52238
52312
  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" };
52239
52313
  if (!isAllowedRedirect(p.redirect_uri)) return { ok: false, status: 400, message: "redirect_uri host not allowed" };
@@ -52252,7 +52326,7 @@ async function mintAccessToken(identity, scope, plan, audience) {
52252
52326
  function tokenErrorResponse(c, error, description, status) {
52253
52327
  return c.json({ error, error_description: description }, status);
52254
52328
  }
52255
- var import_hono23, import_cookie, import_node_crypto28, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
52329
+ var import_hono23, import_cookie, import_node_crypto28, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, OAUTH_DATABASE_TIMEOUT_MS, OAuthDatabaseUnavailableError, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
52256
52330
  var init_oauth_routes = __esm({
52257
52331
  "src/api/oauth-routes.ts"() {
52258
52332
  "use strict";
@@ -52274,6 +52348,15 @@ var init_oauth_routes = __esm({
52274
52348
  REFRESH_TTL_SECONDS = 60 * 60 * 24 * 30;
52275
52349
  CODE_TTL_SECONDS = 60;
52276
52350
  ROTATION_GRACE_SECONDS = 120;
52351
+ OAUTH_DATABASE_TIMEOUT_MS = 5e3;
52352
+ OAuthDatabaseUnavailableError = class extends Error {
52353
+ stage;
52354
+ constructor(stage) {
52355
+ super("The authorization service database is temporarily unavailable.");
52356
+ this.name = "OAuthDatabaseUnavailableError";
52357
+ this.stage = stage;
52358
+ }
52359
+ };
52277
52360
  secureCookies = process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
52278
52361
  sessionCookieOptions = {
52279
52362
  httpOnly: true,
@@ -52284,6 +52367,18 @@ var init_oauth_routes = __esm({
52284
52367
  };
52285
52368
  cachedKeys = null;
52286
52369
  oauthApp = new import_hono23.Hono();
52370
+ oauthApp.onError((error, c) => {
52371
+ if (error instanceof OAuthDatabaseUnavailableError) {
52372
+ console.error("[oauth-db] stage=%s status=timeout timeout_ms=%d", error.stage, OAUTH_DATABASE_TIMEOUT_MS);
52373
+ c.header("Retry-After", "5");
52374
+ return c.json({
52375
+ error: "temporarily_unavailable",
52376
+ error_description: "Authorization is temporarily unavailable. Please retry shortly."
52377
+ }, 503);
52378
+ }
52379
+ console.error("[oauth] unhandled error:", error);
52380
+ return c.json({ error: "server_error", error_description: "Authorization could not be completed." }, 500);
52381
+ });
52287
52382
  oauthApp.use("*", async (c, next) => {
52288
52383
  c.header("Access-Control-Allow-Origin", "*");
52289
52384
  c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
@@ -52345,7 +52440,7 @@ var init_oauth_routes = __esm({
52345
52440
  }
52346
52441
  const clientName = typeof body.client_name === "string" ? body.client_name : null;
52347
52442
  const clientId = `client_${(0, import_node_crypto28.randomBytes)(16).toString("hex")}`;
52348
- await registerClient(clientId, redirectUris, clientName);
52443
+ await withOAuthDatabaseDeadline("register-client", registerClient(clientId, redirectUris, clientName));
52349
52444
  console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
52350
52445
  return c.json({
52351
52446
  client_id: clientId,
@@ -52383,7 +52478,7 @@ var init_oauth_routes = __esm({
52383
52478
  if (action === "login") {
52384
52479
  const email = (get("email") ?? "").trim().toLowerCase();
52385
52480
  const password = get("password") ?? "";
52386
- const user2 = email ? await getUserByEmail(email) : void 0;
52481
+ const user2 = email ? await withOAuthDatabaseDeadline("login-user", getUserByEmail(email)) : void 0;
52387
52482
  const okPw = !!(user2 && user2.password_hash && verifyPassword(password, user2.password_hash));
52388
52483
  console.log("[oauth-dcr] LOGIN email=%s found=%s hasHash=%s pwLen=%d ok=%s", email, !!user2, !!(user2 && user2.password_hash), password.length, okPw);
52389
52484
  if (!okPw) {
@@ -52399,7 +52494,7 @@ var init_oauth_routes = __esm({
52399
52494
  const scope = negotiateScope(p.scope, user, p.resource);
52400
52495
  const code = `code_${(0, import_node_crypto28.randomBytes)(32).toString("base64url")}`;
52401
52496
  const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
52402
- await putCode({
52497
+ await withOAuthDatabaseDeadline("put-authorization-code", putCode({
52403
52498
  code,
52404
52499
  client_id: p.client_id,
52405
52500
  redirect_uri: p.redirect_uri,
@@ -52408,7 +52503,7 @@ var init_oauth_routes = __esm({
52408
52503
  scope,
52409
52504
  resource: p.resource || RESOURCE(),
52410
52505
  expires_at: expiresAt
52411
- });
52506
+ }));
52412
52507
  const u = new URL(p.redirect_uri);
52413
52508
  u.searchParams.set("code", code);
52414
52509
  if (p.state) u.searchParams.set("state", p.state);
@@ -52426,18 +52521,18 @@ var init_oauth_routes = __esm({
52426
52521
  const clientId = get("client_id") ?? "";
52427
52522
  const redirectUri = get("redirect_uri") ?? "";
52428
52523
  const verifier = get("code_verifier") ?? "";
52429
- const record = await consumeCode(code);
52524
+ const record = await withOAuthDatabaseDeadline("consume-authorization-code", consumeCode(code));
52430
52525
  if (!record) return tokenErrorResponse(c, "invalid_grant", "Authorization code is invalid or already used", 400);
52431
52526
  if (new Date(record.expires_at).getTime() < Date.now()) return tokenErrorResponse(c, "invalid_grant", "Authorization code has expired", 400);
52432
52527
  if (record.client_id !== clientId) return tokenErrorResponse(c, "invalid_grant", "client_id mismatch", 400);
52433
52528
  if (record.redirect_uri !== redirectUri) return tokenErrorResponse(c, "invalid_grant", "redirect_uri mismatch", 400);
52434
52529
  if (!pkceMatches(verifier, record.code_challenge)) return tokenErrorResponse(c, "invalid_grant", "PKCE verification failed", 400);
52435
- const user = await getUserByEmail(record.identity);
52530
+ const user = await withOAuthDatabaseDeadline("token-user", getUserByEmail(record.identity));
52436
52531
  const plan = user ? resolvePlan(user) : "free";
52437
52532
  const audience = record.resource ?? RESOURCE();
52438
52533
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
52439
52534
  const refreshToken = `rt_${(0, import_node_crypto28.randomBytes)(40).toString("base64url")}`;
52440
- await putRefresh({
52535
+ await withOAuthDatabaseDeadline("put-refresh-token", putRefresh({
52441
52536
  refresh_token: refreshToken,
52442
52537
  client_id: clientId,
52443
52538
  identity: record.identity,
@@ -52445,15 +52540,15 @@ var init_oauth_routes = __esm({
52445
52540
  resource: audience,
52446
52541
  expires_at: new Date(Date.now() + REFRESH_TTL_SECONDS * 1e3).toISOString(),
52447
52542
  revoked: 0
52448
- });
52543
+ }));
52449
52544
  return c.json({ access_token: accessToken, token_type: "Bearer", expires_in: ACCESS_TTL_SECONDS, refresh_token: refreshToken, scope: record.scope });
52450
52545
  }
52451
52546
  if (grantType === "refresh_token") {
52452
52547
  const refreshToken = get("refresh_token") ?? "";
52453
52548
  const clientId = get("client_id") ?? "";
52454
- const record = await getRefresh(refreshToken);
52549
+ const record = await withOAuthDatabaseDeadline("get-refresh-token", getRefresh(refreshToken));
52455
52550
  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)) {
52456
- const graceUser = await getUserByEmail(record.identity);
52551
+ const graceUser = await withOAuthDatabaseDeadline("refresh-grace-user", getUserByEmail(record.identity));
52457
52552
  const gracePlan = graceUser ? resolvePlan(graceUser) : "free";
52458
52553
  const graceAudience = record.resource ?? RESOURCE();
52459
52554
  const graceAccess = await mintAccessToken(record.identity, record.scope, gracePlan, graceAudience);
@@ -52462,12 +52557,12 @@ var init_oauth_routes = __esm({
52462
52557
  if (!record || record.revoked) return tokenErrorResponse(c, "invalid_grant", "Refresh token is invalid or revoked", 400);
52463
52558
  if (new Date(record.expires_at).getTime() < Date.now()) return tokenErrorResponse(c, "invalid_grant", "Refresh token has expired", 400);
52464
52559
  if (clientId && record.client_id !== clientId) return tokenErrorResponse(c, "invalid_grant", "client_id mismatch", 400);
52465
- const user = await getUserByEmail(record.identity);
52560
+ const user = await withOAuthDatabaseDeadline("refresh-user", getUserByEmail(record.identity));
52466
52561
  const plan = user ? resolvePlan(user) : "free";
52467
52562
  const audience = record.resource ?? RESOURCE();
52468
52563
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
52469
52564
  const nextRefresh = `rt_${(0, import_node_crypto28.randomBytes)(40).toString("base64url")}`;
52470
- await rotateRefresh(refreshToken, {
52565
+ await withOAuthDatabaseDeadline("rotate-refresh-token", rotateRefresh(refreshToken, {
52471
52566
  refresh_token: nextRefresh,
52472
52567
  client_id: record.client_id,
52473
52568
  identity: record.identity,
@@ -52475,7 +52570,7 @@ var init_oauth_routes = __esm({
52475
52570
  resource: audience,
52476
52571
  expires_at: new Date(Date.now() + REFRESH_TTL_SECONDS * 1e3).toISOString(),
52477
52572
  revoked: 0
52478
- });
52573
+ }));
52479
52574
  return c.json({ access_token: accessToken, token_type: "Bearer", expires_in: ACCESS_TTL_SECONDS, refresh_token: nextRefresh, scope: record.scope });
52480
52575
  }
52481
52576
  return tokenErrorResponse(c, "unsupported_grant_type", "grant_type must be authorization_code or refresh_token", 400);
@@ -61698,8 +61793,7 @@ var init_server = __esm({
61698
61793
  const op = opForCostPath(new URL(c.req.url).pathname);
61699
61794
  if (!op) return next();
61700
61795
  const probeRunId = c.req.header("x-cost-probe-id") ?? null;
61701
- const forceHeadless = c.req.header("x-kernel-headless") === "true";
61702
- return runWithCostContext({ op, probeRunId, forceHeadless }, () => next());
61796
+ return runWithCostContext({ op, probeRunId }, () => next());
61703
61797
  });
61704
61798
  app.post("/auth/register", requireAllowedOrigin, async (c) => {
61705
61799
  const { email, password } = await c.req.json();