mcp-scraper 0.49.0 → 0.51.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 (59) hide show
  1. package/README.md +11 -3
  2. package/dist/{analytics-repository-WWXVU3VW.js → analytics-repository-IKE53S3E.js} +10 -2
  3. package/dist/bin/api-server.cjs +840 -177
  4. package/dist/bin/api-server.cjs.map +1 -1
  5. package/dist/bin/api-server.js +4 -4
  6. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  7. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  8. package/dist/bin/mcp-scraper-cli.js +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs +1 -1
  10. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  11. package/dist/bin/mcp-scraper-install.js +1 -1
  12. package/dist/bin/mcp-stdio-server.cjs +141 -35
  13. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  14. package/dist/bin/mcp-stdio-server.js +4 -4
  15. package/dist/bin/paa-harvest.cjs.map +1 -1
  16. package/dist/bin/paa-harvest.js +3 -3
  17. package/dist/{chunk-MFNGUM4L.js → chunk-2XTYLJQK.js} +15 -1
  18. package/dist/chunk-2XTYLJQK.js.map +1 -0
  19. package/dist/{chunk-YW2LXLDU.js → chunk-5BEPOFKG.js} +160 -15
  20. package/dist/chunk-5BEPOFKG.js.map +1 -0
  21. package/dist/chunk-73MUQSWC.js +7 -0
  22. package/dist/chunk-73MUQSWC.js.map +1 -0
  23. package/dist/{chunk-MFE6RMIA.js → chunk-GOZIG6HD.js} +2 -2
  24. package/dist/{chunk-R66PJOZW.js → chunk-NNZXZTJ2.js} +2 -2
  25. package/dist/{chunk-64WBUDPC.js → chunk-O63VVCCE.js} +143 -37
  26. package/dist/chunk-O63VVCCE.js.map +1 -0
  27. package/dist/{chunk-6DPIE262.js → chunk-QHVFKFME.js} +2 -2
  28. package/dist/{chunk-PODASLGT.js → chunk-QXAY44SA.js} +2 -2
  29. package/dist/{chunk-ZEMHL36J.js → chunk-YMSZO62N.js} +2 -2
  30. package/dist/{db-A4YUPV4Z.js → db-UPSS5BGM.js} +2 -2
  31. package/dist/editorial-reading-room/assets/app.js +62 -0
  32. package/dist/editorial-reading-room/assets/index.html +8 -0
  33. package/dist/editorial-reading-room/assets/styles.css +39 -0
  34. package/dist/{extract-bundle-2HONVC5T.js → extract-bundle-RCTNANCH.js} +3 -3
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.js +3 -3
  37. package/dist/{location-data-repository-YWFJGNIP.js → location-data-repository-UUSHH5M2.js} +3 -3
  38. package/dist/{server-JE7SLZLJ.js → server-IGIIEHXY.js} +518 -135
  39. package/dist/server-IGIIEHXY.js.map +1 -0
  40. package/dist/{site-extract-repository-STNOI5OH.js → site-extract-repository-3VQAALNR.js} +3 -3
  41. package/dist/{worker-4MHZS3OB.js → worker-KQN673JF.js} +5 -5
  42. package/package.json +1 -1
  43. package/dist/chunk-64WBUDPC.js.map +0 -1
  44. package/dist/chunk-MFNGUM4L.js.map +0 -1
  45. package/dist/chunk-XLEDHV3J.js +0 -7
  46. package/dist/chunk-XLEDHV3J.js.map +0 -1
  47. package/dist/chunk-YW2LXLDU.js.map +0 -1
  48. package/dist/server-JE7SLZLJ.js.map +0 -1
  49. /package/dist/{analytics-repository-WWXVU3VW.js.map → analytics-repository-IKE53S3E.js.map} +0 -0
  50. /package/dist/{chunk-MFE6RMIA.js.map → chunk-GOZIG6HD.js.map} +0 -0
  51. /package/dist/{chunk-R66PJOZW.js.map → chunk-NNZXZTJ2.js.map} +0 -0
  52. /package/dist/{chunk-6DPIE262.js.map → chunk-QHVFKFME.js.map} +0 -0
  53. /package/dist/{chunk-PODASLGT.js.map → chunk-QXAY44SA.js.map} +0 -0
  54. /package/dist/{chunk-ZEMHL36J.js.map → chunk-YMSZO62N.js.map} +0 -0
  55. /package/dist/{db-A4YUPV4Z.js.map → db-UPSS5BGM.js.map} +0 -0
  56. /package/dist/{extract-bundle-2HONVC5T.js.map → extract-bundle-RCTNANCH.js.map} +0 -0
  57. /package/dist/{location-data-repository-YWFJGNIP.js.map → location-data-repository-UUSHH5M2.js.map} +0 -0
  58. /package/dist/{site-extract-repository-STNOI5OH.js.map → site-extract-repository-3VQAALNR.js.map} +0 -0
  59. /package/dist/{worker-4MHZS3OB.js.map → worker-KQN673JF.js.map} +0 -0
@@ -10,9 +10,11 @@ import {
10
10
  importHostedCensusPlacesCsv,
11
11
  importHostedZipGroupsCsv,
12
12
  listActiveHostedLocationDatasets
13
- } from "./chunk-R66PJOZW.js";
13
+ } from "./chunk-NNZXZTJ2.js";
14
14
  import {
15
+ ANALYTICS_CONTENT_SORTS,
15
16
  AnalyticsRepositoryError,
17
+ MAX_ENGAGED_MS,
16
18
  analyticsAcquisition,
17
19
  analyticsBusinessMetrics,
18
20
  analyticsChannelBreakdown,
@@ -64,14 +66,14 @@ import {
64
66
  updateAnalyticsPixel,
65
67
  upsertAnalyticsAdSpend,
66
68
  verifySession
67
- } from "./chunk-YW2LXLDU.js";
69
+ } from "./chunk-5BEPOFKG.js";
68
70
  import {
69
71
  classifyHarvestProblem,
70
72
  createHarvestAttemptRecorder,
71
73
  deliverWebhook,
72
74
  harvestProblemResponse,
73
75
  serializeHarvestProblem
74
- } from "./chunk-ZEMHL36J.js";
76
+ } from "./chunk-YMSZO62N.js";
75
77
  import {
76
78
  AIModeSelectors,
77
79
  AIOverviewSelectors,
@@ -98,7 +100,7 @@ import {
98
100
  resolveKernelProxyId,
99
101
  serpArrivalUrl,
100
102
  withRuntimeUserAgent
101
- } from "./chunk-MFE6RMIA.js";
103
+ } from "./chunk-GOZIG6HD.js";
102
104
  import {
103
105
  ApiErrorResponseSchema,
104
106
  ArtifactTemplatePresetKeySchema,
@@ -156,7 +158,7 @@ import {
156
158
  resolveDeploymentProfile,
157
159
  resolveLocalSourcebookSchemaType,
158
160
  transcribeMediaUrl
159
- } from "./chunk-64WBUDPC.js";
161
+ } from "./chunk-O63VVCCE.js";
160
162
  import {
161
163
  auditImageUrls,
162
164
  auditImages,
@@ -184,7 +186,7 @@ import {
184
186
  recordKernelSession,
185
187
  recordVendorUsage,
186
188
  runWithCostContext
187
- } from "./chunk-PODASLGT.js";
189
+ } from "./chunk-QXAY44SA.js";
188
190
  import {
189
191
  listWorkflowDefinitions,
190
192
  runWorkflow,
@@ -212,7 +214,7 @@ import {
212
214
  } from "./chunk-3ZUBQQPQ.js";
213
215
  import {
214
216
  PACKAGE_VERSION
215
- } from "./chunk-XLEDHV3J.js";
217
+ } from "./chunk-73MUQSWC.js";
216
218
  import {
217
219
  abandonExtractSettlement,
218
220
  countSuccessfulPages,
@@ -235,7 +237,7 @@ import {
235
237
  setExtractJobTotal,
236
238
  settleExtractJob,
237
239
  terminalExtractJobStatus
238
- } from "./chunk-6DPIE262.js";
240
+ } from "./chunk-QHVFKFME.js";
239
241
  import {
240
242
  BROWSER_OPEN_MIN_BALANCE_MC,
241
243
  CONCURRENCY_PRICE_ID,
@@ -392,7 +394,7 @@ import {
392
394
  startHarvestAttempt,
393
395
  upsertPageSnapshot,
394
396
  verifyPassword
395
- } from "./chunk-MFNGUM4L.js";
397
+ } from "./chunk-2XTYLJQK.js";
396
398
 
397
399
  // src/blog/registry.ts
398
400
  var posts = [
@@ -8738,7 +8740,7 @@ var siteExtractFn = inngest.createFunction(
8738
8740
  });
8739
8741
  }) : null;
