mcp-scraper 0.3.40 → 0.3.44

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.
@@ -16,7 +16,7 @@ import {
16
16
  renderLinkReport,
17
17
  sanitizeAttempts,
18
18
  sanitizeHarvestResult
19
- } from "./chunk-QLQYNE7E.js";
19
+ } from "./chunk-NONBSIES.js";
20
20
  import {
21
21
  csvRecords,
22
22
  listWorkflowDefinitions,
@@ -29,7 +29,7 @@ import {
29
29
  workflowStepCount,
30
30
  workflowSupportsSteps
31
31
  } from "./chunk-NNEIXK5L.js";
32
- import "./chunk-I5EFBTYE.js";
32
+ import "./chunk-3Q2NSHSR.js";
33
33
  import {
34
34
  BROWSER_OPEN_MIN_BALANCE_MC,
35
35
  CONCURRENCY_PRICE_ID,
@@ -38,6 +38,8 @@ import {
38
38
  LedgerOperation,
39
39
  MC_COSTS,
40
40
  MC_PER_CREDIT,
41
+ MC_PER_USD,
42
+ MEMORY_AI_MARGIN_MULTIPLE,
41
43
  MEMORY_PLANS,
42
44
  MEMORY_PLAN_QUOTA,
43
45
  SCHEDULING_PLANS,
@@ -50,7 +52,7 @@ import {
50
52
  harvestProblemResponse,
51
53
  insufficientBalanceResponse,
52
54
  serializeHarvestProblem
53
- } from "./chunk-KJWNVQFL.js";
55
+ } from "./chunk-X53SOJSL.js";
54
56
  import {
55
57
  BrowserDriver,
56
58
  MapsSelectors,
@@ -3548,7 +3550,7 @@ async function fetchWithKernel(url) {
3548
3550
  });
3549
3551
  await client2.browsers.deleteByID(kb.session_id).catch(() => {
3550
3552
  });
3551
- void recordKernelSession({ kernelSessionId: kb.session_id, source: "kernel_fetch", stealth: typeof kb.stealth === "boolean" ? kb.stealth : null, headlessSent: useHeadless ? true : null, proxyUsed: typeof kb.proxy_id === "string" ? Boolean(kb.proxy_id) : null, fallback: true, openedAtMs, closedAtMs: Date.now() });
3553
+ void recordKernelSession({ kernelSessionId: kb.session_id, source: "kernel_fetch", stealth: typeof kb.stealth === "boolean" ? kb.stealth : null, headlessSent: useHeadless, proxyUsed: typeof kb.proxy_id === "string" ? Boolean(kb.proxy_id) : null, fallback: true, openedAtMs, closedAtMs: Date.now() });
3552
3554
  }
3553
3555
  }
3554
3556
 
@@ -5257,7 +5259,7 @@ async function extractSite(opts) {
5257
5259
  }
5258
5260
 
5259
5261
  // src/api/server.ts
5260
- import { Hono as Hono18 } from "hono";
5262
+ import { Hono as Hono20 } from "hono";
5261
5263
  import { serve as serveInngest } from "inngest/hono";
5262
5264
 
5263
5265
  // src/inngest/client.ts
@@ -8287,8 +8289,28 @@ siteAuditApp.get("/jobs", async (c) => {
8287
8289
  return c.json(jobs);
8288
8290
  });
8289
8291
 
8290
- // src/api/youtube-routes.ts
8292
+ // src/api/internal-memory-routes.ts
8291
8293
  import { Hono as Hono2 } from "hono";
8294
+ var internalMemoryApp = new Hono2();
8295
+ internalMemoryApp.post("/ai-debit", async (c) => {
8296
+ const secret2 = c.req.header("x-internal-secret");
8297
+ if (!secret2 || secret2 !== process.env.INTERNAL_MEMORY_DEBIT_SECRET) {
8298
+ return c.json({ error: "unauthorized" }, 401);
8299
+ }
8300
+ const body = await c.req.json().catch(() => ({}));
8301
+ if (!body.identity || typeof body.costUsd !== "number" || body.costUsd < 0) {
8302
+ return c.json({ error: "invalid_request" }, 400);
8303
+ }
8304
+ const user = await getUserByEmail(body.identity.trim().toLowerCase());
8305
+ if (!user) return c.json({ ok: false, reason: "no_user" }, 200);
8306
+ const mc = Math.round(body.costUsd * MEMORY_AI_MARGIN_MULTIPLE * MC_PER_USD);
8307
+ if (mc <= 0) return c.json({ ok: true, mc: 0, balance_mc: null }, 200);
8308
+ const { ok, balance_mc } = await debitMc(user.id, mc, LedgerOperation.MEMORY_AI, `${body.source ?? "ai"}:${body.op ?? ""}`);
8309
+ return c.json({ ok, mc, balance_mc, insufficient: !ok }, 200);
8310
+ });
8311
+
8312
+ // src/api/youtube-routes.ts
8313
+ import { Hono as Hono3 } from "hono";
8292
8314
 
8293
8315
  // src/youtube/youtube-harvest.ts
8294
8316
  import { promises as fs } from "fs";
@@ -9280,7 +9302,7 @@ async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECON
9280
9302
  }
9281
9303
 
9282
9304
  // src/api/youtube-routes.ts