8740
8742
  const finalized = await step.run("finalize", async () => {
8741
- const { assembleExtractArtifacts } = await import("./extract-bundle-2HONVC5T.js");
8743
+ const { assembleExtractArtifacts } = await import("./extract-bundle-RCTNANCH.js");
8742
8744
  const current = await getExtractJob(jobId2);
8743
8745
  if (!current) throw new Error("extract job disappeared before finalization");
8744
8746
  const stored = await assembleExtractArtifacts(current, { branding, imageAudit });
@@ -9680,7 +9682,7 @@ async function resolveUsZipsPath(requestedPath) {
9680
9682
  async function loadZipGroups(stateAbbr, requestedPath, warnings, sourceMode) {
9681
9683
  const mode = resolvedLocationDataMode({ zipSource: sourceMode });
9682
9684
  if (mode === "hosted") {
9683
- const { getHostedZipGroups } = await import("./location-data-repository-YWFJGNIP.js");
9685
+ const { getHostedZipGroups } = await import("./location-data-repository-UUSHH5M2.js");
9684
9686
  const hosted = await getHostedZipGroups(stateAbbr);
9685
9687
  if (!hosted.dataset) {
9686
9688
  throw new DirectoryLocationError(
@@ -9743,7 +9745,7 @@ async function resolveDirectoryMarkets(options, config = {}) {
9743
9745
  const sourceUrl = censusStateUrl(state.fips);
9744
9746
  const warnings = [];
9745
9747
  if (resolvedLocationDataMode(config) === "hosted") {
9746
- const { queryHostedLocationMarkets } = await import("./location-data-repository-YWFJGNIP.js");
9748
+ const { queryHostedLocationMarkets } = await import("./location-data-repository-UUSHH5M2.js");
9747
9749
  const hosted = await queryHostedLocationMarkets({
9748
9750
  state: state.abbr,
9749
9751
  minPopulation: options.minPopulation,
@@ -10732,7 +10734,6 @@ import { randomUUID as randomUUID2 } from "crypto";
10732
10734
  var DEFAULT_LOCK_TTL_SECONDS = 15 * 60;
10733
10735
  var DEFAULT_RETRY_AFTER_SECONDS = 30;
10734
10736
  var MAX_LOCK_TTL_SECONDS = 24 * 60 * 60;
10735
- var schemaReady = null;
10736
10737
  function concurrencyLimitForUser(user) {
10737
10738
  const tier = Math.max(0, Number(user.subscription_concurrency ?? 0));
10738
10739
  return Math.max(1, tier) + Math.max(0, Number(user.extra_concurrency_slots ?? 0));
@@ -10770,40 +10771,15 @@ function safeMetadata(metadata) {
10770
10771
  return null;
10771
10772
  }
10772
10773
  }
10773
- async function ensureConcurrencyGateSchema() {
10774
- if (!schemaReady) {
10775
- schemaReady = (async () => {
10776
- const db = getDb();
10777
- await db.execute(`
10778
- CREATE TABLE IF NOT EXISTS concurrency_locks (
10779
- id TEXT PRIMARY KEY,
10780
- user_id INTEGER NOT NULL REFERENCES users(id),
10781
- operation TEXT NOT NULL,
10782
- status TEXT NOT NULL DEFAULT 'active',
10783
- acquired_at TEXT NOT NULL DEFAULT (datetime('now')),
10784
- expires_at TEXT NOT NULL,
10785
- released_at TEXT,
10786
- metadata TEXT
10787
- )
10788
- `);
10789
- await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_user_status ON concurrency_locks(user_id, status, expires_at)`);
10790
- await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_operation ON concurrency_locks(operation, status, expires_at)`);
10791
- })();
10792
- }
10793
- return schemaReady;
10794
- }
10795
10774
  async function expireConcurrencyLocksForUser(userId) {
10796
- await ensureConcurrencyGateSchema();
10797
10775
  await getDb().execute({
10798
- sql: `UPDATE concurrency_locks
10799
- SET status = 'expired', released_at = datetime('now')
10776
+ sql: `DELETE FROM concurrency_locks
10800
10777
  WHERE user_id = ? AND status = 'active' AND expires_at <= datetime('now')`,
10801
10778
  args: [userId]
10802
10779
  });
10803
10780
  }
10804
- async function countActiveUsageForUser(userId) {
10805
- await ensureConcurrencyGateSchema();
10806
- await expireConcurrencyLocksForUser(userId);
10781
+ async function countActiveUsageForUser(userId, alreadyExpired = false) {
10782
+ if (!alreadyExpired) await expireConcurrencyLocksForUser(userId);
10807
10783
  const res = await getDb().execute({
10808
10784
  sql: `SELECT
10809
10785
  (
@@ -10823,7 +10799,6 @@ async function countActiveUsageForUser(userId) {
10823
10799
  }
10824
10800
  async function reuseExistingConcurrencyGate(userId, lockId, ttlSeconds) {
10825
10801
  if (!/^cl_[a-f0-9]{24,32}$/.test(lockId)) return false;
10826
- await ensureConcurrencyGateSchema();
10827
10802
  await expireConcurrencyLocksForUser(userId);
10828
10803
  const res = await getDb().execute({
10829
10804
  sql: `UPDATE concurrency_locks
@@ -10837,13 +10812,12 @@ async function reuseExistingConcurrencyGate(userId, lockId, ttlSeconds) {
10837
10812
  return Number(res.rowsAffected ?? 0) > 0;
10838
10813
  }
10839
10814
  async function acquireConcurrencyGate(user, operation, options = {}) {
10840
- await ensureConcurrencyGateSchema();
10841
10815
  const limit = concurrencyLimitForUser(user);
10842
10816
  if (isConcurrencyLimitBypassed(user)) {
10843
10817
  return { ok: true, lockId: null, active: 0, limit, operation, bypassed: true };
10844
10818
  }
10845
10819
  if (options.reuseLockId && await reuseExistingConcurrencyGate(user.id, options.reuseLockId, options.ttlSeconds)) {
10846
- return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id), limit, operation, reused: true };
10820
+ return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id, true), limit, operation, reused: true };
10847
10821
  }
10848
10822
  await expireConcurrencyLocksForUser(user.id);
10849
10823
  const lockId = `cl_${randomUUID2().replace(/-/g, "").slice(0, 24)}`;
@@ -10874,7 +10848,7 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
10874
10848
  limit
10875
10849
  ]
10876
10850
  });
10877
- const active = await countActiveUsageForUser(user.id);
10851
+ const active = await countActiveUsageForUser(user.id, true);
10878
10852
  if (Number(res.rowsAffected ?? 0) > 0) {
10879
10853
  return { ok: true, lockId, active, limit, operation };
10880
10854
  }
@@ -10882,17 +10856,13 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
10882
10856
  }
10883
10857
  async function releaseConcurrencyGate(lockId) {
10884
10858
  if (!lockId) return;
10885
- await ensureConcurrencyGateSchema();
10886
10859
  await getDb().execute({
10887
- sql: `UPDATE concurrency_locks
10888
- SET status = 'released', released_at = datetime('now')
10889
- WHERE id = ? AND status = 'active'`,
10860
+ sql: `DELETE FROM concurrency_locks WHERE id = ?`,
10890
10861
  args: [lockId]
10891
10862
  });
10892
10863
  }
10893
10864
  async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECONDS) {
10894
10865
  if (!lockId) return;
10895
- await ensureConcurrencyGateSchema();
10896
10866
  await getDb().execute({
10897
10867
  sql: `UPDATE concurrency_locks
10898
10868
  SET expires_at = datetime('now', ?)
@@ -14709,7 +14679,7 @@ async function recoverSettlement(row, user, authorization, duplicate) {
14709
14679
  }
14710
14680
  }
14711
14681
  await getDb().execute({
14712
- sql: "UPDATE billing_events SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status = 'settling'",
14682
+ sql: "UPDATE billing_events SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status IN ('settling', 'cost_pending')",
14713
14683
  args: [row.id]
14714
14684
  });
14715
14685
  await getDb().execute({
@@ -14754,11 +14724,13 @@ async function settleScheduledRun(args) {
14754
14724
  const rawCostNanos = rawCostUsd == null ? null : Math.round(rawCostUsd * USD_NANOS);
14755
14725
  const modelMc = rawCostUsd == null ? 0 : vendorCostUsdToMc(rawCostUsd, SCHEDULED_AGENT_LLM_MARKUP_BPS);
14756
14726
  const maximumModelMc = Math.max(0, Number(authorization.maximum_amount_mc) - SCHEDULED_RUN_BASE_MC);
14757
- const pendingReason = args.modelCostStatus === "missing" ? "openrouter_cost_missing" : modelMc > maximumModelMc ? "max_credits_per_run_exceeded" : null;
14727
+ const modelCostUnreported = args.modelCostStatus === "missing";
14728
+ const pendingReason = modelMc > maximumModelMc ? "max_credits_per_run_exceeded" : null;
14758
14729
  const metadata = safeMetadata2({
14759
14730
  ...args.metadata,
14760
14731
  billingPhase: "settlement",
14761
14732
  chargeMode: "model_delta",
14733
+ ...modelCostUnreported ? { modelCostUnreported: true } : {},
14762
14734
  ...pendingReason ? { reason: pendingReason } : {}
14763
14735
  });
14764
14736
  const eventId = existing?.id ?? randomUUID4();
@@ -14802,6 +14774,72 @@ async function settleScheduledRun(args) {
14802
14774
  }
14803
14775
  return recoverSettlement(existing, user, authorization, duplicateClaim);
14804
14776
  }
14777
+ async function reconcileScheduledRunSettlements(limit = 25, minimumAgeHours = 24) {
14778
+ const stale = await getDb().execute({
14779
+ sql: `SELECT id, authorization_id, status, amount_mc, raw_cost_usd_nanos, multiplier_bps, metadata
14780
+ FROM billing_events
14781
+ WHERE billing_class = ? AND status = 'cost_pending'
14782
+ AND created_at < datetime('now', ?)
14783
+ ORDER BY created_at
14784
+ LIMIT ?`,
14785
+ args: [SCHEDULED_RUN_BILLING_CLASS, `-${Math.max(1, Math.trunc(minimumAgeHours))} hours`, Math.max(1, Math.trunc(limit))]
14786
+ });
14787
+ const result = { checked: stale.rows.length, settled: 0, chargedMc: 0, failed: 0 };
14788
+ for (const raw of stale.rows) {
14789
+ const row = raw;
14790
+ try {
14791
+ const authorization = await authorizationById(row.authorization_id);
14792
+ if (!authorization) {
14793
+ result.failed += 1;
14794
+ continue;
14795
+ }
14796
+ const cap = Math.max(0, Number(authorization.maximum_amount_mc) - SCHEDULED_RUN_BASE_MC);
14797
+ const chargeMc = Math.min(Math.max(0, Math.round(Number(row.amount_mc))), cap);
14798
+ if (chargeMc > 0) {
14799
+ const debit = await debitMcIdempotent(
14800
+ Number(authorization.user_id),
14801
+ chargeMc,
14802
+ LedgerOperation.SCHEDULED_RUN,
14803
+ "scheduled-run:" + authorization.id + ":model:reconciled",
14804
+ authorization.id + ":model"
14805
+ );
14806
+ if (!debit.ok) {
14807
+ result.failed += 1;
14808
+ continue;
14809
+ }
14810
+ }
14811
+ await getDb().execute({
14812
+ sql: `UPDATE billing_events
14813
+ SET status = 'settled', amount_mc = ?, settled_at = datetime('now'), metadata = ?
14814
+ WHERE id = ? AND status = 'cost_pending'`,
14815
+ args: [
14816
+ chargeMc,
14817
+ safeMetadata2({ ...metadataReason(row) ? { reason: metadataReason(row) } : {}, autoReconciled: true, cappedAtAuthorizedMaximum: chargeMc < Number(row.amount_mc) }),
14818
+ row.id
14819
+ ]
14820
+ });
14821
+ await getDb().execute({
14822
+ sql: "UPDATE billing_authorizations SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status IN ('started', 'cost_pending')",
14823
+ args: [authorization.id]
14824
+ });
14825
+ result.settled += 1;
14826
+ result.chargedMc += chargeMc;
14827
+ console.error(JSON.stringify({
14828
+ event: "scheduled_run_settlement_reconciled",
14829
+ authorization_id: authorization.id,
14830
+ charged_mc: chargeMc,
14831
+ reason: metadataReason(row) ?? "unreported_cost"
14832
+ }));
14833
+ } catch (err) {
14834
+ result.failed += 1;
14835
+ console.error(JSON.stringify({
14836
+ event: "scheduled_run_reconciliation_failed",
14837
+ message: err instanceof Error ? err.message : String(err)
14838
+ }));
14839
+ }
14840
+ }
14841
+ return result;
14842
+ }
14805
14843
  async function voidScheduledRunAuthorization(args) {
14806
14844
  const user = await resolveSchedulingUser(args.identity);
14807
14845
  const authorization = await authorizationById(args.authorizationId);
@@ -15673,28 +15711,6 @@ internalInboxApp.post("/broadcast", async (c) => {
15673
15711
 
15674
15712
  // src/api/connected-account-billing.ts
15675
15713
  var DEFAULT_CONNECTED_ACCOUNT_PRICE_ID = "price_1TtYPAS8aAcsk3TGHOgrDZiQ";
15676
- async function ensureConnectedAccountBillingSchema() {
15677
- await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_billing (
15678
- user_id INTEGER PRIMARY KEY,
15679
- stripe_subscription_id TEXT,
15680
- stripe_subscription_item_id TEXT,
15681
- price_id TEXT,
15682
- quantity INTEGER NOT NULL DEFAULT 0,
15683
- status TEXT NOT NULL DEFAULT 'pending',
15684
- last_error_code TEXT,
15685
- checked_at TEXT NOT NULL DEFAULT (datetime('now')),
15686
- synced_at TEXT,
15687
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
15688
- )`);
15689
- await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_charges (
15690
- user_id INTEGER NOT NULL,
15691
- connection_id TEXT NOT NULL,
15692
- period_start TEXT NOT NULL,
15693
- amount_mc INTEGER NOT NULL,
15694
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
15695
- PRIMARY KEY (user_id, connection_id, period_start)
15696
- )`);
15697
- }
15698
15714
  function connectedBillingPeriodStart(now = /* @__PURE__ */ new Date()) {
15699
15715
  return `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
15700
15716
  }
@@ -15703,7 +15719,6 @@ function isBillableConnection(connection) {
15703
15719
  return BILLABLE_LIFECYCLE.has(String(connection.lifecycleStatus ?? ""));
15704
15720
  }
15705
15721
  async function chargedConnectionIds(userId, periodStart) {
15706
- await ensureConnectedAccountBillingSchema();
15707
15722
  const result = await getDb().execute({
15708
15723
  sql: "SELECT connection_id FROM connected_account_charges WHERE user_id = ? AND period_start = ?",
15709
15724
  args: [Number(userId), periodStart]
@@ -15711,7 +15726,6 @@ async function chargedConnectionIds(userId, periodStart) {
15711
15726
  return new Set(result.rows.map((row) => String(row.connection_id)));
15712
15727
  }
15713
15728
  async function getConnectedAccountBillingState(userId) {
15714
- await ensureConnectedAccountBillingSchema();
15715
15729
  const result = await getDb().execute({
15716
15730
  sql: "SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",
15717
15731
  args: [Number(userId)]
@@ -15719,7 +15733,6 @@ async function getConnectedAccountBillingState(userId) {
15719
15733
  return result.rows[0] ? result.rows[0] : null;
15720
15734
  }
15721
15735
  async function setConnectedAccountBillingState(input) {
15722
- await ensureConnectedAccountBillingSchema();
15723
15736
  await getDb().execute({
15724
15737
  sql: `INSERT INTO connected_account_billing
15725
15738
  (user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)
@@ -19987,6 +20000,34 @@ import { Hono as Hono11 } from "hono";
19987
20000
  import { z as z21 } from "zod";
19988
20001
 
19989
20002
  // src/api/reddit-trending.ts
20003
+ var PARSE_REDDIT_SEARCH = `(() => {
20004
+ const txt = el => ((el && el.innerText) || '').trim();
20005
+ const bodyText = (document.body && document.body.innerText) || '';
20006
+ const blocked = /whoa there|blocked by network|you've been blocked|network (policy|security)|log in to your reddit account/i.test(bodyText);
20007
+ const listing = document.querySelector('.search-result-listing');
20008
+ const rows = [];
20009
+ document.querySelectorAll('.search-result.search-result-link').forEach(r => {
20010
+ const titleEl = r.querySelector('header.search-result-header a.search-title');
20011
+ rows.push({
20012
+ title: txt(titleEl),
20013
+ url: (titleEl && titleEl.getAttribute('href')) || '',
20014
+ subreddit: txt(r.querySelector('a.search-subreddit-link')),
20015
+ scoreText: txt(r.querySelector('.search-score')),
20016
+ commentsText: txt(r.querySelector('a.search-comments')),
20017
+ ageText: txt(r.querySelector('.search-time time')),
20018
+ });
20019
+ });
20020
+ return { blocked: blocked, listingFound: Boolean(listing), rowCount: rows.length, rows: rows };
20021
+ })()`;
20022
+ function buildRedditSearchUrl(topic, subreddit, window2 = "month") {
20023
+ const base = subreddit ? `https://old.reddit.com/r/${encodeURIComponent(subreddit)}/search` : "https://old.reddit.com/search";
20024
+ const params = new URLSearchParams();
20025
+ params.set("q", topic);
20026
+ params.set("sort", "top");
20027
+ params.set("t", window2);
20028
+ if (subreddit) params.set("restrict_sr", "on");
20029
+ return `${base}?${params.toString()}`;
20030
+ }
19990
20031
  function buildSerpQuery(topic, subreddit) {
19991
20032
  const scope = subreddit ? `site:reddit.com/r/${subreddit}` : "site:reddit.com";
19992
20033
  return `${scope} ${topic}`.trim();
@@ -20049,6 +20090,32 @@ function canonicalThreadUrl(href) {
20049
20090
  function engagementScore(score, commentCount) {
20050
20091
  return score + 2 * commentCount;
20051
20092
  }
20093
+ function normalizeSearchRows(rows) {
20094
+ if (!Array.isArray(rows)) return [];
20095
+ const seen = /* @__PURE__ */ new Set();
20096
+ const out = [];
20097
+ for (const raw of rows) {
20098
+ if (!raw || typeof raw !== "object") continue;
20099
+ const r = raw;
20100
+ const url = canonicalThreadUrl(r.url);
20101
+ const title = typeof r.title === "string" ? r.title.trim() : "";
20102
+ if (!url || !title || seen.has(url)) continue;
20103
+ seen.add(url);
20104
+ const score = parseCountText(r.scoreText);
20105
+ const commentCount = parseCountText(r.commentsText);
20106
+ out.push({
20107
+ title,
20108
+ url,
20109
+ subreddit: typeof r.subreddit === "string" ? r.subreddit.trim() : "",
20110
+ score,
20111
+ commentCount,
20112
+ engagementScore: engagementScore(score, commentCount),
20113
+ ageText: typeof r.ageText === "string" ? r.ageText.trim() : "",
20114
+ topQuestions: []
20115
+ });
20116
+ }
20117
+ return out;
20118
+ }
20052
20119
  var INTERROGATIVE_STARTERS = /* @__PURE__ */ new Set([
20053
20120
  "what",
20054
20121
  "whats",
@@ -20200,8 +20267,8 @@ var PARSE_REDDIT = `(() => {
20200
20267
  });
20201
20268
  return { title: title, author: author, score: score, postBody: postBody, blocked: blocked, numComments: comments.length, comments: comments };
20202
20269
  })()`;
20203
- async function scrapeOldRedditWithRetries(url, script, accept) {
20204
- for (let attempt = 0; attempt < 4; attempt++) {
20270
+ async function scrapeOldRedditWithRetries(url, script, accept, maxAttempts = 4) {
20271
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
20205
20272
  const backupAttempt = attempt === 3;
20206
20273
  if (backupAttempt && !backupProxyAvailable()) break;
20207
20274
  const proxyId = backupAttempt ? await createBackupProxyIdSafe(browserServiceApiKey(), attempt) : await residentialProxyId(attempt);
@@ -20317,6 +20384,9 @@ redditApp.post("/trending", createApiKeyAuth(), async (c) => {
20317
20384
  if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.reddit_thread), 402);
20318
20385
  discoveryDebited = true;
20319
20386
  let candidates = [];
20387
+ let discoverySource = "google_serp";
20388
+ let primaryDiscoveryDegraded = false;
20389
+ let primaryDegradationReasons = [];
20320
20390
  try {
20321
20391
  const serp = await harvest({
20322
20392
  query: serpQuery,
@@ -20330,14 +20400,59 @@ redditApp.post("/trending", createApiKeyAuth(), async (c) => {
20330
20400
  softDeadlineMs
20331
20401
  });
20332
20402
  candidates = extractRedditThreadUrls(serp.organicResults, body.maxThreads);
20403
+ primaryDiscoveryDegraded = serp.diagnostics?.degradedResult === true;
20404
+ primaryDegradationReasons = Array.isArray(serp.diagnostics?.degradationReasons) ? serp.diagnostics.degradationReasons.filter((reason) => typeof reason === "string") : [];
20333
20405
  } catch {
20334
20406
  candidates = [];
20407
+ primaryDiscoveryDegraded = true;
20408
+ primaryDegradationReasons = ["primary_serp_failed"];
20409
+ }
20410
+ if (candidates.length === 0) {
20411
+ const fallback = await scrapeOldRedditWithRetries(
20412
+ buildRedditSearchUrl(body.topic, subreddit, body.window),
20413
+ PARSE_REDDIT_SEARCH,
20414
+ (data) => !data.blocked && data.listingFound,
20415
+ 2
20416
+ );
20417
+ if (fallback) {
20418
+ candidates = normalizeSearchRows(fallback.data.rows).slice(0, body.maxThreads).map((thread) => ({ title: thread.title, url: thread.url, subreddit: thread.subreddit.replace(/^r\//i, "") }));
20419
+ discoverySource = "reddit_search_fallback";
20420
+ } else {
20421
+ discoverySource = "none";
20422
+ }
20335
20423
  }
20336
20424
  if (candidates.length === 0) {
20337
20425
  await creditMc(user.id, MC_COSTS.reddit_thread, LedgerOperation.REDDIT_THREAD_REFUND, "no reddit threads discovered");
20338
20426
  discoveryRefunded = true;
20339
- await logRequestEvent({ userId: user.id, source: "reddit_trending", status: "failed", query: body.topic, error: "no reddit threads discovered" });
20340
- return c.json({ error: "No Reddit threads found for that topic and window (refunded)" }, 404);
20427
+ const degradedResult = discoverySource === "none";
20428
+ const degradationReasons = degradedResult ? [.../* @__PURE__ */ new Set([...primaryDegradationReasons, "reddit_fallback_empty_or_blocked"])] : [];
20429
+ const result2 = {
20430
+ topic: body.topic,
20431
+ subreddit: subreddit ?? null,
20432
+ window: body.window === "week" ? "7d" : "30d",
20433
+ totals: trendingTotals([]),
20434
+ rankedThreads: [],
20435
+ questions: [],
20436
+ threadsScraped: 0,
20437
+ candidatesFound: 0,
20438
+ partial: false,
20439
+ searchQuery: serpQuery,
20440
+ discoverySource,
20441
+ resultQuality: degradedResult ? "degraded" : "complete",
20442
+ degradedResult,
20443
+ degradationReasons,
20444
+ retryRecommended: degradedResult,
20445
+ billingRefunded: true
20446
+ };
20447
+ await logRequestEvent({
20448
+ userId: user.id,
20449
+ source: "reddit_trending",
20450
+ status: "done",
20451
+ query: body.topic,
20452
+ resultCount: 0,
20453
+ result: result2
20454
+ });
20455
+ return c.json(result2);
20341
20456
  }
20342
20457
  let ranked;
20343
20458
  if (!body.includeComments) {
@@ -20413,7 +20528,13 @@ redditApp.post("/trending", createApiKeyAuth(), async (c) => {
20413
20528
  threadsScraped: body.includeComments ? ranked.length : 0,
20414
20529
  candidatesFound: candidates.length,
20415
20530
  partial: body.includeComments && ranked.length < candidates.length,
20416
- searchQuery: serpQuery
20531
+ searchQuery: serpQuery,
20532
+ discoverySource,
20533
+ resultQuality: primaryDiscoveryDegraded ? "partial" : "complete",
20534
+ degradedResult: false,
20535
+ degradationReasons: primaryDiscoveryDegraded ? primaryDegradationReasons : [],
20536
+ retryRecommended: false,
20537
+ billingRefunded: false
20417
20538
  };
20418
20539
  await logRequestEvent({ userId: user.id, source: "reddit_trending", status: "done", query: body.topic, resultCount: ranked.length, result });
20419
20540
  return c.json(result);
@@ -22185,6 +22306,82 @@ async function reconcilePaaHarvestSettlements(limit = 25) {
22185
22306
  return { checked: jobs.length, staleFailed, settled, failed };
22186
22307
  }
22187
22308
 
22309
+ // src/api/retention-sweeps.ts
22310
+ var REPORTING_WINDOW_DAYS = 30;
22311
+ var TELEMETRY_RETENTION_DAYS = 45;
22312
+ var FAILURE_RETENTION_DAYS = 90;
22313
+ var RETENTION_RULES = [
22314
+ { table: "request_events", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
22315
+ { table: "kernel_session_log", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
22316
+ { table: "vendor_usage_log", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
22317
+ { table: "harvest_attempts", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS, timestampFormat: "iso8601" },
22318
+ { table: "browser_agent_actions", timestampColumn: "created_at", retentionDays: REPORTING_WINDOW_DAYS },
22319
+ { table: "jobs", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS, where: "status = 'done'" },
22320
+ { table: "jobs", timestampColumn: "created_at", retentionDays: FAILURE_RETENTION_DAYS, where: "status = 'failed'" }
22321
+ ];
22322
+ function cutoffFor(rule) {
22323
+ const cutoff = new Date(Date.now() - rule.retentionDays * 24 * 60 * 60 * 1e3);
22324
+ if (rule.timestampFormat === "iso8601") return cutoff.toISOString();
22325
+ return cutoff.toISOString().replace("T", " ").slice(0, 19);
22326
+ }
22327
+ var DEFAULT_BATCH = 500;
22328
+ var DEFAULT_MAX_DELETES_PER_RULE = 1e3;
22329
+ var DEFAULT_SWEEP_DEADLINE_MS = 15e3;
22330
+ async function tableExists(table) {
22331
+ const res = await getDb().execute({
22332
+ sql: "SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1",
22333
+ args: [table]
22334
+ });
22335
+ return res.rows.length > 0;
22336
+ }
22337
+ async function sweepRule(rule, maxDeletes) {
22338
+ if (!await tableExists(rule.table)) return 0;
22339
+ const filter = rule.where ? ` AND ${rule.where}` : "";
22340
+ const cutoff = cutoffFor(rule);
22341
+ let deleted = 0;
22342
+ while (deleted < maxDeletes) {
22343
+ const res = await getDb().execute({
22344
+ sql: `DELETE FROM ${rule.table} WHERE rowid IN (
22345
+ SELECT rowid FROM ${rule.table}
22346
+ WHERE ${rule.timestampColumn} < ?${filter}
22347
+ LIMIT ?)`,
22348
+ args: [cutoff, Math.min(DEFAULT_BATCH, maxDeletes - deleted)]
22349
+ });
22350
+ const n = Number(res.rowsAffected ?? 0);
22351
+ deleted += n;
22352
+ if (n === 0) break;
22353
+ }
22354
+ return deleted;
22355
+ }
22356
+ async function sweepRetention(options = {}) {
22357
+ const maxDeletes = options.maxDeletesPerRule ?? DEFAULT_MAX_DELETES_PER_RULE;
22358
+ const deadline = Date.now() + (options.deadlineMs ?? DEFAULT_SWEEP_DEADLINE_MS);
22359
+ const perTable = {};
22360
+ let deleted = 0;
22361
+ let truncated = false;
22362
+ for (const rule of RETENTION_RULES) {
22363
+ const key = rule.where ? `${rule.table}:${rule.where}` : rule.table;
22364
+ if (Date.now() >= deadline) {
22365
+ truncated = true;
22366
+ break;
22367
+ }
22368
+ try {
22369
+ const n = await sweepRule(rule, maxDeletes);
22370
+ perTable[key] = n;
22371
+ deleted += n;
22372
+ if (n >= maxDeletes) truncated = true;
22373
+ } catch (err) {
22374
+ perTable[key] = -1;
22375
+ console.error(JSON.stringify({
22376
+ event: "retention_sweep_failed",
22377
+ table: rule.table,
22378
+ message: err instanceof Error ? err.message : String(err)
22379
+ }));
22380
+ }
22381
+ }
22382
+ return { deleted, perTable, truncated };
22383
+ }
22384
+
22188
22385
  // src/api/directory-workflow-reconciliation.ts
22189
22386
  async function reconcileDirectoryWorkflowSettlements(limit = 25) {
22190
22387
  const expiredStarts = await listExpiredDirectoryWorkflowStarts(limit);
@@ -23797,6 +23994,24 @@ import { Hono as Hono21 } from "hono";
23797
23994
  import { createMcpHandler } from "@modelcontextprotocol/server";
23798
23995
 
23799
23996
  // src/mcp/memory-mcp-tool-executor.ts
23997
+ function folderInventory(result) {
23998
+ if (!Array.isArray(result.notes)) return [];
23999
+ const folders = /* @__PURE__ */ new Set();
24000
+ for (const note of result.notes) {
24001
+ if (!note || typeof note !== "object" || Array.isArray(note)) continue;
24002
+ const path5 = note.path;
24003
+ if (typeof path5 !== "string") continue;
24004
+ const segments = path5.replace(/\\/g, "/").split("/").filter(Boolean);
24005
+ for (let depth = 1; depth < segments.length; depth += 1) {
24006
+ folders.add(segments.slice(0, depth).join("/"));
24007
+ }
24008
+ }
24009
+ return [...folders].sort((a, b) => a.localeCompare(b));
24010
+ }
24011
+ function enrichMemoryResult(toolName, result) {
24012
+ if (toolName !== "listTool" || result.ok !== true) return result;
24013
+ return { ...result, folders: folderInventory(result) };
24014
+ }
23800
24015
  var MemoryMcpToolExecutor = class {
23801
24016
  baseUrl;
23802
24017
  apiKey;
@@ -23819,7 +24034,10 @@ var MemoryMcpToolExecutor = class {
23819
24034
  const message = data?.error ?? `memory ${toolName} failed (HTTP ${res.status})`;
23820
24035
  return { content: [{ type: "text", text: message }], isError: true };
23821
24036
  }
23822
- const result = data ?? { ok: false, error: `memory ${toolName} returned no result` };
24037
+ const result = enrichMemoryResult(
24038
+ toolName,
24039
+ data ?? { ok: false, error: `memory ${toolName} returned no result` }
24040
+ );
23823
24041
  return {
23824
24042
  content: [{ type: "text", text: JSON.stringify(result) }],
23825
24043
  structuredContent: result,
@@ -24894,6 +25112,7 @@ async function listSessionRows(userId, includeClosed = false) {
24894
25112
  });
24895
25113
  return res.rows;
24896
25114
  }
25115
+ var ORPHANED_SESSION_STATUS = "runtime_close_pending";
24897
25116
  async function listIdleOpenSessionRows(idleSeconds) {
24898
25117
  const db = getDb();
24899
25118
  const res = await db.execute({
@@ -24919,12 +25138,20 @@ async function setBilledMc(id, billedMc) {
24919
25138
  args: [Math.round(billedMc), id]
24920
25139
  });
24921
25140
  }
24922
- async function markSessionClosed(id) {
25141
+ async function markSessionClosed(id, runtimeClosed = true) {
24923
25142
  const db = getDb();
24924
25143
  await db.execute({
24925
- sql: `UPDATE browser_agent_sessions SET status = 'closed', closed_at = datetime('now') WHERE id = ?`,
24926
- args: [id]
25144
+ sql: `UPDATE browser_agent_sessions SET status = ?, closed_at = datetime('now') WHERE id = ?`,
25145
+ args: [runtimeClosed ? "closed" : ORPHANED_SESSION_STATUS, id]
25146
+ });
25147
+ }
25148
+ async function listOrphanedSessionRows(limit = 50) {
25149
+ const db = getDb();
25150
+ const res = await db.execute({
25151
+ sql: `SELECT * FROM browser_agent_sessions WHERE status = ? ORDER BY closed_at LIMIT ?`,
25152
+ args: [ORPHANED_SESSION_STATUS, Math.max(1, Math.min(200, Math.round(limit)))]
24927
25153
  });
25154
+ return res.rows;
24928
25155
  }
24929
25156
  async function recordAction(input) {
24930
25157
  const db = getDb();
@@ -26065,6 +26292,21 @@ async function charge(sessionId, userId, _startedAtMs) {
26065
26292
  if (res.ok) await setBilledMc(sessionId, owed);
26066
26293
  }
26067
26294
  }
26295
+ async function closeRuntimeSession(runtimeSessionId, sessionId) {
26296
+ try {
26297
+ await closeSession(runtimeSessionId);
26298
+ return true;
26299
+ } catch (err) {
26300
+ console.error(JSON.stringify({
26301
+ event: "browser_runtime_close_failed",
26302
+ session_id: sessionId,
26303
+ runtime_session_id: runtimeSessionId,
26304
+ error_name: err instanceof Error ? err.name : typeof err,
26305
+ message: sanitizeVendorName(err instanceof Error ? err.message : String(err)).slice(0, 300)
26306
+ }));
26307
+ return false;
26308
+ }
26309
+ }
26068
26310
  async function closeAndSettleSession(row) {
26069
26311
  if (row.user_id != null) {
26070
26312
  try {
@@ -26072,15 +26314,24 @@ async function closeAndSettleSession(row) {
26072
26314
  } catch {
26073
26315
  }
26074
26316
  }
26075
- try {
26076
- await closeSession(row.runtime_session_id);
26077
- } catch {
26078
- }
26079
- await markSessionClosed(row.id).catch(() => {
26317
+ const runtimeClosed = await closeRuntimeSession(row.runtime_session_id, row.id);
26318
+ await markSessionClosed(row.id, runtimeClosed).catch(() => {
26080
26319
  });
26081
26320
  await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
26082
26321
  });
26083
26322
  }
26323
+ async function retryOrphanedBrowserSessions(limit = 50) {
26324
+ const rows = await listOrphanedSessionRows(limit);
26325
+ let recovered = 0;
26326
+ for (const row of rows) {
26327
+ if (await closeRuntimeSession(row.runtime_session_id, row.id)) {
26328
+ await markSessionClosed(row.id, true).catch(() => {
26329
+ });
26330
+ recovered++;
26331
+ }
26332
+ }
26333
+ return { recovered, stillOpen: rows.length - recovered };
26334
+ }
26084
26335
  async function completeAuthConnection(connectionId) {
26085
26336
  const connection = await getAuthConnectionRow(connectionId);
26086
26337
  if (!connection) return { ok: false, error: "not found" };
@@ -26105,7 +26356,7 @@ async function reapIdleBrowserSessions(idleSeconds = 120) {
26105
26356
  function publicSession(row) {
26106
26357
  return {
26107
26358
  session_id: row.id,
26108
- status: row.status,
26359
+ status: row.status === ORPHANED_SESSION_STATUS ? "closed" : row.status,
26109
26360
  label: row.label,
26110
26361
  watch_url: `/console/${row.id}`,
26111
26362
  created_at: row.created_at,
@@ -30627,6 +30878,24 @@ function safeFilename(requested, slug2) {
30627
30878
  function escapedJsonForHtml(value) {
30628
30879
  return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
30629
30880
  }
30881
+ function socialMeta(input) {
30882
+ const image = input.site.ogImage;
30883
+ const tags = [
30884
+ '<meta property="og:type" content="website">',
30885
+ `<meta property="og:title" content="${escapeHtml5(input.site.title)}">`,
30886
+ `<meta property="og:description" content="${escapeHtml5(input.deck || input.site.heroTitle)}">`,
30887
+ '<meta name="twitter:card" content="summary_large_image">',
30888
+ `<meta name="twitter:title" content="${escapeHtml5(input.site.title)}">`,
30889
+ `<meta name="twitter:description" content="${escapeHtml5(input.deck || input.site.heroTitle)}">`,
30890
+ `<meta property="og:image" content="${image ? escapeHtml5(image.url) : ""}">`,
30891
+ `<meta property="og:image:alt" content="${image ? escapeHtml5(image.alt) : ""}">`,
30892
+ `<meta property="og:image:width" content="${image?.width ?? ""}">`,
30893
+ `<meta property="og:image:height" content="${image?.height ?? ""}">`,
30894
+ `<meta name="twitter:image" content="${image ? escapeHtml5(image.url) : ""}">`,
30895
+ `<meta name="twitter:image:alt" content="${image ? escapeHtml5(image.alt) : ""}">`
30896
+ ];
30897
+ return tags.join("\n ");
30898
+ }
30630
30899
  function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
30631
30900
  const sourceBytes = input.articles.reduce((total, article) => total + Buffer.byteLength(article.markdown), 0);
30632
30901
  if (sourceBytes > 2e6) throw new Error("Editorial reading-room Markdown must be 2,000,000 bytes or fewer in total.");
@@ -30659,7 +30928,7 @@ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
30659
30928
  const generatedAt = now.toISOString();
30660
30929
  const data = { ...input, generatedAt, articles };
30661
30930
  const template = readAsset("index.html");
30662
- const html = template.replace("<title>Editorial Reading Room</title>", `<title>${escapeHtml5(input.site.title)}</title>`).replace('content="A mobile-first editorial reading room."', `content="${escapeHtml5(input.deck || input.site.heroTitle)}"`).replace("/*__READING_ROOM_STYLES__*/", readAsset("styles.css")).replace("/*__READING_ROOM_DATA__*/", escapedJsonForHtml(data)).replace("/*__READING_ROOM_SCRIPT__*/", readAsset("app.js"));
30931
+ const html = template.replace("<title>Editorial Reading Room</title>", `<title>${escapeHtml5(input.site.title)}</title>`).replace('content="A mobile-first editorial reading room."', `content="${escapeHtml5(input.deck || input.site.heroTitle)}"`).replace("<!--__READING_ROOM_SOCIAL_META__-->", socialMeta(input)).replace("/*__READING_ROOM_STYLES__*/", readAsset("styles.css")).replace("/*__READING_ROOM_DATA__*/", escapedJsonForHtml(data)).replace("/*__READING_ROOM_SCRIPT__*/", readAsset("app.js"));
30663
30932
  const bytes = Buffer.byteLength(html);
30664
30933
  return {
30665
30934
  html,
@@ -30876,6 +31145,31 @@ import { z as z32 } from "zod";
30876
31145
  // src/api/commons-repository.ts
30877
31146
  import { createHash as createHash13, randomUUID as randomUUID15 } from "crypto";
30878
31147
 
31148
+ // src/api/schema-presence.ts
31149
+ var cache = null;
31150
+ async function loadSchemaObjects() {
31151
+ const res = await getDb().execute(
31152
+ "SELECT name FROM sqlite_master WHERE type IN ('table', 'index')"
31153
+ );
31154
+ return new Set(res.rows.map((row) => String(row.name)));
31155
+ }
31156
+ function invalidateSchemaPresence() {
31157
+ cache = null;
31158
+ }
31159
+ async function schemaObjectsPresent(names) {
31160
+ if (names.length === 0) return true;
31161
+ try {
31162
+ if (!cache) cache = loadSchemaObjects();
31163
+ const present = await cache;
31164
+ if (names.every((name) => present.has(name))) return true;
31165
+ invalidateSchemaPresence();
31166
+ return false;
31167
+ } catch {
31168
+ invalidateSchemaPresence();
31169
+ return false;
31170
+ }
31171
+ }
31172
+
30879
31173
  // src/api/commons-linksets.ts
30880
31174
  import { createHash as createHash12 } from "crypto";
30881
31175
  var COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
@@ -31202,9 +31496,38 @@ var COMMONS_ENTITY_PROFILES = {
31202
31496
  ]
31203
31497
  }
31204
31498
  };
31205
- var schemaReady2 = false;
31499
+ var schemaReady = false;
31500
+ var COMMONS_SCHEMA_OBJECTS = [
31501
+ "schema_migrations",
31502
+ "commons_entities",
31503
+ "commons_claims",
31504
+ "commons_proposals",
31505
+ "commons_ledger",
31506
+ "commons_saved_filters",
31507
+ "commons_index_documents",
31508
+ "commons_publications",
31509
+ "commons_publication_editions",
31510
+ "commons_entities_status_updated",
31511
+ "commons_entities_type_updated",
31512
+ "commons_entities_source_domain",
31513
+ "commons_claims_subject_predicate",
31514
+ "commons_claims_object_entity",
31515
+ "commons_proposals_user_created",
31516
+ "commons_proposals_entity_created",
31517
+ "commons_ledger_entity_created",
31518
+ "commons_ledger_user_created",
31519
+ "commons_saved_filters_user_updated",
31520
+ "commons_index_documents_status_updated",
31521
+ "commons_index_documents_entity_type",
31522
+ "commons_publications_updated",
31523
+ "commons_publication_editions_publication_published"
31524
+ ];
31206
31525
  async function ensureCommonsSchema() {
31207
- if (schemaReady2) return;
31526
+ if (schemaReady) return;
31527
+ if (await schemaObjectsPresent(COMMONS_SCHEMA_OBJECTS)) {
31528
+ schemaReady = true;
31529
+ return;
31530
+ }
31208
31531
  const db = getDb();
31209
31532
  await db.batch([
31210
31533
  {
@@ -31425,7 +31748,7 @@ async function ensureCommonsSchema() {
31425
31748
  args: [COMMONS_SCHEMA_VERSION]
31426
31749
  }
31427
31750
  ], "write");
31428
- schemaReady2 = true;
31751
+ schemaReady = true;
31429
31752
  }
31430
31753
  async function searchCommonsEntities(filters = {}, userId) {
31431
31754
  await ensureCommonsSchema();
@@ -34336,7 +34659,9 @@ var EventSchema = z34.object({
34336
34659
  campaign: z34.string().trim().max(240).optional(),
34337
34660
  clickIds: z34.record(z34.string(), z34.string().trim().max(500)).optional(),
34338
34661
  deviceClass: z34.enum(["desktop", "tablet", "mobile", "unknown"]).optional(),
34339
- properties: z34.record(z34.string(), z34.unknown()).optional()
34662
+ properties: z34.record(z34.string(), z34.unknown()).optional(),
34663
+ engagedMs: z34.number().int().min(0).max(MAX_ENGAGED_MS).optional(),
34664
+ scrollDepth: z34.number().int().min(0).max(100).optional()
34340
34665
  }).strict();
34341
34666
  var IngestionSchema = z34.object({
34342
34667
  pixelId: z34.string().trim().min(8).max(160),
@@ -34495,7 +34820,10 @@ analyticsApp.use("*", async (c, next) => {
34495
34820
  await next();
34496
34821
  });
34497
34822
  function analyticsPixelSdk() {
34498
- return `(()=>{const s=document.currentScript;if(!s)return;const pixel=s.dataset.pixel;if(!pixel)return;const endpoint=new URL('/analytics/v1/events',s.src).toString(),ttl=18e5;const key='mcp_analytics_visitor',sessionKey='mcp_analytics_session',touchKey='mcp_analytics_touch';const id=()=>crypto.randomUUID?crypto.randomUUID():'e_'+Date.now().toString(36)+Math.random().toString(36).slice(2);let consent=s.dataset.consent==='granted'&&!navigator.globalPrivacyControl,visitor=null,session=null;const read=k=>{try{return localStorage.getItem(k)}catch{return null}},write=(k,v)=>{try{localStorage.setItem(k,v)}catch{}};const ensure=()=>{if(!consent)return;visitor=read(key)||id();const now=Date.now();let saved={};try{saved=JSON.parse(read(sessionKey)||'{}')}catch{}session=saved.id&&now-Number(saved.seen||0)<ttl?saved.id:id();write(key,visitor);write(sessionKey,JSON.stringify({id:session,seen:now}))};const attribution=()=>{const q=new URLSearchParams(location.search),clickIds={},properties={},map={utm_term:'utm_term',utm_content:'utm_content',utm_id:'campaign_id',campaign_id:'campaign_id',adset_id:'ad_set_id',ad_set_id:'ad_set_id',adgroup_id:'ad_group_id',ad_group_id:'ad_group_id',ad_id:'ad_id',creative_id:'creative_id',placement:'placement'};for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}for(const [from,to] of Object.entries(map)){const v=q.get(from);if(v)properties[to]=v}const current={source:q.get('utm_source')||undefined,medium:q.get('utm_medium')||undefined,campaign:q.get('utm_campaign')||undefined,clickIds,properties};if(current.source||Object.keys(clickIds).length)write(touchKey,JSON.stringify(current));else{try{return JSON.parse(read(touchKey)||'null')||current}catch{}}return current};const send=(name,properties={})=>{if(!consent||navigator.globalPrivacyControl)return;ensure();const a=attribution();const body=JSON.stringify({pixelId:pixel,events:[{eventId:id(),eventName:name,occurredAt:new Date().toISOString(),visitorId:visitor,sessionId:session,path:location.pathname,canonicalUrl:location.origin+location.pathname,title:document.title,referrer:document.referrer||undefined,...a,deviceClass:innerWidth<640?'mobile':innerWidth<1024?'tablet':'desktop',properties:{...a.properties,...properties}}]});if(navigator.sendBeacon&&navigator.sendBeacon(endpoint,new Blob([body],{type:'application/json'})))return;fetch(endpoint,{method:'POST',headers:{'content-type':'application/json'},body,keepalive:true,credentials:'omit'}).catch(()=>{})};const api={page:()=>send('page_view'),track:send,consent:state=>{consent=(state==='granted'||state===true)&&!navigator.globalPrivacyControl;if(consent){ensure();api.page()}},debug:()=>({pixel,endpoint,consent,gpc:Boolean(navigator.globalPrivacyControl),visitorId:visitor,sessionId:session,hostname:location.hostname,path:location.pathname,...attribution()})};window.mcpAnalytics=api;ensure();if(consent)api.page();let current=location.href;const changed=()=>{if(location.href!==current){current=location.href;api.page()}};for(const method of ['pushState','replaceState']){const original=history[method];history[method]=function(...args){const result=original.apply(this,args);queueMicrotask(changed);return result}}addEventListener('popstate',changed)})();`;
34823
+ return `(()=>{const s=document.currentScript;if(!s)return;const pixel=s.dataset.pixel;if(!pixel)return;const endpoint=new URL('/analytics/v1/events',s.src).toString(),ttl=18e5;const key='mcp_analytics_visitor',sessionKey='mcp_analytics_session',touchKey='mcp_analytics_touch';const id=()=>crypto.randomUUID?crypto.randomUUID():'e_'+Date.now().toString(36)+Math.random().toString(36).slice(2);let consent=s.dataset.consent==='granted'&&!navigator.globalPrivacyControl,visitor=null,session=null;const read=k=>{try{return localStorage.getItem(k)}catch{return null}},write=(k,v)=>{try{localStorage.setItem(k,v)}catch{}};const ensure=()=>{if(!consent)return;visitor=read(key)||id();const now=Date.now();let saved={};try{saved=JSON.parse(read(sessionKey)||'{}')}catch{}session=saved.id&&now-Number(saved.seen||0)<ttl?saved.id:id();write(key,visitor);write(sessionKey,JSON.stringify({id:session,seen:now}))};const attribution=()=>{const q=new URLSearchParams(location.search),clickIds={},properties={},map={utm_term:'utm_term',utm_content:'utm_content',utm_id:'campaign_id',campaign_id:'campaign_id',adset_id:'ad_set_id',ad_set_id:'ad_set_id',adgroup_id:'ad_group_id',ad_group_id:'ad_group_id',ad_id:'ad_id',creative_id:'creative_id',placement:'placement'};for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}for(const [from,to] of Object.entries(map)){const v=q.get(from);if(v)properties[to]=v}const current={source:q.get('utm_source')||undefined,medium:q.get('utm_medium')||undefined,campaign:q.get('utm_campaign')||undefined,clickIds,properties};if(current.source||Object.keys(clickIds).length)write(touchKey,JSON.stringify(current));else{try{return JSON.parse(read(touchKey)||'null')||current}catch{}}return current};const send=(name,properties={},over={})=>{if(!consent||navigator.globalPrivacyControl)return;ensure();const a=attribution();const body=JSON.stringify({pixelId:pixel,events:[{eventId:id(),eventName:name,occurredAt:new Date().toISOString(),visitorId:visitor,sessionId:session,path:location.pathname,canonicalUrl:location.origin+location.pathname,title:document.title,referrer:document.referrer||undefined,...a,deviceClass:innerWidth<640?'mobile':innerWidth<1024?'tablet':'desktop',properties:{...a.properties,...properties},...over}]});if(navigator.sendBeacon&&navigator.sendBeacon(endpoint,new Blob([body],{type:'application/json'})))return;fetch(endpoint,{method:'POST',headers:{'content-type':'application/json'},body,keepalive:true,credentials:'omit'}).catch(()=>{})};let visibleSince=document.visibilityState==='visible'?Date.now():0,dwellMs=0,maxScroll=0,dwellPath=location.pathname;const depth=()=>{const d=document.documentElement,h=d.scrollHeight||1;return Math.max(0,Math.min(100,Math.round(((d.scrollTop||document.body.scrollTop||0)+innerHeight)/h*100)))};const accrue=()=>{if(visibleSince){dwellMs+=Date.now()-visibleSince;visibleSince=0}};const resume=()=>{if(!visibleSince)visibleSince=Date.now()};const flush=()=>{accrue();const ms=Math.min(dwellMs,18e5),sd=Math.max(maxScroll,depth());dwellMs=0;maxScroll=0;if(ms<250)return;send('page_engagement',{},{path:dwellPath,canonicalUrl:location.origin+dwellPath,engagedMs:ms,scrollDepth:sd})};addEventListener('scroll',()=>{const d=depth();if(d>maxScroll)maxScroll=d},{passive:true});document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden')flush();else resume()});addEventListener('pagehide',flush);const api={page:()=>send('page_view'),track:send,consent:state=>{consent=(state==='granted'||state===true)&&!navigator.globalPrivacyControl;if(consent){ensure();resume();api.page()}},debug:()=>({pixel,endpoint,consent,gpc:Boolean(navigator.globalPrivacyControl),visitorId:visitor,sessionId:session,hostname:location.hostname,path:location.pathname,engagedMs:dwellMs+(visibleSince?Date.now()-visibleSince:0),scrollDepth:Math.max(maxScroll,depth()),...attribution()})};window.mcpAnalytics=api;ensure();if(consent)api.page();let current=location.href;const changed=()=>{if(location.href!==current){flush();current=location.href;dwellPath=location.pathname;resume();api.page()}};for(const method of ['pushState','replaceState']){const original=history[method];history[method]=function(...args){const result=original.apply(this,args);queueMicrotask(changed);return result}}addEventListener('popstate',changed)})();`;
34824
+ }
34825
+ function pagePathFilter(value) {
34826
+ return value && value.startsWith("/") ? value : void 0;
34499
34827
  }
34500
34828
  function reportFilters(url) {
34501
34829
  const end = url.searchParams.get("end") || (/* @__PURE__ */ new Date()).toISOString();
@@ -34517,7 +34845,7 @@ function reportFilters(url) {
34517
34845
  channelFamily: url.searchParams.get("channelFamily") || void 0,
34518
34846
  platform: url.searchParams.get("platform") || void 0,
34519
34847
  referrer: url.searchParams.get("referrer") || void 0,
34520
- path: url.searchParams.get("path") || void 0,
34848
+ path: pagePathFilter(url.searchParams.get("path")),
34521
34849
  deviceClass: url.searchParams.get("deviceClass") || void 0,
34522
34850
  countryCode: url.searchParams.get("countryCode") || void 0,
34523
34851
  regionCode: url.searchParams.get("regionCode") || void 0,
@@ -35443,14 +35771,13 @@ for (const [path5, load] of [
35443
35771
  ["/sites/:siteId/overview", analyticsOverview],
35444
35772
  ["/sites/:siteId/timeseries", analyticsTimeseries],
35445
35773
  ["/sites/:siteId/acquisition", analyticsAcquisition],
35446
- ["/sites/:siteId/content", analyticsContent],
35447
35774
  ["/sites/:siteId/events", analyticsEventCounts]
35448
35775
  ]) {
35449
35776
  analyticsApp.get(path5, auth3, async (c) => {
35450
35777
  const url = new URL(c.req.url);
35451
35778
  const filters = reportFilters(url);
35452
35779
  validateReportWindow(filters);
35453
- const paginated = path5.endsWith("/acquisition") || path5.endsWith("/content") || path5.endsWith("/events");
35780
+ const paginated = path5.endsWith("/acquisition") || path5.endsWith("/events");
35454
35781
  const data = await load(
35455
35782
  c.req.param("siteId"),
35456
35783
  Number(c.get("user").id),
@@ -35460,6 +35787,24 @@ for (const [path5, load] of [
35460
35787
  return c.json({ ok: true, data, filters });
35461
35788
  });
35462
35789
  }
35790
+ analyticsApp.get("/sites/:siteId/content", auth3, async (c) => {
35791
+ const url = new URL(c.req.url);
35792
+ const filters = reportFilters(url);
35793
+ validateReportWindow(filters);
35794
+ const requestedSort = url.searchParams.get("sort");
35795
+ const data = await analyticsContent(
35796
+ c.req.param("siteId"),
35797
+ Number(c.get("user").id),
35798
+ filters,
35799
+ reportPage(url),
35800
+ {
35801
+ sort: ANALYTICS_CONTENT_SORTS.includes(requestedSort) ? requestedSort : void 0,
35802
+ direction: url.searchParams.get("direction") === "asc" ? "asc" : "desc",
35803
+ search: url.searchParams.get("search") || void 0
35804
+ }
35805
+ );
35806
+ return c.json({ ok: true, data, filters });
35807
+ });
35463
35808
  analyticsApp.get("/sites/:siteId/dashboard", auth3, async (c) => {
35464
35809
  const url = new URL(c.req.url);
35465
35810
  const filters = reportFilters(url);
@@ -35681,9 +36026,9 @@ function scheduledArtifactOwnerIdForApiKey(apiKey) {
35681
36026
 
35682
36027
  // src/api/scheduled-run-view-links.ts
35683
36028
  import { createHash as createHash18, randomBytes as randomBytes4, randomUUID as randomUUID17 } from "crypto";
35684
- var schemaReady3 = false;
36029
+ var schemaReady2 = false;
35685
36030
  async function ensureScheduledRunViewLinksSchema() {
35686
- if (schemaReady3) return;
36031
+ if (schemaReady2) return;
35687
36032
  const db = getDb();
35688
36033
  await db.execute(`
35689
36034
  CREATE TABLE IF NOT EXISTS scheduled_run_view_links (
@@ -35731,7 +36076,7 @@ async function ensureScheduledRunViewLinksSchema() {
35731
36076
  CREATE INDEX IF NOT EXISTS scheduled_run_view_links_expiry
35732
36077
  ON scheduled_run_view_links(expires_at, revoked_at)
35733
36078
  `);
35734
- schemaReady3 = true;
36079
+ schemaReady2 = true;
35735
36080
  }
35736
36081
  function tokenHash2(token4) {
35737
36082
  return createHash18("sha256").update(token4).digest("hex");
@@ -36381,13 +36726,13 @@ import { createHash as createHash22, createHmac as createHmac4, timingSafeEqual
36381
36726
 
36382
36727
  // src/api/service-connections.ts
36383
36728
  import { randomUUID as randomUUID18 } from "crypto";
36384
- var schemaReady4 = null;
36729
+ var schemaReady3 = null;
36385
36730
  var schemaDb3 = null;
36386
36731
  function ensureServiceConnectionsSchema() {
36387
36732
  const currentDb = getDb();
36388
- if (schemaReady4 && schemaDb3 === currentDb) return schemaReady4;
36733
+ if (schemaReady3 && schemaDb3 === currentDb) return schemaReady3;
36389
36734
  schemaDb3 = currentDb;
36390
- schemaReady4 = (async () => {
36735
+ schemaReady3 = (async () => {
36391
36736
  const db = currentDb;
36392
36737
  await db.execute(`
36393
36738
  CREATE TABLE IF NOT EXISTS service_connections (
@@ -36455,11 +36800,11 @@ function ensureServiceConnectionsSchema() {
36455
36800
  } catch {
36456
36801
  }
36457
36802
  })().catch((error) => {
36458
- schemaReady4 = null;
36803
+ schemaReady3 = null;
36459
36804
  schemaDb3 = null;
36460
36805
  throw error;
36461
36806
  });
36462
- return schemaReady4;
36807
+ return schemaReady3;
36463
36808
  }
36464
36809
  function parseTools(value) {
36465
36810
  if (typeof value !== "string") return [];
@@ -43890,35 +44235,73 @@ app.post("/billing/credits", auth4, async (c) => {
43890
44235
  ledger
43891
44236
  });
43892
44237
  });
44238
+ var CRON_TICK_BUDGET_MS = 5e4;
44239
+ var CRON_TICK_DRAIN_BUDGET_MS = 4e4;
44240
+ function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
44241
+ return new Promise((resolve2) => {
44242
+ const timer = setTimeout(() => {
44243
+ unfinished.push(`${label}:deadline`);
44244
+ resolve2(onDeadlineOrError);
44245
+ }, CRON_TICK_BUDGET_MS);
44246
+ work.then(
44247
+ (value) => {
44248
+ clearTimeout(timer);
44249
+ resolve2(value);
44250
+ },
44251
+ (err) => {
44252
+ clearTimeout(timer);
44253
+ unfinished.push(`${label}:error`);
44254
+ console.error(JSON.stringify({
44255
+ event: "cron_tick_job_failed",
44256
+ job: label,
44257
+ message: err instanceof Error ? err.message : String(err)
44258
+ }));
44259
+ resolve2(onDeadlineOrError);
44260
+ }
44261
+ );
44262
+ });
44263
+ }
43893
44264
  app.get("/cron/tick", async (c) => {
43894
44265
  const secret = c.req.header("authorization");
43895
44266
  if (!process.env.CRON_SECRET || secret !== `Bearer ${process.env.CRON_SECRET}`) {
43896
44267
  return c.json({ error: "Unauthorized" }, 401);
43897
44268
  }
43898
- const { drainQueue } = await import("./worker-4MHZS3OB.js");
43899
- const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
43900
- const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
43901
- const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation, localSourcebookRedispatch, analyticsRollup, analyticsFormDelivery] = await Promise.all([
43902
- drainQueue(budget),
43903
- runMonthlyRefreshSweep(),
43904
- reapIdleBrowserSessions(120).catch(() => ({ reaped: 0 })),
43905
- expireOldLots().catch(() => ({ expired_lots: 0, expired_mc: 0 })),
43906
- cleanupExpiredScrapeBlobs().catch(() => ({ deleted: 0, store: "none" })),
43907
- cleanupExpiredConnectedDataArtifacts().catch(() => ({ deleted: 0, store: "none" })),
43908
- reconcileConnectedAccountBillingSweep().catch(() => ({ checked: 0, synced: 0, failed: 0 })),
43909
- dispatchPendingDirectoryWorkflows(25).catch(() => ({ claimed: 0, dispatched: 0, failed: 0 })),
43910
- reconcileDirectoryWorkflowSettlements(25).catch(() => ({ checked: 0, settled: 0, failed: 0 })),
43911
- cleanupExpiredDirectoryArtifacts().catch(() => ({ deleted: 0, store: "none" })),
43912
- cleanupExpiredSiteExtractArtifacts().catch(() => ({ deleted: 0, store: "none" })),
43913
- redispatchFundedSiteExtractJobs(25).catch(() => ({ checked: 0, dispatched: 0, failed: 0 })),
43914
- reconcileSiteExtractSettlements(25).catch(() => ({ checked: 0, settled: 0, failed: 0 })),
43915
- redispatchPendingPaaHarvests(25).catch(() => ({ checked: 0, dispatched: 0, failed: 0 })),
43916
- reconcilePaaHarvestSettlements(25).catch(() => ({ checked: 0, staleFailed: 0, settled: 0, failed: 0 })),
43917
- redispatchQueuedLocalSourcebookAcquisitions(25).catch(() => ({ checked: 0, dispatched: 0, failed: 0 })),
43918
- refreshAnalyticsDailyRollupsIfDue().catch(() => ({ status: "failed", rows: 0 })),
43919
- deliverPendingAnalyticsForms(10).catch(() => ({ claimed: 0, delivered: 0, deferred: 0 }))
44269
+ const startedAt = Date.now();
44270
+ const { drainQueue } = await import("./worker-KQN673JF.js");
44271
+ const budget = { maxJobs: 10, deadlineMs: startedAt + CRON_TICK_DRAIN_BUDGET_MS };
44272
+ const origin = `${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`;
44273
+ const unfinished = [];
44274
+ const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation, localSourcebookRedispatch, analyticsRollup, analyticsFormDelivery, workflowDispatchResult, scheduledRunReconciliation, retentionSweep] = await Promise.all([
44275
+ settleWithinTickBudget("drainQueue", unfinished, drainQueue(budget), []),
44276
+ settleWithinTickBudget("monthlyRefreshSweep", unfinished, runMonthlyRefreshSweep(), { status: "skipped_deadline" }),
44277
+ settleWithinTickBudget("reapBrowserSessions", unfinished, reapIdleBrowserSessions(120), { reaped: 0 }),
44278
+ settleWithinTickBudget("retryOrphanedBrowserSessions", unfinished, retryOrphanedBrowserSessions(50), { recovered: 0, stillOpen: 0 }),
44279
+ settleWithinTickBudget("expireLots", unfinished, expireOldLots(), { expired_lots: 0, expired_mc: 0 }),
44280
+ settleWithinTickBudget("scrapeBlobCleanup", unfinished, cleanupExpiredScrapeBlobs(), { deleted: 0, store: "none" }),
44281
+ settleWithinTickBudget("connectedDataCleanup", unfinished, cleanupExpiredConnectedDataArtifacts(), { deleted: 0, store: "none" }),
44282
+ settleWithinTickBudget("connectedBillingSweep", unfinished, reconcileConnectedAccountBillingSweep(), { checked: 0, synced: 0, failed: 0 }),
44283
+ settleWithinTickBudget("directoryDispatch", unfinished, dispatchPendingDirectoryWorkflows(5), { claimed: 0, dispatched: 0, failed: 0 }),
44284
+ settleWithinTickBudget("directoryReconcile", unfinished, reconcileDirectoryWorkflowSettlements(5), { checked: 0, settled: 0, failed: 0 }),
44285
+ settleWithinTickBudget("directoryArtifactCleanup", unfinished, cleanupExpiredDirectoryArtifacts(), { deleted: 0, store: "none" }),
44286
+ settleWithinTickBudget("siteExtractArtifactCleanup", unfinished, cleanupExpiredSiteExtractArtifacts(), { deleted: 0, store: "none" }),
44287
+ settleWithinTickBudget("siteExtractRedispatch", unfinished, redispatchFundedSiteExtractJobs(10), { checked: 0, dispatched: 0, failed: 0 }),
44288
+ settleWithinTickBudget("siteExtractReconcile", unfinished, reconcileSiteExtractSettlements(10), { checked: 0, settled: 0, failed: 0 }),
44289
+ settleWithinTickBudget("paaRedispatch", unfinished, redispatchPendingPaaHarvests(10), { checked: 0, dispatched: 0, failed: 0 }),
44290
+ settleWithinTickBudget("paaReconcile", unfinished, reconcilePaaHarvestSettlements(10), { checked: 0, staleFailed: 0, settled: 0, failed: 0 }),
44291
+ settleWithinTickBudget("localSourcebookRedispatch", unfinished, redispatchQueuedLocalSourcebookAcquisitions(5), { checked: 0, dispatched: 0, failed: 0 }),
44292
+ settleWithinTickBudget("analyticsRollup", unfinished, refreshAnalyticsDailyRollupsIfDue(), { status: "failed", rows: 0 }),
44293
+ settleWithinTickBudget("analyticsForms", unfinished, deliverPendingAnalyticsForms(10), { claimed: 0, delivered: 0, deferred: 0 }),
44294
+ settleWithinTickBudget("workflowDispatch", unfinished, dispatchDueWorkflowSchedules(origin), { dispatched: 0, status: "skipped_deadline" }),
44295
+ settleWithinTickBudget("scheduledRunReconcile", unfinished, reconcileScheduledRunSettlements(25), { checked: 0, settled: 0, chargedMc: 0, failed: 0 }),
44296
+ settleWithinTickBudget("retentionSweep", unfinished, sweepRetention(), { deleted: 0, perTable: {}, truncated: false })
43920
44297
  ]);
43921
44298
  return c.json({
44299
+ durationMs: Date.now() - startedAt,
44300
+ tickBudgetMs: CRON_TICK_BUDGET_MS,
44301
+ unfinished,
44302
+ complete: unfinished.length === 0,
44303
+ scheduledRunReconciliation,
44304
+ retentionSweep,
43922
44305
  drained: results.length,
43923
44306
  results,
43924
44307
  sweepResult,
@@ -43954,8 +44337,8 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
43954
44337
  finishExtractJob: finishExtractJob2,
43955
44338
  terminalExtractJobStatus: terminalExtractJobStatus2,
43956
44339
  extractJobLimitInfo: extractJobLimitInfo2
43957
- } = await import("./site-extract-repository-STNOI5OH.js");
43958
- const { assembleExtractArtifacts } = await import("./extract-bundle-2HONVC5T.js");
44340
+ } = await import("./site-extract-repository-3VQAALNR.js");
44341
+ const { assembleExtractArtifacts } = await import("./extract-bundle-RCTNANCH.js");
43959
44342
  const existing = await getExtractJob2(jobId2);
43960
44343
  if (!existing) return c.json({ error: "job not found" }, 404);
43961
44344
  if (existing.status !== "failed") {
@@ -43982,7 +44365,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
43982
44365
  if (!updated) return c.json({ error: "job state changed during re-finalization" }, 409);
43983
44366
  let settlement = "already_settled_or_refunded";
43984
44367
  if (job.billedMc == null && job.userId != null) {
43985
- const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-STNOI5OH.js");
44368
+ const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-3VQAALNR.js");
43986
44369
  const heldMc = Number(job.options.heldMc ?? 0);
43987
44370
  const successful = await countSuccessfulPages2(jobId2);
43988
44371
  const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
@@ -44167,4 +44550,4 @@ app.get("/blog/:slug/", (c) => {
44167
44550
  export {
44168
44551
  app
44169
44552
  };
44170
- //# sourceMappingURL=server-JE7SLZLJ.js.map
44553
+ //# sourceMappingURL=server-IGIIEHXY.js.map