9283
- var youtubeApp = new Hono2();
9305
+ var youtubeApp = new Hono3();
9284
9306
  youtubeApp.post("/harvest", createApiKeyAuth(), async (c) => {
9285
9307
  const raw = await c.req.json().catch(() => ({}));
9286
9308
  const parsed = YoutubeHarvestBodySchema.safeParse(raw);
@@ -9439,14 +9461,14 @@ function fmtTs(secs) {
9439
9461
  }
9440
9462
 
9441
9463
  // src/api/screenshot-routes.ts
9442
- import { Hono as Hono3 } from "hono";
9464
+ import { Hono as Hono4 } from "hono";
9443
9465
  import { z as z12 } from "zod";
9444
9466
  var ScreenshotBodySchema = z12.object({
9445
9467
  url: z12.string().trim().min(1, "url is required"),
9446
9468
  device: z12.string().trim().optional(),
9447
9469
  allowLocal: z12.boolean().optional()
9448
9470
  });
9449
- var screenshotApp = new Hono3();
9471
+ var screenshotApp = new Hono4();
9450
9472
  screenshotApp.use("*", createApiKeyAuth());
9451
9473
  screenshotApp.post("/", async (c) => {
9452
9474
  const raw = await c.req.json().catch(() => ({}));
@@ -9503,7 +9525,7 @@ screenshotApp.post("/", async (c) => {
9503
9525
  });
9504
9526
 
9505
9527
  // src/api/facebook-ad-routes.ts
9506
- import { Hono as Hono4 } from "hono";
9528
+ import { Hono as Hono5 } from "hono";
9507
9529
  import { z as z13 } from "zod";
9508
9530
 
9509
9531
  // src/extractor/FacebookAdExtractor.ts
@@ -10502,7 +10524,7 @@ async function kernelLaunchOptsResidential() {
10502
10524
  }
10503
10525
  return { headless: true, kernelApiKey: browserServiceApiKey(), kernelProxyId: proxyId, viewport: { width: 1280, height: 900 }, locale: "en-US" };
10504
10526
  }
10505
- var facebookAdApp = new Hono4();
10527
+ var facebookAdApp = new Hono5();
10506
10528
  facebookAdApp.post("/ad", createApiKeyAuth(), async (c) => {
10507
10529
  const raw = await c.req.json().catch(() => ({}));
10508
10530
  const parsed = FacebookAdBodySchema.safeParse(raw);
@@ -10835,7 +10857,7 @@ facebookAdApp.post("/media", createApiKeyAuth(), async (c) => {
10835
10857
  });
10836
10858
 
10837
10859
  // src/api/google-ads-routes.ts
10838
- import { Hono as Hono5 } from "hono";
10860
+ import { Hono as Hono6 } from "hono";
10839
10861
  import { z as z14 } from "zod";
10840
10862
 
10841
10863
  // src/extractor/GoogleAdsExtractor.ts
@@ -11044,7 +11066,7 @@ async function kernelLaunchOptsResidential2() {
11044
11066
  function isBlockedMessage(msg) {
11045
11067
  return msg.toLowerCase().includes("blocked") || msg.toLowerCase().includes("captcha");
11046
11068
  }
11047
- var googleAdsApp = new Hono5();
11069
+ var googleAdsApp = new Hono6();
11048
11070
  googleAdsApp.post("/search", createApiKeyAuth(), async (c) => {
11049
11071
  const raw = await c.req.json().catch(() => ({}));
11050
11072
  const parsed = GoogleAdsSearchBodySchema.safeParse(raw);
@@ -11156,7 +11178,7 @@ googleAdsApp.post("/transcribe", createApiKeyAuth(), async (c) => {
11156
11178
  });
11157
11179
 
11158
11180
  // src/api/instagram-routes.ts
11159
- import { Hono as Hono6 } from "hono";
11181
+ import { Hono as Hono7 } from "hono";
11160
11182
  import { z as z15 } from "zod";
11161
11183
  import { createWriteStream as createWriteStream2, mkdirSync as mkdirSync3, statSync, writeFileSync as writeFileSync2 } from "fs";
11162
11184
  import { homedir as homedir3 } from "os";
@@ -11546,7 +11568,7 @@ var InstagramMediaDownloadBodySchema = z15.object({
11546
11568
  includeTranscript: z15.boolean().default(false),
11547
11569
  mux: z15.boolean().default(true)
11548
11570
  });
11549
- var instagramApp = new Hono6();
11571
+ var instagramApp = new Hono7();
11550
11572
  function invalidRequest3(message) {
11551
11573
  return { error_code: "invalid_request", message };
11552
11574
  }
@@ -11820,7 +11842,7 @@ instagramApp.post("/media-download", createApiKeyAuth(), async (c) => {
11820
11842
  });
11821
11843
 
11822
11844
  // src/api/reddit-routes.ts
11823
- import { Hono as Hono7 } from "hono";
11845
+ import { Hono as Hono8 } from "hono";
11824
11846
  import { z as z16 } from "zod";
11825
11847
  var RedditThreadBodySchema = z16.object({
11826
11848
  url: z16.string().trim().min(1, "url is required"),
@@ -11880,7 +11902,7 @@ var PARSE_REDDIT = `(() => {
11880
11902
  });
11881
11903
  return { title: title, author: author, score: score, postBody: postBody, blocked: blocked, numComments: comments.length, comments: comments };
11882
11904
  })()`;
11883
- var redditApp = new Hono7();
11905
+ var redditApp = new Hono8();
11884
11906
  redditApp.post("/thread", createApiKeyAuth(), async (c) => {
11885
11907
  const raw = await c.req.json().catch(() => ({}));
11886
11908
  const parsed = RedditThreadBodySchema.safeParse(raw);
@@ -11914,11 +11936,11 @@ redditApp.post("/thread", createApiKeyAuth(), async (c) => {
11914
11936
  locale: "en-US"
11915
11937
  });
11916
11938
  const page = driver.getPage();
11917
- await page.goto(oldUrl, { waitUntil: "domcontentloaded", timeout: 45e3 });
11939
+ await page.goto(oldUrl, { waitUntil: "domcontentloaded", timeout: attempt === 0 ? 3e4 : 45e3 });
11918
11940
  await page.waitForTimeout(2500);
11919
11941
  const data = await page.evaluate(PARSE_REDDIT);
11920
11942
  if (!data.blocked && (data.postBody || data.comments.length > 0)) {
11921
- result = { ...data, sourceUrl: body.url, oldRedditUrl: oldUrl, attempts: attempt + 1 };
11943
+ result = { ...data, sourceUrl: body.url, oldRedditUrl: oldUrl, attempts: attempt + 1, proxied: Boolean(proxyId) };
11922
11944
  }
11923
11945
  } catch {
11924
11946
  } finally {
@@ -11947,8 +11969,231 @@ redditApp.post("/thread", createApiKeyAuth(), async (c) => {
11947
11969
  }
11948
11970
  });
11949
11971
 
11972
+ // src/api/video-routes.ts
11973
+ import { Hono as Hono9 } from "hono";
11974
+ import { z as z17 } from "zod";
11975
+
11976
+ // src/api/memory.ts
11977
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
11978
+
11979
+ // src/api/session.ts
11980
+ import { createHmac, timingSafeEqual } from "crypto";
11981
+ var isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
11982
+ function getSessionSecret() {
11983
+ const configured = process.env.SESSION_SECRET?.trim();
11984
+ if (configured) return configured;
11985
+ if (isProduction()) throw new Error("SESSION_SECRET is not set \u2014 add it to your Vercel env vars (see src/api/env.ts)");
11986
+ return "dev-secret-change-me";
11987
+ }
11988
+ var secret = () => getSessionSecret();
11989
+ function safeEqualHex(a, b) {
11990
+ if (a.length !== b.length) return false;
11991
+ try {
11992
+ return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
11993
+ } catch {
11994
+ return false;
11995
+ }
11996
+ }
11997
+ function signSession(userId) {
11998
+ const payload = String(userId);
11999
+ const sig = createHmac("sha256", secret()).update(payload).digest("hex");
12000
+ return `${payload}.${sig}`;
12001
+ }
12002
+ function verifySession(token) {
12003
+ const dot = token.lastIndexOf(".");
12004
+ if (dot === -1) return null;
12005
+ const payload = token.slice(0, dot);
12006
+ const sig = token.slice(dot + 1);
12007
+ const expected = createHmac("sha256", secret()).update(payload).digest("hex");
12008
+ if (!safeEqualHex(sig, expected)) return null;
12009
+ const id = parseInt(payload);
12010
+ return isNaN(id) ? null : id;
12011
+ }
12012
+
12013
+ // src/api/memory.ts
12014
+ var MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://mcp-memory-omega.vercel.app").replace(/\/$/, "");
12015
+ var ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
12016
+ function isMemoryOperator(email) {
12017
+ if (!email) return false;
12018
+ const ops = (process.env.MEMORY_OPERATOR_IDENTITIES ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
12019
+ return ops.includes(email.trim().toLowerCase());
12020
+ }
12021
+ function encKey() {
12022
+ return scryptSync(getSessionSecret(), "mcp-memory-key-v1", 32);
12023
+ }
12024
+ function encryptMemoryKey(secret2) {
12025
+ const iv = randomBytes(12);
12026
+ const cipher = createCipheriv("aes-256-gcm", encKey(), iv);
12027
+ const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
12028
+ const tag = cipher.getAuthTag();
12029
+ return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
12030
+ }
12031
+ function decryptMemoryKey(stored) {
12032
+ try {
12033
+ const [ivB, tagB, dataB] = stored.split(":");
12034
+ const decipher = createDecipheriv("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
12035
+ decipher.setAuthTag(Buffer.from(tagB, "base64"));
12036
+ return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
12037
+ } catch {
12038
+ return null;
12039
+ }
12040
+ }
12041
+ async function memoryCall(toolName, args, userMemoryKey) {
12042
+ try {
12043
+ const res = await fetch(`${MEMORY_BASE_URL()}/api/mcp/memoryServer/tools/${toolName}/execute`, {
12044
+ method: "POST",
12045
+ headers: { "content-type": "application/json" },
12046
+ body: JSON.stringify({ data: { ...args, apiKey: userMemoryKey } })
12047
+ });
12048
+ const body = await res.json().catch(() => null);
12049
+ if (!res.ok) return { ok: false, error: body?.error ?? `memory ${toolName} failed (${res.status})` };
12050
+ if (body?.result) return body.result;
12051
+ return { ok: false, error: body?.error ?? `memory ${toolName} returned no result` };
12052
+ } catch (err) {
12053
+ return { ok: false, error: err?.message ?? "memory call failed" };
12054
+ }
12055
+ }
12056
+ function personalVault(user) {
12057
+ return `mcp-${user.id}`;
12058
+ }
12059
+ function memoryIdentity(user) {
12060
+ return user.email;
12061
+ }
12062
+ function resolveMemoryIdentity(user) {
12063
+ return memoryIdentity(user);
12064
+ }
12065
+ function memoryPlanForReads(user) {
12066
+ if (isMemoryOperator(user.email)) return "unlimited";
12067
+ if (user.memory_plan === "pro" || user.memory_plan === "team") return user.memory_plan;
12068
+ return "free";
12069
+ }
12070
+ async function provisionMemoryForUser(user) {
12071
+ try {
12072
+ const { key } = await getOrCreateUserMemoryKey(user);
12073
+ if (key) await setMemoryProvisioned(user.id, true);
12074
+ } catch {
12075
+ }
12076
+ }
12077
+ async function getOrCreateUserMemoryKey(user) {
12078
+ const creds = await getUserMemoryCreds(user.id);
12079
+ if (creds.memory_key) {
12080
+ const decrypted = decryptMemoryKey(creds.memory_key);
12081
+ if (decrypted) return { key: decrypted };
12082
+ }
12083
+ const admin = ADMIN_KEY();
12084
+ if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
12085
+ const identity = memoryIdentity(user);
12086
+ const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
12087
+ const provisioned = await memoryCall(
12088
+ "provisionDefaultsTool",
12089
+ { granteeIdentity: identity, issueKey: true, plan },
12090
+ admin
12091
+ );
12092
+ if (!provisioned.ok || !provisioned.secret) {
12093
+ return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
12094
+ }
12095
+ await setUserMemoryCreds(user.id, encryptMemoryKey(provisioned.secret), identity);
12096
+ return { key: provisioned.secret };
12097
+ }
12098
+ async function syncMemoryKeyPlan(user, plan) {
12099
+ const effectivePlan = isMemoryOperator(user.email) ? "unlimited" : plan;
12100
+ const { key, error } = await getOrCreateUserMemoryKey(user);
12101
+ if (!key) return { ok: false, error: error ?? "memory unavailable" };
12102
+ const listed = await memoryCall(
12103
+ "listKeysTool",
12104
+ {},
12105
+ key
12106
+ );
12107
+ if (!listed.ok || !listed.keys?.length) return { ok: false, error: listed.error ?? "no memory keys found" };
12108
+ const target = listed.keys[0];
12109
+ if (target.plan === effectivePlan) return { ok: true };
12110
+ const res = await memoryCall("setScopeTool", { keyId: target.keyId, plan: effectivePlan }, key);
12111
+ return { ok: res.ok, error: res.error };
12112
+ }
12113
+ async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
12114
+ const admin = ADMIN_KEY();
12115
+ if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
12116
+ const identity = resolveMemoryIdentity(user);
12117
+ const res = await memoryCall("setScheduleEntitlementTool", {
12118
+ granteeIdentity: identity,
12119
+ enabled,
12120
+ quotaPerPeriod,
12121
+ mcpScraperApiKey: user.api_key
12122
+ }, admin);
12123
+ return { ok: res.ok, error: res.error };
12124
+ }
12125
+
12126
+ // src/api/video-routes.ts
12127
+ var videoApp = new Hono9();
12128
+ var AnalyzeBodySchema = z17.object({
12129
+ sourceUrl: z17.string().trim().url("sourceUrl must be a direct video file URL"),
12130
+ intervalS: z17.number().min(1).max(30).optional(),
12131
+ maxFrames: z17.number().int().min(1).max(120).optional(),
12132
+ detail: z17.enum(["fast", "standard", "deep"]).optional(),
12133
+ vault: z17.string().trim().min(1).optional()
12134
+ });
12135
+ function invalidRequest5(message) {
12136
+ return { error_code: "invalid_request", message };
12137
+ }
12138
+ videoApp.post("/analyze", createApiKeyAuth(), async (c) => {
12139
+ const parsed = AnalyzeBodySchema.safeParse(await c.req.json().catch(() => ({})));
12140
+ if (!parsed.success) return c.json(invalidRequest5(parsed.error.issues[0]?.message ?? "invalid request"), 400);
12141
+ const body = parsed.data;
12142
+ const user = c.get("user");
12143
+ const { key: memKey, error: memErr } = await getOrCreateUserMemoryKey(user);
12144
+ if (!memKey) return c.json({ error_code: "memory_unavailable", message: memErr ?? "memory unavailable" }, 502);
12145
+ const { ok, balance_mc } = await debitMc(user.id, MC_COSTS.video_analysis, LedgerOperation.VIDEO_ANALYSIS, body.sourceUrl);
12146
+ if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.video_analysis), 402);
12147
+ const started = await memoryCall(
12148
+ "videoAnalyzeStartTool",
12149
+ {
12150
+ sourceUrl: body.sourceUrl,
12151
+ intervalS: body.intervalS ?? 2,
12152
+ maxFrames: body.maxFrames ?? 120,
12153
+ detail: body.detail ?? "standard",
12154
+ vault: body.vault ?? "Library"
12155
+ },
12156
+ memKey
12157
+ );
12158
+ if (!started.ok || !started.runId) {
12159
+ await creditMc(user.id, MC_COSTS.video_analysis, LedgerOperation.VIDEO_ANALYSIS_REFUND, `start-failed:${body.sourceUrl}`);
12160
+ return c.json({ error_code: "video_start_failed", message: started.error ?? "could not start analysis" }, 502);
12161
+ }
12162
+ return c.json({
12163
+ ok: true,
12164
+ runId: started.runId,
12165
+ status: "queued",
12166
+ message: 'Analysis started. Poll GET /video/status?runId=... every few seconds until status is "done".'
12167
+ });
12168
+ });
12169
+ videoApp.post("/status", createApiKeyAuth(), async (c) => {
12170
+ const rawBody = await c.req.json().catch(() => ({}));
12171
+ const runId = rawBody.runId?.trim();
12172
+ if (!runId) return c.json(invalidRequest5("runId is required"), 400);
12173
+ const user = c.get("user");
12174
+ const { key: memKey, error: memErr } = await getOrCreateUserMemoryKey(user);
12175
+ if (!memKey) return c.json({ error_code: "memory_unavailable", message: memErr ?? "memory unavailable" }, 502);
12176
+ const st = await memoryCall("videoAnalyzeStatusTool", { runId }, memKey);
12177
+ if (!st.ok) return c.json({ error_code: "not_found", message: st.error ?? "run not found" }, 404);
12178
+ if (st.status === "failed") {
12179
+ const refundOp = `${LedgerOperation.VIDEO_ANALYSIS_REFUND}:${runId}`;
12180
+ if (!await ledgerExistsForOperation(user.id, refundOp)) {
12181
+ await creditMc(user.id, MC_COSTS.video_analysis, refundOp, `run-failed:${runId}`);
12182
+ }
12183
+ }
12184
+ return c.json({
12185
+ ok: true,
12186
+ runId,
12187
+ status: st.status ?? "unknown",
12188
+ progress: st.progress ?? null,
12189
+ frameCount: st.frameCount ?? null,
12190
+ ...st.status === "done" ? { artifactPath: st.artifactPath ?? null, report: st.report ?? null } : {},
12191
+ ...st.error ? { error: st.error } : {}
12192
+ });
12193
+ });
12194
+
11950
12195
  // src/api/maps-routes.ts
11951
- import { Hono as Hono8 } from "hono";
12196
+ import { Hono as Hono10 } from "hono";
11952
12197
 
11953
12198
  // src/extractor/MapsNavigator.ts
11954
12199
  var MapsNavigator = class {
@@ -12123,7 +12368,7 @@ var MapsReviewCollector = class {
12123
12368
  };
12124
12369
 
12125
12370
  // src/extractor/MapsExtractor.ts
12126
- import { z as z17 } from "zod";
12371
+ import { z as z18 } from "zod";
12127
12372
  var MapsExtractor = class {
12128
12373
  constructor(driver) {
12129
12374
  this.driver = driver;
@@ -12309,7 +12554,7 @@ var MapsExtractor = class {
12309
12554
  });
12310
12555
  return rows;
12311
12556
  }, { hoursTable: MapsSelectors.hoursTable, hoursTableAlt: MapsSelectors.hoursTableAlt });
12312
- const result = z17.array(RawMapsHoursRowSchema).safeParse(raw);
12557
+ const result = z18.array(RawMapsHoursRowSchema).safeParse(raw);
12313
12558
  if (!result.success) {
12314
12559
  console.warn("[MapsExtractor] hours parse failed", result.error.flatten());
12315
12560
  return [];
@@ -12373,7 +12618,7 @@ var MapsExtractor = class {
12373
12618
  });
12374
12619
  return results;
12375
12620
  });
12376
- const result = z17.array(RawMapsAboutAttributeSchema).safeParse(raw);
12621
+ const result = z18.array(RawMapsAboutAttributeSchema).safeParse(raw);
12377
12622
  if (!result.success) {
12378
12623
  console.warn("[MapsExtractor] about parse failed", result.error.flatten());
12379
12624
  return [];
@@ -12749,7 +12994,7 @@ function mapsErrorResponse(c, err, errorCode) {
12749
12994
  attempts: rotationError?.attempts ?? void 0
12750
12995
  }, retryable ? 503 : 500);
12751
12996
  }
12752
- var mapsApp = new Hono8();
12997
+ var mapsApp = new Hono10();
12753
12998
  mapsApp.post("/search", createApiKeyAuth(), async (c) => {
12754
12999
  const user = c.get("user");
12755
13000
  const body = await c.req.json().catch(() => ({}));
@@ -12887,12 +13132,12 @@ mapsApp.post("/place", createApiKeyAuth(), async (c) => {
12887
13132
  });
12888
13133
 
12889
13134
  // src/api/directory-routes.ts
12890
- import { Hono as Hono9 } from "hono";
13135
+ import { Hono as Hono11 } from "hono";
12891
13136
 
12892
13137
  // src/directory/directory-workflow.ts
12893
13138
  import { mkdir as mkdir2, writeFile } from "fs/promises";
12894
13139
  import { join as join6 } from "path";
12895
- import { z as z18 } from "zod";
13140
+ import { z as z19 } from "zod";
12896
13141
 
12897
13142
  // src/directory/location-db.ts
12898
13143
  import { access, readFile } from "fs/promises";
@@ -13053,24 +13298,24 @@ async function resolveDirectoryMarkets(options) {
13053
13298
  }
13054
13299
 
13055
13300
  // src/directory/directory-workflow.ts
13056
- var DirectoryWorkflowOptionsSchema = z18.object({
13057
- query: z18.string().min(1),
13058
- state: z18.string().min(2).default("TN"),
13059
- minPopulation: z18.number().int().min(0).default(1e5),
13060
- populationYear: z18.union(POPULATION_YEARS.map((year) => z18.literal(year))).default(2025),
13061
- maxCities: z18.number().int().min(1).max(100).default(25),
13062
- maxResultsPerCity: z18.number().int().min(1).max(50).default(50),
13063
- concurrency: z18.number().int().min(1).max(5).default(5),
13064
- includeZipGroups: z18.boolean().default(true),
13065
- usZipsCsvPath: z18.string().optional(),
13066
- saveCsv: z18.boolean().default(true),
13067
- gl: z18.string().length(2).default("us"),
13068
- hl: z18.string().length(2).default("en"),
13069
- proxyMode: z18.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE),
13070
- proxyZip: z18.string().regex(/^\d{5}$/).optional(),
13071
- debug: z18.boolean().default(false),
13072
- headless: z18.boolean().default(true),
13073
- kernelApiKey: z18.string().optional()
13301
+ var DirectoryWorkflowOptionsSchema = z19.object({
13302
+ query: z19.string().min(1),
13303
+ state: z19.string().min(2).default("TN"),
13304
+ minPopulation: z19.number().int().min(0).default(1e5),
13305
+ populationYear: z19.union(POPULATION_YEARS.map((year) => z19.literal(year))).default(2025),
13306
+ maxCities: z19.number().int().min(1).max(100).default(25),
13307
+ maxResultsPerCity: z19.number().int().min(1).max(50).default(50),
13308
+ concurrency: z19.number().int().min(1).max(5).default(5),
13309
+ includeZipGroups: z19.boolean().default(true),
13310
+ usZipsCsvPath: z19.string().optional(),
13311
+ saveCsv: z19.boolean().default(true),
13312
+ gl: z19.string().length(2).default("us"),
13313
+ hl: z19.string().length(2).default("en"),
13314
+ proxyMode: z19.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE),
13315
+ proxyZip: z19.string().regex(/^\d{5}$/).optional(),
13316
+ debug: z19.boolean().default(false),
13317
+ headless: z19.boolean().default(true),
13318
+ kernelApiKey: z19.string().optional()
13074
13319
  });
13075
13320
  function errorMessage(err) {
13076
13321
  return err instanceof Error ? err.message : String(err);
@@ -13291,7 +13536,7 @@ async function runDirectoryWorkflowFromPlan(options, plan) {
13291
13536
  }
13292
13537
 
13293
13538
  // src/api/directory-routes.ts
13294
- var directoryApp = new Hono9();
13539
+ var directoryApp = new Hono11();
13295
13540
  directoryApp.post("/run", createApiKeyAuth(), async (c) => {
13296
13541
  const user = c.get("user");
13297
13542
  const body = await c.req.json().catch(() => ({}));
@@ -13362,37 +13607,37 @@ directoryApp.post("/run", createApiKeyAuth(), async (c) => {
13362
13607
  });
13363
13608
 
13364
13609
  // src/api/workflow-routes.ts
13365
- import { createHmac } from "crypto";
13610
+ import { createHmac as createHmac2 } from "crypto";
13366
13611
  import { readFile as readFile2 } from "fs/promises";
13367
- import { Hono as Hono10 } from "hono";
13368
- import { z as z19 } from "zod";
13369
- var workflowApp = new Hono10();
13370
- var WorkflowInputSchema = z19.record(z19.unknown()).default({});
13371
- var WorkflowIdSchema = z19.string().min(1);
13372
- var CadenceSchema = z19.enum(["daily", "weekly", "monthly"]);
13373
- var ScheduleStatusSchema = z19.enum(["active", "paused"]);
13374
- var RunBodySchema = z19.object({
13612
+ import { Hono as Hono12 } from "hono";
13613
+ import { z as z20 } from "zod";
13614
+ var workflowApp = new Hono12();
13615
+ var WorkflowInputSchema = z20.record(z20.unknown()).default({});
13616
+ var WorkflowIdSchema = z20.string().min(1);
13617
+ var CadenceSchema = z20.enum(["daily", "weekly", "monthly"]);
13618
+ var ScheduleStatusSchema = z20.enum(["active", "paused"]);
13619
+ var RunBodySchema = z20.object({
13375
13620
  workflowId: WorkflowIdSchema,
13376
13621
  input: WorkflowInputSchema,
13377
- webhookUrl: z19.string().url().optional()
13622
+ webhookUrl: z20.string().url().optional()
13378
13623
  });
13379
- var ScheduleCreateSchema = z19.object({
13624
+ var ScheduleCreateSchema = z20.object({
13380
13625
  workflowId: WorkflowIdSchema,
13381
- name: z19.string().min(1).max(120).optional(),
13626
+ name: z20.string().min(1).max(120).optional(),
13382
13627
  input: WorkflowInputSchema,
13383
13628
  cadence: CadenceSchema.default("weekly"),
13384
- timezone: z19.string().min(1).max(64).default("UTC"),
13385
- webhookUrl: z19.string().url().optional(),
13386
- nextRunAt: z19.string().datetime().optional()
13629
+ timezone: z20.string().min(1).max(64).default("UTC"),
13630
+ webhookUrl: z20.string().url().optional(),
13631
+ nextRunAt: z20.string().datetime().optional()
13387
13632
  });
13388
- var SchedulePatchSchema = z19.object({
13389
- name: z19.string().min(1).max(120).optional(),
13633
+ var SchedulePatchSchema = z20.object({
13634
+ name: z20.string().min(1).max(120).optional(),
13390
13635
  status: ScheduleStatusSchema.optional(),
13391
13636
  input: WorkflowInputSchema.optional(),
13392
13637
  cadence: CadenceSchema.optional(),
13393
- timezone: z19.string().min(1).max(64).optional(),
13394
- webhookUrl: z19.string().url().nullable().optional(),
13395
- nextRunAt: z19.string().datetime().nullable().optional()
13638
+ timezone: z20.string().min(1).max(64).optional(),
13639
+ webhookUrl: z20.string().url().nullable().optional(),
13640
+ nextRunAt: z20.string().datetime().nullable().optional()
13396
13641
  });
13397
13642
  function hostedWorkflowOutputDir() {
13398
13643
  return workflowOutputBaseDir(process.env.MCP_SCRAPER_WORKFLOW_OUTPUT_DIR?.trim() || "/tmp/mcp-scraper-workflows");
@@ -13450,7 +13695,7 @@ async function readManifestFromSummary(summary) {
13450
13695
  function webhookSignature(body, timestamp) {
13451
13696
  const secret2 = process.env.MCP_SCRAPER_WEBHOOK_SECRET?.trim();
13452
13697
  if (!secret2) return null;
13453
- return createHmac("sha256", secret2).update(`${timestamp}.${body}`).digest("hex");
13698
+ return createHmac2("sha256", secret2).update(`${timestamp}.${body}`).digest("hex");
13454
13699
  }
13455
13700
  async function deliverWorkflowWebhook(input) {
13456
13701
  if (!input.webhookUrl) return;
@@ -13896,7 +14141,7 @@ workflowApp.post("/cron/dispatch", async (c) => {
13896
14141
  });
13897
14142
 
13898
14143
  // src/api/serp-intelligence-routes.ts
13899
- import { Hono as Hono11 } from "hono";
14144
+ import { Hono as Hono13 } from "hono";
13900
14145
 
13901
14146
  // src/serp-intelligence/page-snapshot-extractor.ts
13902
14147
  import { createHash as createHash2 } from "crypto";
@@ -14208,7 +14453,7 @@ async function capturePageSnapshots(targets, options = {}) {
14208
14453
  }
14209
14454
 
14210
14455
  // src/serp-intelligence/schemas.ts
14211
- import { z as z20 } from "zod";
14456
+ import { z as z21 } from "zod";
14212
14457
  var SerpIntelligenceDeviceValues = ["desktop", "mobile"];
14213
14458
  var SerpIntelligenceProxyModeValues = ["location", "configured", "none"];
14214
14459
  var SerpIntelligenceAttemptOutcomeValues = [
@@ -14270,171 +14515,171 @@ function isPublicHttpUrl(value) {
14270
14515
  return false;
14271
14516
  }
14272
14517
  }
14273
- var SerpIntelligencePublicHttpUrlSchema = z20.string().url().refine(isPublicHttpUrl, "url must be a public HTTP or HTTPS URL");
14274
- var SerpIntelligenceCaptureBodySchema = z20.object({
14275
- query: z20.string().trim().min(1, "query is required"),
14276
- location: z20.string().trim().min(1).optional(),
14277
- gl: z20.string().trim().length(2).default("us"),
14278
- hl: z20.string().trim().length(2).default("en"),
14279
- device: z20.enum(SerpIntelligenceDeviceValues).default("desktop"),
14280
- proxyMode: z20.enum(SerpIntelligenceProxyModeValues).default(DEFAULT_PROXY_MODE),
14281
- proxyZip: z20.string().regex(/^\d{5}$/).optional(),
14282
- pages: z20.number().int().min(1).max(2).default(1),
14283
- debug: z20.boolean().default(false),
14284
- includePageSnapshots: z20.boolean().default(false),
14285
- pageSnapshotLimit: z20.number().int().min(0).max(10).default(0)
14518
+ var SerpIntelligencePublicHttpUrlSchema = z21.string().url().refine(isPublicHttpUrl, "url must be a public HTTP or HTTPS URL");
14519
+ var SerpIntelligenceCaptureBodySchema = z21.object({
14520
+ query: z21.string().trim().min(1, "query is required"),
14521
+ location: z21.string().trim().min(1).optional(),
14522
+ gl: z21.string().trim().length(2).default("us"),
14523
+ hl: z21.string().trim().length(2).default("en"),
14524
+ device: z21.enum(SerpIntelligenceDeviceValues).default("desktop"),
14525
+ proxyMode: z21.enum(SerpIntelligenceProxyModeValues).default(DEFAULT_PROXY_MODE),
14526
+ proxyZip: z21.string().regex(/^\d{5}$/).optional(),
14527
+ pages: z21.number().int().min(1).max(2).default(1),
14528
+ debug: z21.boolean().default(false),
14529
+ includePageSnapshots: z21.boolean().default(false),
14530
+ pageSnapshotLimit: z21.number().int().min(0).max(10).default(0)
14286
14531
  }).strict();
14287
- var SerpIntelligencePageSnapshotRequestSchema = z20.object({
14532
+ var SerpIntelligencePageSnapshotRequestSchema = z21.object({
14288
14533
  url: SerpIntelligencePublicHttpUrlSchema,
14289
- sourceKind: z20.enum(SerpPageSnapshotSourceKindValues).default("configured_target"),
14290
- sourcePosition: z20.number().int().min(1).optional()
14534
+ sourceKind: z21.enum(SerpPageSnapshotSourceKindValues).default("configured_target"),
14535
+ sourcePosition: z21.number().int().min(1).optional()
14291
14536
  }).strict();
14292
- var SerpIntelligencePageSnapshotsBodySchema = z20.object({
14293
- urls: z20.array(SerpIntelligencePublicHttpUrlSchema).min(1).max(25),
14294
- targets: z20.array(SerpIntelligencePageSnapshotRequestSchema).min(1).max(25).optional(),
14295
- maxConcurrency: z20.number().int().min(1).max(5).default(2),
14296
- timeoutMs: z20.number().int().min(1e3).max(6e4).default(15e3),
14297
- debug: z20.boolean().default(false)
14537
+ var SerpIntelligencePageSnapshotsBodySchema = z21.object({
14538
+ urls: z21.array(SerpIntelligencePublicHttpUrlSchema).min(1).max(25),
14539
+ targets: z21.array(SerpIntelligencePageSnapshotRequestSchema).min(1).max(25).optional(),
14540
+ maxConcurrency: z21.number().int().min(1).max(5).default(2),
14541
+ timeoutMs: z21.number().int().min(1e3).max(6e4).default(15e3),
14542
+ debug: z21.boolean().default(false)
14298
14543
  }).strict();
14299
- var SerpIntelligenceAICitationSchema = z20.object({
14300
- text: z20.string(),
14301
- href: z20.string()
14544
+ var SerpIntelligenceAICitationSchema = z21.object({
14545
+ text: z21.string(),
14546
+ href: z21.string()
14302
14547
  }).strict();
14303
- var SerpIntelligenceOrganicResultSchema = z20.object({
14304
- position: z20.number().int().min(1),
14305
- title: z20.string(),
14306
- url: z20.string(),
14307
- domain: z20.string(),
14308
- cite: z20.string().nullable(),
14309
- snippet: z20.string().nullable(),
14310
- isRedditStyle: z20.boolean(),
14311
- inlineRating: z20.object({
14312
- value: z20.string(),
14313
- count: z20.string()
14548
+ var SerpIntelligenceOrganicResultSchema = z21.object({
14549
+ position: z21.number().int().min(1),
14550
+ title: z21.string(),
14551
+ url: z21.string(),
14552
+ domain: z21.string(),
14553
+ cite: z21.string().nullable(),
14554
+ snippet: z21.string().nullable(),
14555
+ isRedditStyle: z21.boolean(),
14556
+ inlineRating: z21.object({
14557
+ value: z21.string(),
14558
+ count: z21.string()
14314
14559
  }).strict().nullable()
14315
14560
  }).strict();
14316
- var SerpIntelligenceLocationEvidenceSchema = z20.object({
14317
- status: z20.enum(SerpIntelligenceLocalizationStatusValues),
14318
- expected: z20.object({
14319
- city: z20.string(),
14320
- regionCode: z20.string().nullable(),
14321
- canonicalLocation: z20.string()
14561
+ var SerpIntelligenceLocationEvidenceSchema = z21.object({
14562
+ status: z21.enum(SerpIntelligenceLocalizationStatusValues),
14563
+ expected: z21.object({
14564
+ city: z21.string(),
14565
+ regionCode: z21.string().nullable(),
14566
+ canonicalLocation: z21.string()
14322
14567
  }).strict().nullable(),
14323
- candidates: z20.array(z20.object({
14324
- city: z20.string(),
14325
- regionCode: z20.string(),
14326
- count: z20.number().int().min(0),
14327
- examples: z20.array(z20.string())
14568
+ candidates: z21.array(z21.object({
14569
+ city: z21.string(),
14570
+ regionCode: z21.string(),
14571
+ count: z21.number().int().min(0),
14572
+ examples: z21.array(z21.string())
14328
14573
  }).strict())
14329
14574
  }).strict();
14330
- var SerpIntelligenceHarvestResultSchema = z20.object({
14331
- seed: z20.string(),
14332
- location: z20.string().nullable(),
14333
- extractedAt: z20.string(),
14334
- totalQuestions: z20.number().int().min(0),
14335
- surface: z20.enum(["web", "aim", "unknown"]),
14336
- aiOverview: z20.object({
14337
- detected: z20.boolean(),
14338
- text: z20.string().nullable(),
14339
- citations: z20.array(SerpIntelligenceAICitationSchema),
14340
- expanded: z20.boolean().optional(),
14341
- fullyExpanded: z20.boolean().optional(),
14342
- sections: z20.array(z20.string()).optional()
14575
+ var SerpIntelligenceHarvestResultSchema = z21.object({
14576
+ seed: z21.string(),
14577
+ location: z21.string().nullable(),
14578
+ extractedAt: z21.string(),
14579
+ totalQuestions: z21.number().int().min(0),
14580
+ surface: z21.enum(["web", "aim", "unknown"]),
14581
+ aiOverview: z21.object({
14582
+ detected: z21.boolean(),
14583
+ text: z21.string().nullable(),
14584
+ citations: z21.array(SerpIntelligenceAICitationSchema),
14585
+ expanded: z21.boolean().optional(),
14586
+ fullyExpanded: z21.boolean().optional(),
14587
+ sections: z21.array(z21.string()).optional()
14343
14588
  }).strict(),
14344
- aiMode: z20.object({
14345
- detected: z20.boolean(),
14346
- text: z20.string().nullable(),
14347
- citations: z20.array(SerpIntelligenceAICitationSchema)
14589
+ aiMode: z21.object({
14590
+ detected: z21.boolean(),
14591
+ text: z21.string().nullable(),
14592
+ citations: z21.array(SerpIntelligenceAICitationSchema)
14348
14593
  }).strict(),
14349
- tree: z20.array(z20.unknown()),
14350
- flat: z20.array(z20.unknown()),
14351
- videos: z20.array(z20.unknown()),
14352
- forums: z20.array(z20.unknown()),
14353
- organicResults: z20.array(SerpIntelligenceOrganicResultSchema),
14354
- localPack: z20.array(z20.unknown()),
14355
- entityIds: z20.object({
14356
- entities: z20.array(z20.object({
14357
- name: z20.string(),
14358
- kgId: z20.string().nullable(),
14359
- cid: z20.string().nullable(),
14360
- gcid: z20.string().nullable()
14594
+ tree: z21.array(z21.unknown()),
14595
+ flat: z21.array(z21.unknown()),
14596
+ videos: z21.array(z21.unknown()),
14597
+ forums: z21.array(z21.unknown()),
14598
+ organicResults: z21.array(SerpIntelligenceOrganicResultSchema),
14599
+ localPack: z21.array(z21.unknown()),
14600
+ entityIds: z21.object({
14601
+ entities: z21.array(z21.object({
14602
+ name: z21.string(),
14603
+ kgId: z21.string().nullable(),
14604
+ cid: z21.string().nullable(),
14605
+ gcid: z21.string().nullable()
14361
14606
  }).strict()),
14362
- kgIds: z20.array(z20.string()),
14363
- cids: z20.array(z20.string()),
14364
- gcids: z20.array(z20.string())
14607
+ kgIds: z21.array(z21.string()),
14608
+ cids: z21.array(z21.string()),
14609
+ gcids: z21.array(z21.string())
14365
14610
  }).strict(),
14366
- stats: z20.object({
14367
- seed: z20.string(),
14368
- totalQuestions: z20.number().int().min(0),
14369
- maxDepthReached: z20.number().int().min(0),
14370
- durationMs: z20.number().min(0),
14371
- errorCount: z20.number().int().min(0)
14611
+ stats: z21.object({
14612
+ seed: z21.string(),
14613
+ totalQuestions: z21.number().int().min(0),
14614
+ maxDepthReached: z21.number().int().min(0),
14615
+ durationMs: z21.number().min(0),
14616
+ errorCount: z21.number().int().min(0)
14372
14617
  }).strict(),
14373
- diagnostics: z20.object({
14374
- completionStatus: z20.enum(["paa_found", "no_paa", "serp_only"]),
14375
- problem: z20.null(),
14376
- warnings: z20.array(z20.unknown()).optional(),
14377
- debug: z20.object({
14618
+ diagnostics: z21.object({
14619
+ completionStatus: z21.enum(["paa_found", "no_paa", "serp_only"]),
14620
+ problem: z21.null(),
14621
+ warnings: z21.array(z21.unknown()).optional(),
14622
+ debug: z21.object({
14378
14623
  locationEvidence: SerpIntelligenceLocationEvidenceSchema.optional()
14379
14624
  }).passthrough().optional()
14380
14625
  }).passthrough(),
14381
- whatPeopleSaying: z20.array(z20.unknown())
14626
+ whatPeopleSaying: z21.array(z21.unknown())
14382
14627
  }).strict();
14383
- var SerpIntelligenceCaptureAttemptSchema = z20.object({
14384
- attemptNumber: z20.number().int().min(1),
14385
- outcome: z20.enum(SerpIntelligenceAttemptOutcomeValues),
14386
- startedAt: z20.string().optional(),
14387
- completedAt: z20.string().optional(),
14388
- durationMs: z20.number().min(0).optional(),
14389
- problemCode: z20.string().optional(),
14390
- message: z20.string().optional(),
14391
- kernelSessionId: z20.string().nullable().optional(),
14392
- cleanupSucceeded: z20.boolean().nullable().optional()
14628
+ var SerpIntelligenceCaptureAttemptSchema = z21.object({
14629
+ attemptNumber: z21.number().int().min(1),
14630
+ outcome: z21.enum(SerpIntelligenceAttemptOutcomeValues),
14631
+ startedAt: z21.string().optional(),
14632
+ completedAt: z21.string().optional(),
14633
+ durationMs: z21.number().min(0).optional(),
14634
+ problemCode: z21.string().optional(),
14635
+ message: z21.string().optional(),
14636
+ kernelSessionId: z21.string().nullable().optional(),
14637
+ cleanupSucceeded: z21.boolean().nullable().optional()
14393
14638
  }).strict();
14394
- var SerpPageSnapshotCaptureSchema = z20.object({
14639
+ var SerpPageSnapshotCaptureSchema = z21.object({
14395
14640
  url: SerpIntelligencePublicHttpUrlSchema,
14396
14641
  requestedUrl: SerpIntelligencePublicHttpUrlSchema,
14397
14642
  finalUrl: SerpIntelligencePublicHttpUrlSchema.nullable(),
14398
- sourceKind: z20.enum(SerpPageSnapshotSourceKindValues),
14399
- sourcePosition: z20.number().int().min(1).nullable(),
14400
- status: z20.enum(SerpPageFetchStatusValues),
14401
- fetchedVia: z20.enum(SerpPageFetchedViaValues).nullable(),
14402
- httpStatus: z20.number().int().min(100).max(599).nullable(),
14403
- contentType: z20.string().nullable(),
14404
- title: z20.string().nullable(),
14643
+ sourceKind: z21.enum(SerpPageSnapshotSourceKindValues),
14644
+ sourcePosition: z21.number().int().min(1).nullable(),
14645
+ status: z21.enum(SerpPageFetchStatusValues),
14646
+ fetchedVia: z21.enum(SerpPageFetchedViaValues).nullable(),
14647
+ httpStatus: z21.number().int().min(100).max(599).nullable(),
14648
+ contentType: z21.string().nullable(),
14649
+ title: z21.string().nullable(),
14405
14650
  canonicalUrl: SerpIntelligencePublicHttpUrlSchema.nullable(),
14406
- metaDescription: z20.string().nullable(),
14407
- headings: z20.array(z20.object({
14408
- level: z20.number().int().min(1).max(6),
14409
- text: z20.string()
14651
+ metaDescription: z21.string().nullable(),
14652
+ headings: z21.array(z21.object({
14653
+ level: z21.number().int().min(1).max(6),
14654
+ text: z21.string()
14410
14655
  }).strict()).default([]),
14411
- artifact: z20.object({
14412
- htmlBlobUrl: z20.string().url().nullable(),
14413
- textBlobUrl: z20.string().url().nullable(),
14414
- markdownBlobUrl: z20.string().url().nullable(),
14415
- screenshotBlobUrl: z20.string().url().nullable(),
14416
- contentSha256: z20.string().nullable(),
14417
- capturedAt: z20.string().nullable()
14656
+ artifact: z21.object({
14657
+ htmlBlobUrl: z21.string().url().nullable(),
14658
+ textBlobUrl: z21.string().url().nullable(),
14659
+ markdownBlobUrl: z21.string().url().nullable(),
14660
+ screenshotBlobUrl: z21.string().url().nullable(),
14661
+ contentSha256: z21.string().nullable(),
14662
+ capturedAt: z21.string().nullable()
14418
14663
  }).strict(),
14419
- error: z20.object({
14420
- code: z20.string(),
14421
- message: z20.string()
14664
+ error: z21.object({
14665
+ code: z21.string(),
14666
+ message: z21.string()
14422
14667
  }).strict().nullable()
14423
14668
  }).strict();
14424
- var SerpIntelligenceCaptureResponseSchema = z20.object({
14669
+ var SerpIntelligenceCaptureResponseSchema = z21.object({
14425
14670
  harvestResult: SerpIntelligenceHarvestResultSchema,
14426
- attempts: z20.array(SerpIntelligenceCaptureAttemptSchema),
14671
+ attempts: z21.array(SerpIntelligenceCaptureAttemptSchema),
14427
14672
  locationEvidence: SerpIntelligenceLocationEvidenceSchema.nullable(),
14428
- pageSnapshotArtifacts: z20.array(SerpPageSnapshotCaptureSchema),
14429
- billing: z20.object({
14430
- creditsUsed: z20.number().min(0).optional(),
14431
- requestId: z20.string().optional(),
14432
- jobId: z20.string().optional()
14673
+ pageSnapshotArtifacts: z21.array(SerpPageSnapshotCaptureSchema),
14674
+ billing: z21.object({
14675
+ creditsUsed: z21.number().min(0).optional(),
14676
+ requestId: z21.string().optional(),
14677
+ jobId: z21.string().optional()
14433
14678
  }).strict().optional()
14434
14679
  }).strict();
14435
- var SerpIntelligencePageSnapshotsResponseSchema = z20.object({
14436
- pageSnapshotArtifacts: z20.array(SerpPageSnapshotCaptureSchema),
14437
- attempts: z20.array(SerpIntelligenceCaptureAttemptSchema).default([])
14680
+ var SerpIntelligencePageSnapshotsResponseSchema = z21.object({
14681
+ pageSnapshotArtifacts: z21.array(SerpPageSnapshotCaptureSchema),
14682
+ attempts: z21.array(SerpIntelligenceCaptureAttemptSchema).default([])
14438
14683
  }).strict();
14439
14684
 
14440
14685
  // src/serp-intelligence/serp-capture-service.ts
@@ -14606,7 +14851,7 @@ var SERP_INTELLIGENCE_RATE_LIMIT = 60;
14606
14851
  var SERP_INTELLIGENCE_RATE_WINDOW_SECONDS = 60;
14607
14852
  var POST_CAPTURE_ROUTE_LABEL = "POST /capture";
14608
14853
  var POST_PAGE_SNAPSHOTS_ROUTE_LABEL = "POST /page-snapshots";
14609
- var serpIntelligenceApp = new Hono11();
14854
+ var serpIntelligenceApp = new Hono13();
14610
14855
  serpIntelligenceApp.use("*", createApiKeyAuth());
14611
14856
  function structuredError(input) {
14612
14857
  return {
@@ -14775,7 +15020,7 @@ serpIntelligenceApp.post("/page-snapshots", async (c) => {
14775
15020
  });
14776
15021
 
14777
15022
  // src/mcp/mcp-routes.ts
14778
- import { Hono as Hono12 } from "hono";
15023
+ import { Hono as Hono14 } from "hono";
14779
15024
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
14780
15025
 
14781
15026
  // src/mcp/mcp-oauth.ts
@@ -14859,7 +15104,7 @@ async function requireMcpCallerKey(c) {
14859
15104
  if (!user) return mcpAuthError();
14860
15105
  return callerKey;
14861
15106
  }
14862
- var mcpApp = new Hono12();
15107
+ var mcpApp = new Hono14();
14863
15108
  mcpApp.all("/", async (c) => {
14864
15109
  try {
14865
15110
  const keyOrError = await requireMcpCallerKey(c);
@@ -14883,7 +15128,7 @@ mcpApp.all("/", async (c) => {
14883
15128
  });
14884
15129
 
14885
15130
  // src/api/browser-agent-routes.ts
14886
- import { Hono as Hono13 } from "hono";
15131
+ import { Hono as Hono15 } from "hono";
14887
15132
 
14888
15133
  // src/api/browser-agent-db.ts
14889
15134
  import { randomUUID as randomUUID2 } from "crypto";
@@ -16317,7 +16562,7 @@ async function loadOpenSession(id, userId) {
16317
16562
  return row;
16318
16563
  }
16319
16564
  function buildBrowserAgentRoutes() {
16320
- const app2 = new Hono13();
16565
+ const app2 = new Hono15();
16321
16566
  app2.use("*", async (c, next) => {
16322
16567
  await migrateBrowserAgent();
16323
16568
  return next();
@@ -17023,161 +17268,9 @@ if (state.key) { refreshSessions(); if (state.current) selectSession(state.curre
17023
17268
 
17024
17269
  // src/api/stripe-routes.ts
17025
17270
  import Stripe from "stripe";
17026
- import { Hono as Hono14 } from "hono";
17027
-
17028
- // src/api/memory.ts
17029
- import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
17030
-
17031
- // src/api/session.ts
17032
- import { createHmac as createHmac2, timingSafeEqual } from "crypto";
17033
- var isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
17034
- function getSessionSecret() {
17035
- const configured = process.env.SESSION_SECRET?.trim();
17036
- if (configured) return configured;
17037
- if (isProduction()) throw new Error("SESSION_SECRET is not set \u2014 add it to your Vercel env vars (see src/api/env.ts)");
17038
- return "dev-secret-change-me";
17039
- }
17040
- var secret = () => getSessionSecret();
17041
- function safeEqualHex(a, b) {
17042
- if (a.length !== b.length) return false;
17043
- try {
17044
- return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
17045
- } catch {
17046
- return false;
17047
- }
17048
- }
17049
- function signSession(userId) {
17050
- const payload = String(userId);
17051
- const sig = createHmac2("sha256", secret()).update(payload).digest("hex");
17052
- return `${payload}.${sig}`;
17053
- }
17054
- function verifySession(token) {
17055
- const dot = token.lastIndexOf(".");
17056
- if (dot === -1) return null;
17057
- const payload = token.slice(0, dot);
17058
- const sig = token.slice(dot + 1);
17059
- const expected = createHmac2("sha256", secret()).update(payload).digest("hex");
17060
- if (!safeEqualHex(sig, expected)) return null;
17061
- const id = parseInt(payload);
17062
- return isNaN(id) ? null : id;
17063
- }
17064
-
17065
- // src/api/memory.ts
17066
- var MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://mcp-memory-omega.vercel.app").replace(/\/$/, "");
17067
- var ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
17068
- function isMemoryOperator(email) {
17069
- if (!email) return false;
17070
- const ops = (process.env.MEMORY_OPERATOR_IDENTITIES ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
17071
- return ops.includes(email.trim().toLowerCase());
17072
- }
17073
- function encKey() {
17074
- return scryptSync(getSessionSecret(), "mcp-memory-key-v1", 32);
17075
- }
17076
- function encryptMemoryKey(secret2) {
17077
- const iv = randomBytes(12);
17078
- const cipher = createCipheriv("aes-256-gcm", encKey(), iv);
17079
- const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
17080
- const tag = cipher.getAuthTag();
17081
- return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
17082
- }
17083
- function decryptMemoryKey(stored) {
17084
- try {
17085
- const [ivB, tagB, dataB] = stored.split(":");
17086
- const decipher = createDecipheriv("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
17087
- decipher.setAuthTag(Buffer.from(tagB, "base64"));
17088
- return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
17089
- } catch {
17090
- return null;
17091
- }
17092
- }
17093
- async function memoryCall(toolName, args, userMemoryKey) {
17094
- try {
17095
- const res = await fetch(`${MEMORY_BASE_URL()}/api/mcp/memoryServer/tools/${toolName}/execute`, {
17096
- method: "POST",
17097
- headers: { "content-type": "application/json" },
17098
- body: JSON.stringify({ data: { ...args, apiKey: userMemoryKey } })
17099
- });
17100
- const body = await res.json().catch(() => null);
17101
- if (!res.ok) return { ok: false, error: body?.error ?? `memory ${toolName} failed (${res.status})` };
17102
- if (body?.result) return body.result;
17103
- return { ok: false, error: body?.error ?? `memory ${toolName} returned no result` };
17104
- } catch (err) {
17105
- return { ok: false, error: err?.message ?? "memory call failed" };
17106
- }
17107
- }
17108
- function personalVault(user) {
17109
- return `mcp-${user.id}`;
17110
- }
17111
- function memoryIdentity(user) {
17112
- return user.email;
17113
- }
17114
- function resolveMemoryIdentity(user) {
17115
- return memoryIdentity(user);
17116
- }
17117
- function memoryPlanForReads(user) {
17118
- if (isMemoryOperator(user.email)) return "unlimited";
17119
- if (user.memory_plan === "pro" || user.memory_plan === "team") return user.memory_plan;
17120
- return "free";
17121
- }
17122
- async function provisionMemoryForUser(user) {
17123
- try {
17124
- const { key } = await getOrCreateUserMemoryKey(user);
17125
- if (key) await setMemoryProvisioned(user.id, true);
17126
- } catch {
17127
- }
17128
- }
17129
- async function getOrCreateUserMemoryKey(user) {
17130
- const creds = await getUserMemoryCreds(user.id);
17131
- if (creds.memory_key) {
17132
- const decrypted = decryptMemoryKey(creds.memory_key);
17133
- if (decrypted) return { key: decrypted };
17134
- }
17135
- const admin = ADMIN_KEY();
17136
- if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
17137
- const identity = memoryIdentity(user);
17138
- const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
17139
- const provisioned = await memoryCall(
17140
- "provisionDefaultsTool",
17141
- { granteeIdentity: identity, issueKey: true, plan },
17142
- admin
17143
- );
17144
- if (!provisioned.ok || !provisioned.secret) {
17145
- return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
17146
- }
17147
- await setUserMemoryCreds(user.id, encryptMemoryKey(provisioned.secret), identity);
17148
- return { key: provisioned.secret };
17149
- }
17150
- async function syncMemoryKeyPlan(user, plan) {
17151
- const effectivePlan = isMemoryOperator(user.email) ? "unlimited" : plan;
17152
- const { key, error } = await getOrCreateUserMemoryKey(user);
17153
- if (!key) return { ok: false, error: error ?? "memory unavailable" };
17154
- const listed = await memoryCall(
17155
- "listKeysTool",
17156
- {},
17157
- key
17158
- );
17159
- if (!listed.ok || !listed.keys?.length) return { ok: false, error: listed.error ?? "no memory keys found" };
17160
- const target = listed.keys[0];
17161
- if (target.plan === effectivePlan) return { ok: true };
17162
- const res = await memoryCall("setScopeTool", { keyId: target.keyId, plan: effectivePlan }, key);
17163
- return { ok: res.ok, error: res.error };
17164
- }
17165
- async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
17166
- const admin = ADMIN_KEY();
17167
- if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
17168
- const identity = resolveMemoryIdentity(user);
17169
- const res = await memoryCall("setScheduleEntitlementTool", {
17170
- granteeIdentity: identity,
17171
- enabled,
17172
- quotaPerPeriod,
17173
- mcpScraperApiKey: user.api_key
17174
- }, admin);
17175
- return { ok: res.ok, error: res.error };
17176
- }
17177
-
17178
- // src/api/stripe-routes.ts
17271
+ import { Hono as Hono16 } from "hono";
17179
17272
  var stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: "2026-02-25.clover" });
17180
- var stripeApp = new Hono14();
17273
+ var stripeApp = new Hono16();
17181
17274
  stripeApp.post("/webhooks", async (c) => {
17182
17275
  const sig = c.req.header("stripe-signature");
17183
17276
  const body = await c.req.text();
@@ -17277,7 +17370,7 @@ async function resolveUser(customerId, emailFallback) {
17277
17370
  }
17278
17371
 
17279
17372
  // src/api/oauth-routes.ts
17280
- import { Hono as Hono15 } from "hono";
17373
+ import { Hono as Hono17 } from "hono";
17281
17374
  import { getCookie, setCookie } from "hono/cookie";
17282
17375
  import { createHash as createHash3, randomBytes as randomBytes2, randomUUID as randomUUID3 } from "crypto";
17283
17376
  import { importPKCS8, exportJWK, SignJWT } from "jose";
@@ -17325,8 +17418,9 @@ function isAllowedRedirect(uri) {
17325
17418
  } catch {
17326
17419
  return false;
17327
17420
  }
17328
- if (u.protocol !== "https:") return false;
17329
17421
  const h = u.hostname.toLowerCase();
17422
+ if (u.protocol === "http:" && (h === "localhost" || h === "127.0.0.1" || h === "::1")) return true;
17423
+ if (u.protocol !== "https:") return false;
17330
17424
  if (["claude.ai", "www.claude.ai", "claude.com", "www.claude.com"].includes(h)) return true;
17331
17425
  if (h.endsWith(".claude.ai") || h.endsWith(".claude.com")) return true;
17332
17426
  if (["chatgpt.com", "www.chatgpt.com", "chat.openai.com", "www.chat.openai.com"].includes(h)) return true;
@@ -17444,7 +17538,7 @@ function redirectWithError(redirectUri, state, error) {
17444
17538
  if (state) u.searchParams.set("state", state);
17445
17539
  return new Response(null, { status: 302, headers: { Location: u.toString() } });
17446
17540
  }
17447
- var oauthApp = new Hono15();
17541
+ var oauthApp = new Hono17();
17448
17542
  oauthApp.use("*", async (c, next) => {
17449
17543
  c.header("Access-Control-Allow-Origin", "*");
17450
17544
  c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
@@ -17452,51 +17546,42 @@ oauthApp.use("*", async (c, next) => {
17452
17546
  if (c.req.method === "OPTIONS") return c.body(null, 204);
17453
17547
  return next();
17454
17548
  });
17455
- function authServerMetadata(scopes = SUPPORTED_SCOPES) {
17549
+ function authServerMetadata() {
17456
17550
  return {
17457
17551
  issuer: ISSUER,
17458
17552
  authorization_endpoint: `${ISSUER}/oauth/authorize`,
17459
17553
  token_endpoint: `${ISSUER}/oauth/token`,
17460
17554
  registration_endpoint: `${ISSUER}/oauth/register`,
17461
17555
  jwks_uri: `${ISSUER}/.well-known/jwks.json`,
17462
- scopes_supported: [...scopes],
17556
+ scopes_supported: [...SUPPORTED_SCOPES],
17463
17557
  response_types_supported: ["code"],
17464
17558
  grant_types_supported: ["authorization_code", "refresh_token"],
17465
17559
  code_challenge_methods_supported: ["S256"],
17466
17560
  token_endpoint_auth_methods_supported: ["none"]
17467
17561
  };
17468
17562
  }
17469
- function openidConfiguration(scopes = SUPPORTED_SCOPES) {
17563
+ function openidConfiguration() {
17470
17564
  return {
17471
- ...authServerMetadata(scopes),
17565
+ ...authServerMetadata(),
17472
17566
  subject_types_supported: ["public"],
17473
17567
  id_token_signing_alg_values_supported: ["RS256"]
17474
17568
  };
17475
17569
  }
17476
- function scopesForRequest(c) {
17477
- const host = (c.req.header("host") ?? "").toLowerCase();
17478
- if (!host) return SUPPORTED_SCOPES;
17479
- const suffix2 = c.req.path.replace(/^\/\.well-known\/(oauth-authorization-server|openid-configuration)/, "");
17480
- const candidate = `https://${host}${suffix2}`.replace(/\/$/, "");
17481
- if (candidate === SCRAPER_RESOURCE()) return SCRAPER_SCOPES;
17482
- if (candidate === RESOURCE().replace(/\/$/, "")) return MEMORY_SCOPES;
17483
- return SUPPORTED_SCOPES;
17484
- }
17485
17570
  oauthApp.get("/.well-known/oauth-authorization-server", (c) => {
17486
17571
  console.log("[oauth-dcr] GET /.well-known/oauth-authorization-server ua=%s", c.req.header("user-agent") ?? "");
17487
- return c.json(authServerMetadata(scopesForRequest(c)));
17572
+ return c.json(authServerMetadata());
17488
17573
  });
17489
17574
  oauthApp.get("/.well-known/oauth-authorization-server/*", (c) => {
17490
17575
  console.log("[oauth-dcr] GET %s (path-aware AS) ua=%s", c.req.path, c.req.header("user-agent") ?? "");
17491
- return c.json(authServerMetadata(scopesForRequest(c)));
17576
+ return c.json(authServerMetadata());
17492
17577
  });
17493
17578
  oauthApp.get("/.well-known/openid-configuration", (c) => {
17494
17579
  console.log("[oauth-dcr] GET /.well-known/openid-configuration ua=%s", c.req.header("user-agent") ?? "");
17495
- return c.json(openidConfiguration(scopesForRequest(c)));
17580
+ return c.json(openidConfiguration());
17496
17581
  });
17497
17582
  oauthApp.get("/.well-known/openid-configuration/*", (c) => {
17498
17583
  console.log("[oauth-dcr] GET %s (path-aware OIDC) ua=%s", c.req.path, c.req.header("user-agent") ?? "");
17499
- return c.json(openidConfiguration(scopesForRequest(c)));
17584
+ return c.json(openidConfiguration());
17500
17585
  });
17501
17586
  oauthApp.get("/.well-known/jwks.json", async (c) => {
17502
17587
  const { publicJwk } = await getKeys();
@@ -17700,8 +17785,8 @@ oauthApp.post("/oauth/token", async (c) => {
17700
17785
  });
17701
17786
 
17702
17787
  // src/api/chat-routes.ts
17703
- import { Hono as Hono16 } from "hono";
17704
- var chatApp = new Hono16();
17788
+ import { Hono as Hono18 } from "hono";
17789
+ var chatApp = new Hono18();
17705
17790
  function tokenOk(token) {
17706
17791
  return !!token && token.startsWith("mk_") && token.length > 10;
17707
17792
  }
@@ -18371,8 +18456,8 @@ loadChannels();
18371
18456
  }
18372
18457
 
18373
18458
  // src/api/schedule-routes.ts
18374
- import { Hono as Hono17 } from "hono";
18375
- var scheduleApp = new Hono17();
18459
+ import { Hono as Hono19 } from "hono";
18460
+ var scheduleApp = new Hono19();
18376
18461
  function tokenOk2(token) {
18377
18462
  return !!token && token.startsWith("mk_") && token.length > 10;
18378
18463
  }
@@ -18911,6 +18996,21 @@ async function entitledVaultRows(identity) {
18911
18996
  }
18912
18997
  return [...seen.values()];
18913
18998
  }
18999
+ var EMBED_BYTES_PER_CHUNK = Number(process.env.JINA_EMBED_DIM ?? 1024) * 4;
19000
+ async function embedBytesByPhysical(physicals) {
19001
+ const m = /* @__PURE__ */ new Map();
19002
+ try {
19003
+ const rows = await sql().query(
19004
+ `SELECT metadata->>'vault' AS vault, COUNT(*)::bigint AS chunks
19005
+ FROM "memory" WHERE metadata->>'vault' = ANY($1) GROUP BY 1`,
19006
+ [physicals]
19007
+ );
19008
+ for (const r of rows) if (r.vault) m.set(r.vault, Number(r.chunks) * EMBED_BYTES_PER_CHUNK);
19009
+ } catch {
19010
+ return m;
19011
+ }
19012
+ return m;
19013
+ }
18914
19014
  async function storageByPhysical(physicals) {
18915
19015
  if (physicals.length === 0) return /* @__PURE__ */ new Map();
18916
19016
  const rows = await sql().query(
@@ -18918,8 +19018,9 @@ async function storageByPhysical(physicals) {
18918
19018
  FROM mem_notes WHERE vault = ANY($1) GROUP BY vault`,
18919
19019
  [physicals]
18920
19020
  );
19021
+ const embed = await embedBytesByPhysical(physicals);
18921
19022
  const m = /* @__PURE__ */ new Map();
18922
- for (const r of rows) m.set(r.vault, { notes: Number(r.notes), bytes: Number(r.bytes) });
19023
+ for (const r of rows) m.set(r.vault, { notes: Number(r.notes), bytes: Number(r.bytes) + (embed.get(r.vault) ?? 0) });
18923
19024
  return m;
18924
19025
  }
18925
19026
  async function dbListVaults(identity) {
@@ -19111,7 +19212,7 @@ var sessionAuth = createMiddleware3(async (c, next) => {
19111
19212
  c.set("sessionUser", { ...refreshed, balance_mc: balanceMc });
19112
19213
  return next();
19113
19214
  });
19114
- var app = new Hono18();
19215
+ var app = new Hono20();
19115
19216
  var STRIPE_API_VERSION = "2026-02-25.clover";
19116
19217
  function requireStripeSecret() {
19117
19218
  const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
@@ -19156,6 +19257,7 @@ function opForCostPath(p) {
19156
19257
  if (p.startsWith("/instagram/")) return "instagram";
19157
19258
  if (p === "/reddit/thread") return "reddit_thread";
19158
19259
  if (p.startsWith("/api/internal/site-architecture-auditor")) return "audit_site";
19260
+ if (p.startsWith("/api/internal/memory")) return "memory_ai";
19159
19261
  if (p.startsWith("/agent/")) return "browser_session";
19160
19262
  return null;
19161
19263
  }
@@ -19233,6 +19335,27 @@ app.post("/auth/logout", requireAllowedOrigin, (c) => {
19233
19335
  deleteCookie(c, "session", { path: "/", secure: secureCookies2, sameSite: "Strict" });
19234
19336
  return c.json({ ok: true });
19235
19337
  });
19338
+ app.post("/account/delete", requireAllowedOrigin, sessionAuth, async (c) => {
19339
+ const user = c.get("sessionUser");
19340
+ const secret2 = requireStripeSecret();
19341
+ if (secret2 && user.stripe_customer_id) {
19342
+ const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
19343
+ const subs = await stripeClient.subscriptions.list({ customer: user.stripe_customer_id, status: "all", limit: 100 });
19344
+ for (const sub of subs.data) {
19345
+ if (sub.status === "active" || sub.status === "trialing" || sub.status === "past_due") {
19346
+ try {
19347
+ await stripeClient.subscriptions.cancel(sub.id);
19348
+ } catch (err) {
19349
+ console.error("[account/delete] failed to cancel subscription", sub.id, err instanceof Error ? err.message : String(err));
19350
+ return c.json({ error: "Could not cancel an active subscription \u2014 please try again or contact support." }, 500);
19351
+ }
19352
+ }
19353
+ }
19354
+ }
19355
+ await deactivateUser(user.id);
19356
+ deleteCookie(c, "session", { path: "/", secure: secureCookies2, sameSite: "Strict" });
19357
+ return c.json({ ok: true });
19358
+ });
19236
19359
  app.post("/auth/forgot-password", requireAllowedOrigin, async (c) => {
19237
19360
  const { email } = await c.req.json();
19238
19361
  const normalizedEmail = email?.trim().toLowerCase();
@@ -20255,6 +20378,24 @@ app.post("/billing/subscribe", requireAllowedOrigin, sessionAuth, async (c) => {
20255
20378
  return c.json({ error: message }, 500);
20256
20379
  }
20257
20380
  });
20381
+ app.post("/billing/portal", requireAllowedOrigin, sessionAuth, async (c) => {
20382
+ try {
20383
+ const user = c.get("sessionUser");
20384
+ if (!user.stripe_customer_id) return c.json({ error: "No billing account yet \u2014 subscribe first." }, 409);
20385
+ const secret2 = requireStripeSecret();
20386
+ if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
20387
+ const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
20388
+ const session = await stripeClient.billingPortal.sessions.create({
20389
+ customer: user.stripe_customer_id,
20390
+ return_url: `${appOrigin()}/billing`
20391
+ });
20392
+ return c.json({ url: session.url });
20393
+ } catch (err) {
20394
+ const message = err instanceof Error ? err.message : "Unable to open billing portal.";
20395
+ console.error("[billing/portal]", message);
20396
+ return c.json({ error: message }, 500);
20397
+ }
20398
+ });
20258
20399
  app.post("/billing/concurrency/terminal-checkout", auth2, async (c) => {
20259
20400
  try {
20260
20401
  const user = c.get("user");
@@ -20423,7 +20564,7 @@ app.get("/cron/tick", async (c) => {
20423
20564
  if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
20424
20565
  return c.json({ error: "Unauthorized" }, 401);
20425
20566
  }
20426
- const { drainQueue } = await import("./worker-TL4J65DE.js");
20567
+ const { drainQueue } = await import("./worker-H4EUD2Q5.js");
20427
20568
  const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
20428
20569
  const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
20429
20570
  const [results, sweepResult, reapResult, expiredResult, blobCleanup] = await Promise.all([
@@ -20437,12 +20578,14 @@ app.get("/cron/tick", async (c) => {
20437
20578
  });
20438
20579
  app.on(["GET", "POST", "PUT"], "/api/inngest", serveInngest({ client: inngest, functions: [siteAuditFn, siteExtractFn] }));
20439
20580
  app.route("/api/internal/site-architecture-auditor", siteAuditApp);
20581
+ app.route("/api/internal/memory", internalMemoryApp);
20440
20582
  app.route("/youtube", youtubeApp);
20441
20583
  app.route("/screenshot", screenshotApp);
20442
20584
  app.route("/facebook", facebookAdApp);
20443
20585
  app.route("/google-ads", googleAdsApp);
20444
20586
  app.route("/instagram", instagramApp);
20445
20587
  app.route("/reddit", redditApp);
20588
+ app.route("/video", videoApp);
20446
20589
  app.route("/maps", mapsApp);
20447
20590
  app.route("/directory", directoryApp);
20448
20591
  app.route("/workflows", workflowApp);
@@ -20574,4 +20717,4 @@ app.get("/blog/:slug/", (c) => {
20574
20717
  export {
20575
20718
  app
20576
20719
  };
20577
- //# sourceMappingURL=server-HB7WJO3N.js.map
20720
+ //# sourceMappingURL=server-AN6QUH6C.js.map